(function (Handsontable) {
  'use strict';
 
  function HandsontableFormula() {

    var isFormula = function (value) {
      if (value) {
        if (value[0] === '=') {
          return true;
        }
      }

      return false;
    };

    var formulaRenderer = function (instance, TD, row, col, prop, value, cellProperties) {
      if (instance.formulasEnabled && isFormula(value)) {
        // translate coordinates into cellId 
        var cellId = instance.plugin.utils.translateCellCoords({row: row, col: col}),
          prevFormula = null,
          formula = null,
          needUpdate = false,
          error, result;

        if (!cellId) {
          return;
        }

        // get cell data
        var item = instance.plugin.matrix.getItem(cellId);

        if (item) {

          needUpdate = !!item.needUpdate;

          if (item.error) {
            prevFormula = item.formula;
            error = item.error;

            if (needUpdate) {
              error = null;
            }
          }
        }

        // check if typed formula or cell value should be recalculated
        if ((value && value[0] === '=') || needUpdate) { 
         
          formula = value.substr(1).toUpperCase();

          if (!error || formula !== prevFormula) {

            var currentItem = item;

            if (!currentItem) {

              // define item to rulesJS matrix if not exists
              item = {
                id: cellId,
                formula: formula
              }; 
              var deps = instance.plugin.matrix.getDependencies(cellId); 
              // add item to matrix
              currentItem = instance.plugin.matrix.addItem(item);
            }

            // parse formula
            var newValue = instance.plugin.parse(formula, {row: row, col: col, id: cellId});

            // check if update needed
            needUpdate = (newValue.error === '#NEED_UPDATE');

            // update item value and error
            instance.plugin.matrix.updateItem(currentItem, {formula: formula, value: newValue.result, error: newValue.error, needUpdate: needUpdate});

            error = newValue.error;
            result = newValue.result;
            if(result&&result.toFixed){
              result=result.toFixed(2);
            }
            // update cell value in hot
            value = error || result;
          }
        }

        if (error) {
          // clear cell value
          if (!value) {
            // reset error
            error = null;
          } else {
            // show error
            value = error;
          }
        }

        // change background color
        if (instance.plugin.utils.isSet(error)) {
          Handsontable.Dom.addClass(TD, 'formula-error');
        } else if (instance.plugin.utils.isSet(result)) {
          Handsontable.Dom.removeClass(TD, 'formula-error');
          Handsontable.Dom.addClass(TD, 'formula_');
        }
      }

      // apply changes
      if (cellProperties.type === 'numeric') {
        numericCell.renderer.apply(this, [instance, TD, row, col, prop, value, cellProperties]);
      } else {
        textCell.renderer.apply(this, [instance, TD, row, col, prop, value, cellProperties]);
      }
    };

    var afterChange = function (changes, source) {
      var instance = this;

      if (!instance.formulasEnabled) {
        return;
      }
      console.log('handson formula');
      console.log(changes);
      // -----------cyy 修改于2015年5月26号 10:32:00↓--------------
      // 修改拷贝数据后公式不自动计算的bug、修改handsontable撤销操作时的bug
      //if (source === 'edit' || source === 'undo' || source === 'autofill') {
      if (source === 'edit' || source === 'undo' || source === 'autofill' || source === 'paste' 
      || source === 'create_row_formula' || source === 'remove_row_formula') {
      // -----------cyy 修改于2015年5月26号 10:32:00↑--------------

        var rerender = false;

        changes.forEach(function (item) {

          var row = item[0],
            col = item[1],
            prevValue = item[2],
            value = item[3];
      
          // -----------cyy 添加于2015年5月21号 13:52:00↓--------------
          // 修改handsontable使用json数据时,公式无法自动刷新计算的bug
      var columns = instance.getSettings().columns;
      if (columns != null && columns.length > 0 && columns[0].data != null) {
       for (var i=0;i<columns.length;i++) {
        if (col == columns[i].data) {
          col = i;
          break;
        }
       }
      }
      // -----------cyy 添加于2015年5月21号 13:52:00↑--------------
      
          var cellId = instance.plugin.utils.translateCellCoords({row: row, col: col});

          // if changed value, all references cells should be recalculated
          // -----------cyy 修改于2015年7月20号 16:30:00↓--------------
          //if (value[0] !== '=' || prevValue !== value) {
          //修改前台js报错的bug
          if ((value != null && value[0] !== '=') || prevValue !== value) {
          // -----------cyy 修改于2015年7月20号 16:30:00↑--------------
            instance.plugin.matrix.removeItem(cellId);

            // get referenced cells
            var deps = instance.plugin.matrix.getDependencies(cellId);

            // update cells
            deps.forEach(function (itemId) {
              instance.plugin.matrix.updateItem(itemId, {needUpdate: true});
            });

            rerender = true;
          }
        });

        if (rerender) {
          instance.render();
        }
      }
    };

    var beforeAutofillInsidePopulate = function (index, direction, data, deltas, iterators, selected) {
      var instance = this;

      var r = index.row,
        c = index.col,
        value = data[r][c],
        delta = 0,
        rlength = data.length, // rows
        clength = data ? data[0].length : 0; //cols

      if (value[0] === '=') { // formula

        if (['down', 'up'].indexOf(direction) !== -1) {
          delta = rlength * iterators.row;
        } else if (['right', 'left'].indexOf(direction) !== -1) {
          delta = clength * iterators.col;
        }

        return {
          value: instance.plugin.utils.updateFormula(value, direction, delta),
          iterators: iterators
        }

      } else { // other value

        // increment or decrement  values for more than 2 selected cells
        if (rlength >= 2 || clength >= 2) {

          var newValue = instance.plugin.helper.number(value),
            ii,
            start;

          if (instance.plugin.utils.isNumber(newValue)) {

            if (['down', 'up'].indexOf(direction) !== -1) {

              delta = deltas[0][c];

              if (direction === 'down') {
                newValue += (delta * rlength * iterators.row);
              } else {

                ii = (selected.row - r) % rlength;
                start = ii > 0 ? rlength - ii : 0;

                newValue = instance.plugin.helper.number(data[start][c]);

                newValue += (delta * rlength * iterators.row);

                // last element in array -> decrement iterator
                // iterator cannot be less than 1
                if (iterators.row > 1 && (start + 1) === rlength) {
                  iterators.row--;
                }
              }

            } else if (['right', 'left'].indexOf(direction) !== -1) {
              delta = deltas[r][0];

              if (direction === 'right') {
                newValue += (delta * clength * iterators.col);
              } else {

                ii = (selected.col - c) % clength;
                start = ii > 0 ? clength - ii : 0;

                newValue = instance.plugin.helper.number(data[r][start]);

                newValue += (delta * clength * (iterators.col || 1));

                // last element in array -> decrement iterator
                // iterator cannot be less than 1
                if (iterators.col > 1 && (start + 1) === clength) {
                  iterators.col--;
                }
              }
            }

            return {
              value: newValue,
              iterators: iterators
            }
          }
        }

      }

      return {
        value: value,
        iterators: iterators
      };
    };

    var afterCreateRow = function (row, amount, auto) {
      if (auto) {
        return;
      }

      var instance = this;
      // reSetFormulaCells(instance);
      // -----------cyy 修正于2015年7月3号 09:56:00↓--------------
      //var selectedRow = instance.plugin.utils.isArray(instance.getSelected()) ? instance.getSelected()[0] : undefined;

      //if (instance.plugin.utils.isUndefined(selectedRow)) {
      //  return;
      //}
      
      //var direction = (selectedRow >= row) ? 'before' : 'after',
      
      // 修正从下往上选中单元格进行添加行后,公式计算错误的bug
    // 取得添加的开始和结束行号
      var createdRowStart = row;
    var createdRowEnd = row + amount - 1;

      // 修正为根据选中的开始和结束行号共通判断方向
      var direction = (createdRowStart >= row && createdRowEnd >= row) ? 'before' : 'after',
      // -----------cyy 修正于2015年7月3号 09:56:00↑--------------
        items = instance.plugin.matrix.getRefItemsToRow(row),
        // -----------cyy 添加于2015年5月21号 13:52:00↓--------------
        // 修改添加多行时,公式计算错误的bug
        // counter = 1,
        counter = amount,
        // -----------cyy 添加于2015年5月21号 13:52:00↑--------------
        changes = []; 
        
      items.forEach(function (id) {
        var item = instance.plugin.matrix.getItem(id),
          // -----------cyy 添加于2015年5月21号 13:52:00↓--------------
          // 修改添加多行时,公式计算错误的bug
          // formula = instance.plugin.utils.changeFormula(item.formula, 1, {row: row}), // update formula if needed
          formula = instance.plugin.utils.changeFormula(item.formula, counter, {row: row}), // update formula if needed
          // -----------cyy 添加于2015年5月21号 13:52:00↑--------------
          newId = id;

        if (formula !== item.formula) { // formula updated

          // -----------cyy 修正于2015年7月3号 09:56:00↓--------------
          // change row index and get new coordinates
          //if ((direction === 'before' && selectedRow <= item.row) || (direction === 'after' && selectedRow < item.row)) {
          
          //修正为通过选中开始和结束行号共通判断是否要变为新的坐标
          if ((direction === 'before' && (createdRowStart <= item.row || createdRowEnd <= item.row)) || 
              (direction === 'after' && (createdRowStart < item.row || createdRowEnd < item.row ))) {
          // -----------cyy 修正于2015年7月3号 09:56:00↑--------------
            newId = instance.plugin.utils.changeRowIndex(id, counter);
          }

          var cellCoords = instance.plugin.utils.cellCoords(newId);

          if (newId !== id) {
            // remove current item from matrix
            instance.plugin.matrix.removeItem(id);
          }

          // set updated formula in new cell
          changes.push([cellCoords.row, cellCoords.col, '=' + formula]);
        }
      });

      if (items) {
        instance.plugin.matrix.removeItemsBelowRow(row);
      }
      // console.log("fan");
      // console.log(changes);
      if (changes) {
      // -----------cyy 修正于2015年7月20号 16:41:00↓--------------
      // 由于添加行引起的公式变更,使用单独的source
        instance.setDataAtCell(changes, null, null, "create_row_formula");
        // -----------cyy 修正于2015年7月20号 16:41:00↑--------------
      }
    };

  // -----------cyy 添加于2015年5月21号 13:52:00↓--------------
  // 修改删除行时,公式没有自动变化的bug
  var afterRemoveRow = function (row, amount, auto) {
  
   /*console.log(auto);
      if (auto) {
        return;
      }*/

      var instance = this;

    // 删除要删除行里的公式的ITEM
      for(var i = row; i < row + amount; i++) {
        instance.plugin.matrix.removeItemsInRow(i);
      }
    
      // 取得删除的开始和结束行号
      var deletedRowStart = row;
    var deletedRowEnd = row + amount - 1;
      
      // 根据要删除的开始和结束行号共同判断方向
      var direction = (deletedRowStart >= row && deletedRowEnd >= row) ? 'before' : 'after',
        items = instance.plugin.matrix.getRefItemsToRow(row),
        counter = 0-amount,
        changes = [];

    //tanhf
    // console.log(direction);
    // console.log(counter);
    // console.log(items);

    //tanhf
      items.forEach(function (id) {
        var item = instance.plugin.matrix.getItem(id),
          formula = instance.plugin.utils.changeFmu(item.formula, counter, {row: row}), // update formula if needed
          newId = id;

        if (formula !== item.formula) { // formula updated

          // change row index and get new coordinates
          if ((direction === 'before' && (deletedRowStart <= item.row || deletedRowEnd <= item.row)) 
              || (direction === 'after' && (deletedRowStart < item.row || deletedRowEnd < item.row ))) {
            newId = instance.plugin.utils.changeRowIndex(id, counter);
          }

          var cellCoords = instance.plugin.utils.cellCoords(newId);

          if (newId !== id) {
            // remove current item from matrix
            instance.plugin.matrix.removeItem(id);
          }

          // set updated formula in new cell
          changes.push([cellCoords.row, cellCoords.col, '=' + formula]);
        }
      });

      if (items) {
        instance.plugin.matrix.removeItemsBelowRow(row);
      }

      if (changes) {
      // 由于删除行引起的公式变更,使用单独的source
        instance.setDataAtCell(changes, null, null, "remove_row_formula");
      }
    };
  // -----------cyy 添加于2015年5月21号 13:52:00↑-------------

    var afterCreateCol = function (col) {
      var instance = this;

      var selectedCol = instance.plugin.utils.isArray(instance.getSelected()) ? instance.getSelected()[1] : undefined;

      if (instance.plugin.utils.isUndefined(selectedCol)) {
        return;
      }

      var items = instance.plugin.matrix.getRefItemsToColumn(col),
        counter = 1,
        direction = (selectedCol >= col) ? 'before' : 'after',
        changes = [];

      items.forEach(function (id) {

        var item = instance.plugin.matrix.getItem(id),
          formula = instance.plugin.utils.changeFormula(item.formula, 1, {col: col}), // update formula if needed
          newId = id;

        if (formula !== item.formula) { // formula updated

          // change col index and get new coordinates
          if ((direction === 'before' && selectedCol <= item.col) || (direction === 'after' && selectedCol < item.col)) {
            newId = instance.plugin.utils.changeColIndex(id, counter);
          }

          var cellCoords = instance.plugin.utils.cellCoords(newId);

          if (newId !== id) {
            // remove current item from matrix if id changed
            instance.plugin.matrix.removeItem(id);
          }

          // set updated formula in new cell
          changes.push([cellCoords.row, cellCoords.col, '=' + formula]);
        }
      });

      if (items) {
        instance.plugin.matrix.removeItemsBelowCol(col);
      }

      if (changes) {
        instance.setDataAtCell(changes);
      }
    };

  // -----------cyy 添加于2015年5月21号 13:52:00↓--------------
  // 修改删除列时,公式没有自动变化的bug
  var afterRemoveCol = function (col,amount) {
      var instance = this;

      var selectedCol = instance.plugin.utils.isArray(instance.getSelected()) ? instance.getSelected()[1] : undefined;

      if (instance.plugin.utils.isUndefined(selectedCol)) {
        return;
      }

      // 删除要删除列里的公式的ITEM
      for(var i = col; i < col + amount; i++) {
        instance.plugin.matrix.removeItemsInCol(i);
      }
      
      var items = instance.plugin.matrix.getRefItemsToColumn(col),
        counter = 0-amount,
        direction = (selectedCol >= col) ? 'before' : 'after',
        changes = [];

      items.forEach(function (id) {

        var item = instance.plugin.matrix.getItem(id),
          formula = instance.plugin.utils.changeFormula(item.formula, counter, {col: col+1}), // update formula if needed
          newId = id;

        if (formula !== item.formula) { // formula updated

          // change col index and get new coordinates
          if ((direction === 'before' && selectedCol <= item.col) || (direction === 'after' && selectedCol < item.col)) {
            newId = instance.plugin.utils.changeColIndex(id, counter);
          }

          var cellCoords = instance.plugin.utils.cellCoords(newId);

          if (newId !== id) {
            // remove current item from matrix if id changed
            instance.plugin.matrix.removeItem(id);
          }

          // set updated formula in new cell
          changes.push([cellCoords.row, cellCoords.col, '=' + formula]);
        }
      });

      if (items) {
        instance.plugin.matrix.removeItemsBelowCol(col);
      }

      if (changes) {
        instance.setDataAtCell(changes);
      }
    };
  // -----------cyy 添加于2015年5月21号 13:52:00↑-------------

    var formulaCell = {
      renderer: formulaRenderer,
      editor: Handsontable.editors.TextEditor,
      dataType: 'formula'
    };

    var textCell = {
      renderer: Handsontable.renderers.TextRenderer,
      editor: Handsontable.editors.TextEditor
    };

    var numericCell = {
      renderer: Handsontable.renderers.NumericRenderer,
      editor: Handsontable.editors.NumericEditor
    };

    this.init = function () {
      var instance = this;
      instance.formulasEnabled = !!instance.getSettings().formulas;

      if (instance.formulasEnabled) {

        var custom = {
          cellValue: instance.getDataAtCell
        };

        instance.plugin = new ruleJS();
        instance.plugin.init();
        instance.plugin.custom = custom;

        Handsontable.cellTypes['formula'] = formulaCell;

        Handsontable.TextCell.renderer = formulaRenderer;
        Handsontable.NumericCell.renderer = formulaRenderer;

        instance.addHook('afterChange', afterChange);
        instance.addHook('beforeAutofillInsidePopulate', beforeAutofillInsidePopulate);

        instance.addHook('afterCreateRow', afterCreateRow);
        instance.addHook('afterCreateCol', afterCreateCol);
        // -----------cyy 添加于2015年5月21号 13:52:00↓--------------
        // 添加删除行列,监听执行事件
        instance.addHook('afterRemoveRow', afterRemoveRow);
        instance.addHook('afterRemoveCol', afterRemoveCol);
        // -----------cyy 添加于2015年5月21号 13:52:00↑--------------

      } else {
        instance.removeHook('afterChange', afterChange);
        instance.removeHook('beforeAutofillInsidePopulate', beforeAutofillInsidePopulate);

        instance.removeHook('afterCreateRow', afterCreateRow);
        instance.removeHook('afterCreateCol', afterCreateCol);
        // -----------cyy 添加于2015年5月21号 13:52:00↓--------------
        // 添加删除行列,监听执行事件
        instance.addHook('afterRemoveRow', afterRemoveRow);
        instance.addHook('afterRemoveCol', afterRemoveCol);
        // -----------cyy 添加于2015年5月21号 13:52:00↑--------------
      }
    };
  }

  var htFormula = new HandsontableFormula();

  Handsontable.hooks.add('beforeInit', htFormula.init);

  Handsontable.hooks.add('afterUpdateSettings', function () {
    htFormula.init.call(this, 'afterUpdateSettings')
  });

})(Handsontable);