/**
 * Show input title.
 *
 * @param  object obj
 * @access public
 * @return void
 */
function showTitle(obj)
{
  obj.title = obj.value;
}

/**
 * 判断是否为自然数（零和正整数）
 *
 * @param  any  num
 * @return bool
 */
function isNaturalNumber(num)
{
  var ret = num === undefined || num === null || Number.isNaN(num) || !num.length;
  if(ret) return false;

  num = Number(num);

  if(!Number.isFinite(num)) return false;

  if(typeof num === 'number' && Number.isInteger(num) && num>=0) return true;

  return false;
}

/**
 * Set Distribution.
 *
 * @param  string id
 * @param  string str
 * @access public
 * @return void
 */
function setDist(id, str)
{
  $("input[name='" + id + "']").val(str);
  $("input[name='" + id + "']").siblings('.dist-text').find('a').html(getDistLang(str));
}

/**
 * Set Condition formula.
 *
 * @param  string name
 * @param  string str
 * @access public
 * @return void
 */
function setConditionFormula(name, str)
{
  var value = $("textarea[name='formula[" + name + "]']").val();
  $("textarea[name='formula[" + name + "]']").val(value + str);
}

/**
 * Set Condition formula.
 *
 * @param  object obj
 * @access public
 * @return void
 */
function clearFormula(obj)
{
  var name = $(obj).attr('data-id');
  $("input[name='formula[" + name + "]']").val('');
  $("input[name='formula[" + name + "]']").attr('readonly', false);

  $("input[name='isCondition[" + name + "]']").val('false');
  $('#clear' + name).addClass('hidden');
}

function switchDecision(val)
{
  getFormData();
  formData.decisionStus = val;
  if(val == 'true')
  {
    if(!formData.decision)
    {
      formData.decision = [{variable: 'x1', decisionName: '', decisionVar: null}];
    }
    $('#decisionTable').show();
  }
  else
  {
    $('#decisionTable').hide();
    /* Clear decisionData for validateForm check formula. */
    $('#decisionData').empty();
  }

  render();
}

function switchExportY(val)
{
  getFormData();
  formData.forecast = formData.forecast.map(function(value)
  {
    value.exportY = val == value.variable ? 'checked' : '';
    return value;
  });

  formData.target[0].variable = val;
  render();
}

function getFormData()
{
  formData = {};
  formConfig.forEach(function(config)
    {
      if(!config.group)
      {
        var field = config.field;
        var type  = config.type;
        var id    = config.id;

        var formItem = $('td#' + id).find("input[id^='" + id + "']");

        if(type == 'radio')
        {
          formData[field] = formItem.filter(':checked').val();
        }

        if(type == 'input' || type == 'select')
        {
          formData[field] = formItem.val();
        }

        if(type == 'textarea')
        {
          formData[field] = $('td#' + id).find("textarea[id^='" + id + "']");
        }

        return;
      }
      var data    = [];
      var tableId = config.id;
      var form    = config.form;

      if(!$('#' + tableId).is(":visible")) return;

      $('#' + tableId + ' tbody').find('tr').each(function(index, elem)
        {
          var dataItem = {};
          form.forEach(function(item)
            {
              var field = item.field;
              var type  = item.type;

              if(type == 'text')
              {
                dataItem[field] = $(elem).find("span[id^='" + field +"']").text();
              }

              if(type == 'input')
              {
                dataItem[field] = $(elem).find("input[id^='" + field + "']").val();
              }

              if(type == 'radio')
              {
                dataItem[field] = $(elem).find("input[id^='" + field + "']").attr('checked');
              }

              if(type == 'textarea')
              {
                dataItem[field] = $(elem).find("textarea[id^='" + field + "']").val();
              }

              if(type == 'select')
              {
                dataItem[field] = $(elem).find("select[id^='" + field + "']").val();
              }
            });

          data.push(dataItem);
        });

      formData[config.field] = data;
    });
}

/**
 * Add var.
 *
 * @param  object $obj
 * @access public
 * @return void
 */
function addVar()
{
  getFormData();
  var charMap = formData.hypo.map(function(x)
    {
      return x.variable;
    });
  var canAdd   = false;
  var currChar = '';
  for(var i = ASCII_a; i <= ASCII_z; i ++)
  {
    var letter = String.fromCharCode(i);
    if(!charMap.includes(letter))
    {
      currChar = letter;
      canAdd = true;
      break;
    }
  }

  if(!canAdd) return;

  formData.hypo.push({variable: currChar, varName:'', type: 'dist', distribution: ''});
  render();
}

/**
 * Delete var.
 *
 * @param  object $obj
 * @access public
 * @return void
 */
function deleteVar(obj)
{
  if($(obj).hasClass('disable')) return;
  getFormData();
  var letter = $(obj).attr('data-id');
  var index = formData.hypo.findIndex(function(hypo)
    {
      return hypo.variable == letter;
    });
  formData.hypo.splice(index, 1);

  render();
}

/**
 * Add forecast.
 *
 * @access public
 * @return void
 */
function addForecast()
{
  getFormData();
  var canAdd = false;
  var currY  = '';

  var ynameMap = formData.forecast.map(function(x)
    {
      return x.variable;
    });
  for(var i = 1; i <= 9 ; i ++)
  {
    if(!ynameMap.includes('y' + i))
    {
      canAdd = true;
      currY = 'y' + i;
      break;
    }
  }
  if(!canAdd) return;

  formData.forecast.push({export: '', variable: currY, forecastName: '', formula: ''});
  render();
}

/**
 * Delete forecast
 *
 * @access public
 * @return void
 */
function deleteForecast(obj)
{
  if($(obj).hasClass('disable')) return;
  getFormData();
  var id = $(obj).attr('data-id');
  deleteTarget('', id);
  deleteRequest(null, id);

  var index = formData.forecast.findIndex(function(forecast)
    {
      return forecast.variable == id;
    });
  formData.forecast.splice(index, 1);
  render();
}

/**
 * Add target.
 *
 * @access public
 * @return void
 */
function addTarget(obj)
{
  if($(obj).hasClass('disable')) return;
  getFormData();
  var dataId = $(obj).attr('data-id');
  if(!formData.target)
  {
    formData.target = [];
  }
  else
  {
    var exist = formData.target.some(function(x)
      {
        return x.variable == dataId;
      });
    if(exist) return;
  }

  formData.target.push({variable: dataId, target: 'max', statistic: 'mean'});
  render();
}

/**
 * Delete target.
 *
 * @access public
 * @return void
 */
function deleteTarget(obj, id)
{
  if(!$('#targetTable').is(":visible")) return;
  getFormData();
  var dataId = id ? id : $(obj).attr('data-id');

  var index = formData.target.findIndex(function(target)
    {
      return target.variable == dataId;
    });
  if(index != -1) formData.target.splice(index, 1);

  render();
}

/**
 * Add request.
 *
 * @param object obj
 * @param int copy
 * @access public
 * @return void
 */
function addRequest()
{
  getFormData();
  var firstY  = formData.forecast[0].variable;
  formData.request.push({reqVar: firstY, reqStat: 'mean', reqCondition: 'gt', reqValue: ''});
  render();
}

/**
 * Delete request.
 *
 * @param object obj
 * @param string id
 * @access public
 * @return void
 */
function deleteRequest(obj, id)
{
  if(!$('#requestTable').is(":visible")) return;
  getFormData();
  if(obj)
  {
    var index = $(obj).attr('data-id');
    formData.request.splice(index, 1);
    render();
    return;
  }

  var keepList = formData.request.filter(function(request)
  {
    return request.reqVar !== id;
  });
  formData.request = keepList;

  render();
}

/**
 * Add decision.
 *
 * @access public
 * @return void
 */
function addDecision()
{
  getFormData();
  var canAdd = false;
  var currX  = '';
  var xnameMap = formData.decision.map(function(x)
    {
      return x.variable;
    });
  for(var i = 1; i <= 9 ; i ++)
  {
    if(!xnameMap.includes('x' + i))
    {
      canAdd = true;
      currX = 'x' + i
      break;
    }
  }

  if(!canAdd) return;

  formData.decision.push({variable: currX, decisionName:'', decisionVar: null});
  render();
}

/**
 * Delete decision
 *
 * @access public
 * @return void
 */
function deleteDecision(obj)
{
  if($(obj).hasClass('disable')) return;
  getFormData();
  var id = $(obj).attr('data-id');

  var index = formData.decision.findIndex(function(x)
    {
      return x.variable == id;
    });

  if(index != -1) formData.decision.splice(index, 1);
  render();
}

/**
 * Render all.
 *
 * @access public
 * @return void
 */
function render()
{
  renderHypo();
  renderForecast();
  renderDecision();
  renderTarget();
  renderRequest();
}

/**
 * Empty and regenerate vars.
 *
 * @access public
 * @return void
 */
function renderHypo()
{
  $('#hypoTable tbody').empty();
  var tml = $("#rowTpl").html();
  var dataMap = formData.hypo;

  dataMap.forEach(function(value, i)
    {
      var firstAction = (dataMap.length == 1) ?  'disable' : '';
      var data = {};
      data.letter       = value.variable;
      data.varName      = value.varName || '';
      data.type         = value.type || 'dist';
      data.distribution = value.distribution || '';
      data.distInput    = (data.type == 'dist' ? 'hidden' : '');
      data.distText     = (data.type == 'dist' ? '' : 'hidden');
      data.distTip      = data.distribution && data.distribution.length > 0 ? getDistLang(data.distribution) : distTip;
      data.action       = firstAction;

      var html = $($.zui.formatString(tml, data));
      var picker = html.find('.picker');
      picker.picker();
      picker.data('zui.picker').setValue(data.type);
      html.find('.distribution>input').attr('value', data.distribution);;
      $('#hypoTable tbody').append(html);
    });
}

/**
 * Empty and regenerate forecasts.
 *
 * @access public
 * @return void
 */
function renderForecast()
{
  $('#forecastTable tbody').empty();
  var tml = $("#forecastTpl").html();

  var forecastDataMap = formData.forecast;
  var firstAction = forecastDataMap.length == 1 ? 'disable' : '';

  var findExport = forecastDataMap.find(function(value){return value.exportY == 'checked'});

  if(!findExport) forecastDataMap[0].exportY = 'checked';

  forecastDataMap.forEach(function(value, i)
    {
      var data = {};
      data.i = value.variable;
      data.forecastName = value.forecastName || '';
      data.formula      = value.formula || '';
      data.action       = firstAction;
      data.isCondition  = value.isCondition || 'false';
      data.readonly     = value.isCondition == 'true' ? 'readonly' : '';
      data.clearHidden  = value.isCondition == 'true' ? '' : 'hidden';
      data.exportY      = value.exportY;

      var html = $($.zui.formatString(tml, data));
      if(value.exportY == 'checked') html.find("input[name='exportY']").attr('checked', '');
      $('#forecastTable tbody').append(html);
    });
}

/**
 * Empty and regenerate decision.
 *
 * @access public
 * @return void
 */
function renderDecision()
{
  $('#decision' + formData.decisionStus).prop('checked', true);
  if(formData.decisionStus == 'false')
  {
    $('#displayPlans').hide();
    $('#displayLabel').hide();
    return;
  }
  $('#displayPlans').show();
  $('#displayLabel').show();
  $('#decisionTable tbody').empty();
  var decisionDataMap = formData.decision;
  $('#decisionTable').show();

  var tml = $("#decisionTpl").html();
  var firstAction = decisionDataMap.length == 1 ?  'disable' : '';
  var optionList = formData.hypo.map(function(hypo)
  {
    return {text: hypo.variable, value: hypo.variable};
  });

  decisionDataMap.forEach(function(value, i)
  {
    var data = {};
    data.i = value.variable;
    data.decisionName = value.decisionName || '';
    data.decisionVar  = value.decisionVar || '';
    data.action       = firstAction;

    var html = $($.zui.formatString(tml, data));

    $('#decisionTable tbody').append(html);
  });
}

/**
 * Refresh target struct.
 *
 * @access public
 * @return void
 */
function renderTarget()
{
  $('#targetTable tbody').empty();
  var targetMap = formData.target;
  if(formData.decisionStus == 'false')
  {
    $('#targetTable').hide();
    formData.target = [];
    return;
  }

  var exportY = formData.forecast.find(function(value){return value.exportY == 'checked'});
  var firstY  = formData.forecast[0].variable;
  if(!targetMap || targetMap.length == 0) targetMap = [{variable: exportY ? exportY.variable : firstY, target: 'max', statistic: 'mean'}];
  targetMap[0].variable = exportY ? exportY.variable : firstY;
  formData.target = targetMap;

  $('#targetTable').show();

  var tml = $('#targetTpl').html();
  targetMap.forEach(function(value)
    {
      value.i = value.variable;
      var html = $($.zui.formatString(tml, value));
      var picker = html.find('.picker');
      picker.each(function(index, elem)
        {
          $(elem).picker({allowSingleDeselect: false});
          var type = elem.id.slice(0, -2);
          $(elem).data('zui.picker').setValue(value[type]);
        });
      $('#targetTable tbody').append(html);
    });
}

/**
 * Refresh request struct.
 *
 * @access public
 * @return void
 */
function renderRequest()
{
  $('#requestTable tbody').empty();
  var requestMap = formData.request;
  if(formData.decisionStus == 'false')
  {
    $('#requestTable').hide();
    return;
  }

  if(!requestMap || requestMap.length == 0)
  {
    $('#requestTable thead').hide();
  }
  else
  {
    $('#requestTable thead').show();
  }

  $('#requestTable').show();

  var tml = $('#requestTpl').html();
  requestMap.forEach(function(value, index)
    {
      value.i = index;
      value.text = index == 0 ? letLang : andLang;
      var html = $($.zui.formatString(tml, value));
      var picker = html.find('.picker');
      picker.each(function(index, elem)
        {
          $(elem).picker({allowSingleDeselect: false});
          var type = elem.id.slice(0, -1);
          $(elem).data('zui.picker').setValue(value[type]);
        });

      var optionList = formData.forecast.map(function(forecast)
        {
          return {text: forecast.variable, value: forecast.variable};
        });

      var picker = html.find('select[id^=reqVar]').data('zui.picker');
      if(picker) picker.destroy();
      html.find('select[id^=reqVar]').picker(
        {
          list: optionList,
        });
      html.find('select[id^=reqVar]').data('zui.picker').setValue(value.reqVar);

      $('#requestTable tbody').append(html);
    });
}

/**
 * Check value is empty.
 *
 * @param any val
 * @access public
 * @return bool
 */
function isEmptyValue(val)
{
  return val === null || val === undefined || (typeof val === 'string' && !val.trim().length);
}

/**
 * Traverse form with config.
 *
 * @param array datas.
 * @param function callback.
 * @access public
 * @return void
 */
function formTraverse(datas, callback)
{
  var dataObj  = {};
  datas.forEach(function(data){dataObj[data.name] = data.value});

  formConfig.forEach(function(config)
    {
      if(config.conditions)
      {
        for(var condition in config.conditions)
        {
          if(dataObj[condition] != config.conditions[condition]) return;
        }
      }

      if(config.group)
      {
        var items = $('#' + config.id + ' tbody tr');
        $.each(items, function(index, elem)
          {
            var variable = $(elem).attr('id');
            config.form.forEach(function(formItem)
              {
                if(formItem.required)
                {
                  var field = formItem.field + '[' + variable + ']';
                  if(callback) callback(formItem, field);
                }
              });
          });
      }
      else
      {
        var field = config.id;
        if(config.required && callback) callback(config, field);
      }
    });
}

/**
 * Validate form.
 *
 * @param array datas.
 * @param object $form
 * @access public
 * @return bool
 */
function validateForm(datas, $form)
{
  if(datas.find(function(x){return x.name == 'draft'}).value == '1') return true;

  var hasError = false;
  var formContent = $('.table-settings');

  /* check required. */
  formTraverse(datas, function(config, field)
    {
      if(config.multiple) field += '[]';

      var control = $('[name="' + field + '"]');
      var value   = control.val();
      var isEmpty = isEmptyValue(value);
      var name    = varMsg[config.field];

      var labelId = field.replace(/(\[|\])/g, function($0, $1){return {'[':'', ']':''}[$1]}) + 'Label';
      var label   = $('#' + labelId);

      if(label.length) label.remove();

      if(isEmpty)
      {
        hasError = true;
        var errorMsg = validMsg.required.replace('%s', name);
        label = '<div id="' + labelId + '"class="text-danger nowrap help-text">' + errorMsg + '</div>';
        control.closest('td').append(label);
        formContent.scrollTop(control.closest('td').offset().top + formContent.scrollTop() - 50);
      }
    });

  if(hasError) return false;

  /* check rule. */
  formTraverse(datas, function(config, field)
    {
      if(config.multiple) field += '[]';

      var control = $('[name="' + field + '"]');
      var value   = control.val();
      var name    = varMsg[config.field];

      var labelId = field.replace(/(\[|\])/g, function($0, $1){return {'[':'', ']':''}[$1]}) + 'Label';
      var label   = $('#' + labelId);

      if(label.length) label.remove();

      if(config.rule && config.ruleCondition)
      {
        for(var conditionField in config.ruleCondition)
        {
          var controlField   = field.replace(config.field, conditionField);
          var conditionValue = $('[name="' + controlField + '"]').val();

          if(conditionValue != config.ruleCondition[conditionField]) return;
        }
      }

      if(config.rule == ('number') && isNaN(value))
      {
        hasError = true;
        var errorMsg = validMsg.number.replace('%s', name);
        label   = '<div id="' + labelId + '" class="text-danger help-text">' + errorMsg + '</div>';
        control.closest('td').append(label);
        formContent.scrollTop(control.closest('td').offset().top + formContent.scrollTop() - 50);
        return false;
      }

      if(config.rule == ('integer') && Math.floor(value) != value)
      {
        hasError = true;
        var errorMsg = validMsg.integer.replace('%s', name);
        label   = '<div id="' + labelId + '" class="text-danger help-text">' + errorMsg + '</div>';
        control.closest('td').append(label);
        formContent.scrollTop(control.closest('td').offset().top + formContent.scrollTop() - 50);
        return false;
      }

      if((config.max || config.min) && ((config.max && value > config.max) || (config.min && value < config.min)))
      {
        hasError = true;
        config.min = config.min || '-∞';
        config.max = config.max || '+∞';
        var errorMsg = validMsg.limit.replace('%s', config.min).replace('%s', name).replace('%s', config.max);
        label   = '<div id="' + labelId + '" class="text-danger help-text">' + errorMsg + '</div>';
        control.closest('td').append(label);
        formContent.scrollTop(control.closest('td').offset().top + formContent.scrollTop() - 50);
        return false;
      }

      if(config.rule == ('formula'))
      {
        var execStr = '';
        var vars = [];

        var decision = datas.find(function(x)
        {
          return x.name == 'decision';
        });

        var varNames = datas.forEach(function(item)
        {
          if(item.name.indexOf('varName') != -1)
          {
            var varName = item.name.substr(8,1);
            vars.push('var ' + varName + ' = 1;');
          }
          if(decision.value == 'true' && item.name.indexOf('decisionXname') != -1)
          {
            var varName = item.value;
            vars.push('var ' + varName + ' = 1;');
          }
          if(item.name.indexOf('formula') != -1 && item.name != field)
          {
            var varName = item.name.substr(8, 2);
            vars.push('var ' + varName + ' = 1;');
          }
        });

        try
        {
          var execStr = vars.join('') + replaceFunc(value);
          Function(execStr)()
        }
        catch(e)
        {
          hasError = true;
          var errorMsg = validMsg.formula.replace('%s', name);
          label   = '<div id="' + labelId + '" class="text-danger unset-height help-text">' + errorMsg + '</div>';
          control.closest('td').append(label);
          formContent.scrollTop(control.closest('td').offset().top + formContent.scrollTop() - 50);
          return false;
        }
      }

      if(config.rule == ('decision'))
      {
        var decision = datas.find(function(x)
        {
          return x.name == field;
        });

        if(decision)
        {
          var splitValueList = decision.value.split(',');
          var errorList = splitValueList.filter(function(item)
          {
            return !isNaturalNumber(item.trim());
          });

          if(errorList.length != 0)
          {
            hasError = true;
            var errorMsg = validMsg.decision.replace('%s', name);
            label   = '<div id="' + labelId + '" class="text-danger help-text">' + errorMsg + '</div>';
            control.closest('td').append(label);
            formContent.scrollTop(control.closest('td').offset().top + formContent.scrollTop() - 50);
            return false;
          }

        }
      }
    });

  if(hasError) return false;
}

function replaceFunc(formula)
{
  // Replace function
  funcList.forEach(function(func)
  {
    var pattern = new RegExp(func.reg, 'gi');
    formula = formula.replace(pattern, func.name);
    formula = formula.replaceAll(func.name, 'Math.' + func.name);
  });

  // Replace judge.
  customList.forEach(function(func)
  {
    formula = formula.replaceAll(func.name, func.reg);
  });

  // Replace equal.
  var pattern = new RegExp('(?<!(=|<|>))=(?!=)', 'gi');
  formula = formula.replaceAll(pattern, '==');

  return formula;
}

function judge(condition, right, error)
{
  return condition ? right : error;
}

function setReadOnly(obj)
{
  if($(obj).hasClass('no-remove'))
  {
    $(obj).removeClass('no-remove');
    return;
  }
  var input = $(obj).siblings("input");
  var text  = $(obj).siblings('.dist-text');
  input.val('');
  input.attr('readonly', obj.value == 'dist' ? 'readonly' : false);
  if(obj.value == 'dist')
  {
    text.find('a').html(distTip);
    text.removeClass('hidden');
    input.addClass('hidden');
  }
  else
  {
    text.find('a').html('');
    text.addClass('hidden');
    input.removeClass('hidden');
  }
}

function getDistLang(str)
{
  var distKey = ['normal', 'triangular'];
  distKey.forEach(function(key)
    {
      if(str.indexOf(key) != -1)
      {
        str = str.replace(key, dists[key].title);
      }
    });

  return str;
}

function createFromExcel(data)
{
    data = atob(data);
    data = JSON.parse(data);
    formData = {...formData, ...data};
    render();
}

function downloadTemplate()
{
    var url = $.createLink('montecarlo', 'downloadTemplate');
    var form = document.createElement('form');
    form.style.display = 'none';
    form.action = url;
    form.method = 'post';
    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);
}

$(function()
{
    var $form = $('#montecarlo');
    if(window.validMsg && $form.length)
    {
        $form.ajaxForm({beforeSubmit: validateForm});
        $form.on('change', function(event)
        {
            $(event.target).closest('td').find('.help-text').remove();
        });
    }

    $(document).on('click', "#montecarlo .btn-draft", function(event)
    {
        $form.find('input#draft').val('1');
        $form.submit();
    })

    $(document).on('click', '.montecarlo-add-dist', function(event)
    {
        var $a = $(this);
        var width = $a.attr('data-width') || 650;
        var inputId = $a.attr('input-id');
        var inputValue = $('input[name="' + inputId + '"]').val();
        addDistModal = $.zui.modalTrigger;
        addDistModal.show(
        {
            type:  'iframe',
            url:   $a.attr('href').replace('[value]', btoa(inputValue)),
            width: width,
            //scrollInside: true
        });
        event.preventDefault();
        return false;
    });

    $(document).on('click', '.btn-import', function(event)
    {
        var $a = $(this);
        var width = $a.attr('data-width') || 650;
        var modal = $.zui.modalTrigger;
        modal.show(
        {
            type:  'iframe',
            url:   $a.attr('href'),
            width: width,
            //scrollInside: true
        });
        event.preventDefault();
        return false;
    });

    $(document).on('click', '.montecarlo-add-condition', function(event)
    {
        var $a      = $(this);
        var url     = $a.attr('href');
        var name    = $a.attr('data-id');
        var width   = $a.attr('data-width') || 650;

        /* Replace variable. */
        var varList = [];
        formData.hypo.forEach(function(item) { varList.push(item.variable); })
        if(formData.decision) formData.decision.forEach(function(item) { varList.push(item.variable); })
        url = url.replace('[variable]', varList.join());

        addConditionModal = $.zui.modalTrigger;
        addConditionModal.show(
        {
            type:  'iframe',
            url:   url,
            width: width,
            //scrollInside: true
        });
        event.preventDefault();
        return false;
    });


    $('[data-toggle="popover"]').popover();
});
