text: "use strict";
  
  var $ = require("../../core/renderer"),
      extend = require("../../core/utils/extend").extend,
      pivotUtils = require("./ui.pivot_grid.utils"),
      each = require("../../core/utils/iterator").each,
      foreachTree = pivotUtils.foreachTree,
      FORMAT_DICTIONARY = {
          number: "numeric",
          date: "datetime"
      },
      UNBIND_KEY = "dxPivotGridUnbinding";
  
  function getFormattedValue(path, fields) {
      var value = [],
          lastFieldIndex = fields.length - 1;
  
      each(path, function(i, item) {
          value.push(item.text || pivotUtils.formatValue(item.value, fields[lastFieldIndex - i]));
      });
  
      return value.reverse();
  }
  
  function getExpandedLevel(node) {
      var level = 0;
      foreachTree(node, function(members) {
          level = Math.max(level, members.length - 1);
      });
      return level;
  }
  
  function processDataCell(processCellArgs, processCell) {
      var chartDataItem = processCellArgs.chartDataItem,
          processedCell = processCell && processCell(processCellArgs);
  
      if(processedCell) {
          chartDataItem = extend({}, chartDataItem, processedCell.chartDataItem);
          processedCell = extend({}, processCellArgs, processedCell, {
              chartDataItem: chartDataItem
          });
  
          return processedCell;
      }
  
      return processCellArgs;
  }
  
  function createChartDataSource(pivotGridDataSource, mapOptions, axisDictionary) {
      var data = pivotGridDataSource.getData(),
          dataSource = [],
  
          dataFields = pivotGridDataSource.getAreaFields("data"),
          rowFields = pivotGridDataSource.getAreaFields("row"),
          columnFields = pivotGridDataSource.getAreaFields("column"),
  
          columnElements = [{ index: data.grandTotalColumnIndex, children: data.columns }],
          rowElements = [{ index: data.grandTotalRowIndex, children: data.rows }],
  
          rowLevel = getExpandedLevel(rowElements),
          columnLevel = getExpandedLevel(columnElements),
  
          measureIndex,
          dataField,
  
          rowMemberIndex,
          rowVisibility,
          rowPathFormatted,
          rowPath,
  
          columnMemberIndex,
          columnVisibility,
          columnPath,
          columnPathFormatted;
  
      function createDataItem() {
          var dataCell = ((data.values[rowMemberIndex] || [])[columnMemberIndex] || []),
              value = dataCell[measureIndex],
              axis,
              processCellArgs = {
                  rowPath: rowPath,
                  maxRowLevel: rowLevel,
                  rowPathFormatted: rowPathFormatted,
                  rowFields: rowFields,
  
                  columnPathFormatted: columnPathFormatted,
                  maxColumnLevel: columnLevel,
                  columnPath: columnPath,
                  columnFields: columnFields,
  
                  dataFields: dataFields,
                  dataIndex: measureIndex,
                  dataValues: dataCell,
  
                  visible: columnVisibility && rowVisibility
              },
              seriesName = (mapOptions.inverted ? columnPathFormatted : rowPathFormatted).join(" - "),
              argument = (mapOptions.inverted ? rowPathFormatted : columnPathFormatted).join("/");
  
          if(dataFields.length > 1) {
              if(mapOptions.putDataFieldsInto === "args" || mapOptions.putDataFieldsInto === "both") {
                  argument += " | " + dataField.caption;
  
              }
  
              if(mapOptions.putDataFieldsInto !== "args") {
                  seriesName += " | " + dataField.caption;
                  if(mapOptions.dataFieldsDisplayMode !== "singleAxis") {
                      axis = dataField.caption;
                  }
              }
  
          }
  
          processCellArgs.chartDataItem = {
              val: value === undefined ? null : value,
              series: seriesName,
              arg: argument
          };
  
          processCellArgs = processDataCell(processCellArgs, mapOptions.processCell);
  
          if(processCellArgs.visible) {
  
              axisDictionary[processCellArgs.chartDataItem.series] = axisDictionary[processCellArgs.chartDataItem.series] || axis;
              dataSource.push(processCellArgs.chartDataItem);
          }
  
      }
  
      function foreachRowColumn(callBack) {
          foreachTree(rowElements, function(rowMembers) {
              rowMemberIndex = rowMembers[0].index;
  
              rowMembers = rowMembers.slice(0, rowMembers.length - 1);
  
              rowVisibility = rowLevel === rowMembers.length;
  
              rowPath = pivotUtils.createPath(rowMembers);
              rowPathFormatted = getFormattedValue(rowMembers, rowFields);
  
              if(rowPath.length === 0) {
                  rowPathFormatted = [mapOptions.grandTotalText];
              }
  
              foreachTree(columnElements, function(columnMembers) {
                  columnMemberIndex = columnMembers[0].index;
  
                  columnMembers = columnMembers.slice(0, columnMembers.length - 1);
                  columnVisibility = columnLevel === columnMembers.length;
  
                  columnPath = pivotUtils.createPath(columnMembers);
                  columnPathFormatted = getFormattedValue(columnMembers, columnFields);
  
                  if(columnPath.length === 0) {
                      columnPathFormatted = [mapOptions.grandTotalText];
                  }
  
                  callBack();
              });
          });
      }
  
      function foreachDataField(callback) {
          each(dataFields, function(index, field) {
              dataField = field;
              measureIndex = index;
              callback();
          });
      }
  
      if(mapOptions.alternateDataFields === false) {
          foreachDataField(function() {
              foreachRowColumn(createDataItem);
          });
      } else {
          foreachRowColumn(function() {
              foreachDataField(createDataItem);
          });
      }
  
      return dataSource;
  }
  
  function createValueAxisOptions(dataSource, options) {
      var dataFields = dataSource.getAreaFields("data");
      if(options.putDataFieldsInto !== "args" && options.dataFieldsDisplayMode !== "singleAxis" || dataFields.length === 1) {
          var valueAxisSettings = [];
          each(dataFields, function(_, dataField) {
              var valueAxisOptions = {
                  name: dataField.caption,
                  title: dataField.caption,
                  valueType: FORMAT_DICTIONARY[dataField.dataType] || dataField.dataType,
                  label: {
                      format: dataField.format,
                      precision: dataField.precision
                  }
              };
  
              if(dataField.customizeText) {
                  valueAxisOptions.label.customizeText = function(formatObject) {
                      return dataField.customizeText.call(dataField, formatObject);
                  };
              }
  
              if(options.dataFieldsDisplayMode === "splitPanes") {
                  valueAxisOptions.pane = dataField.caption;
              }
  
              valueAxisSettings.push(valueAxisOptions);
          });
  
          return valueAxisSettings;
      }
  
      return [{}];
  }
  
  function createPanesOptions(dataSource, options) {
      var panes = [];
      var dataFields = dataSource.getAreaFields("data");
  
      if(dataFields.length > 1 && options.dataFieldsDisplayMode === "splitPanes" && options.putDataFieldsInto !== "args") {
          each(dataFields, function(_, dataField) {
              panes.push({
                  name: dataField.caption
              });
          });
      }
  
      if(!panes.length) {
          panes.push({});
      }
  
      return panes;
  }
  
  function createChartOptions(dataSource, options) {
      var customizeSeries = options.customizeSeries,
          customizeChart = options.customizeChart,
          chartOptions = {
              valueAxis: createValueAxisOptions(dataSource, options),
              panes: createPanesOptions(dataSource, options)
          },
          axisDictionary = {};
  
      if(customizeChart) {
          chartOptions = extend(true, {}, chartOptions, customizeChart(chartOptions));
      }
  
      chartOptions.dataSource = createChartDataSource(dataSource, options, axisDictionary);
  
      chartOptions.seriesTemplate = {
          nameField: "series",
          customizeSeries: function(seriesName) {
              var seriesOptions = {};
  
              if(options.dataFieldsDisplayMode === "splitPanes") {
                  seriesOptions.pane = axisDictionary[seriesName];
              } else if(options.dataFieldsDisplayMode !== "singleAxis") {
                  seriesOptions.axis = axisDictionary[seriesName];
              }
  
              if(customizeSeries) {
                  seriesOptions = extend(seriesOptions, customizeSeries(seriesName, seriesOptions));
              }
  
              return seriesOptions;
          }
      };
  
      return chartOptions;
  }
  
  function getChartInstance(chartElement) {
      if(!chartElement) {
          return false;
      }
  
      if(chartElement.NAME) {
          return chartElement.NAME === "dxChart" && chartElement;
      }
  
      var element = $(chartElement);
      return element.data("dxChart") && element.dxChart("instance");
  }
  
  function removeBinding(chart) {
      var unbind = chart.$element().data(UNBIND_KEY);
      unbind && unbind();
  }
  
  module.exports = {
      /**
      * @name dxPivotGridMethods.bindChart
      * @publicName bindChart(chart, integrationOptions)
      * @param1 chart:string|jQuery|object
      * @param2 integrationOptions:object
      * @param2_field1 inverted:boolean
      * @param2_field2 dataFieldsDisplayMode:string
      * @param2_field3 putDataFieldsInto:string
      * @param2_field4 alternateDataFields:boolean
      * @param2_field5 processCell:function(cellData)
      * @param2_field6 customizeChart:function(chartOptions)
      * @param2_field7 customizeSeries:function(seriesName, seriesOptions)
      * @return function | null
      */
      bindChart: function(chart, integrationOptions) {
          integrationOptions = extend({}, integrationOptions);
  
          var that = this,
              updateChart = function() {
                  integrationOptions.grandTotalText = that.option("texts.grandTotal");
                  var chartOptions = createChartOptions(that.getDataSource(), integrationOptions);
                  chart.option(chartOptions);
              },
              disposeBinding;
  
          chart = getChartInstance(chart);
  
          if(!chart) {
              return null;
          }
  
          removeBinding(chart);
  
          that.on("changed", updateChart);
          updateChart();
  
          disposeBinding = function() {
              chart.$element().removeData(UNBIND_KEY);
              that.off("changed", updateChart);
          };
  
          chart.on("disposing", disposeBinding);
          this.on("disposing", disposeBinding);
  
          chart.$element().data(UNBIND_KEY, disposeBinding);
  
          return disposeBinding;
      }
  };
  