"use strict";

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var _ = require('../../../../utils/under-dash');

var utils = require('../../../../utils/utils');

var colCache = require('../../../../utils/col-cache');

var BaseXform = require('../../base-xform');

var Range = require('../../../../doc/range'); // formatting rules appear to be directly in <worksheet>
// custom - by formula
//   <conditionalFormatting sqref="A1:E5">
//     <cfRule type="expression" dxfId="0" priority="1">
//       <formula>IF(MOD(ROW()+COLUMN(),2)=0,TRUE,FALSE)</formula>
//     </cfRule>
//   </conditionalFormatting>
//
// greaterThan
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="cellIs" dxfId="6" priority="1" operator="greaterThan">
//       <formula>13</formula>
//     </cfRule>
//   </conditionalFormatting>
// top 10%
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="top10" dxfId="5" priority="1" percent="1" rank="10"/>
//   </conditionalFormatting>
// bottom 10
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="top10" dxfId="4" priority="1" bottom="1" rank="10"/>
//   </conditionalFormatting>
// gradient fill
//   Needs some study
// solid fill
//   like gradient fill
// Green Yellow Red (looks a bit like gradient path)
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="colorScale" priority="1">
//       <colorScale>
//         <cfvo type="min"/>
//         <cfvo type="percentile" val="50"/>
//         <cfvo type="max"/>
//         <color rgb="FFF8696B"/>
//         <color rgb="FFFFEB84"/>
//         <color rgb="FF63BE7B"/>
//       </colorScale>
//     </cfRule>
//   </conditionalFormatting>
// Arrow Icons
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="iconSet" priority="1">
//       <iconSet iconSet="3Arrows">
//         <cfvo type="percent" val="0"/>
//         <cfvo type="percent" val="33"/>
//         <cfvo type="percent" val="67"/>
//       </iconSet>
//     </cfRule>
//   </conditionalFormatting>
// Star Icons - needs more work
// Equal to 13
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="cellIs" dxfId="3" priority="1" operator="equal">
//       <formula>13</formula>
//     </cfRule>
//   </conditionalFormatting>
// Text Contains
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="containsText" dxfId="0" priority="1" operator="containsText" text="sday">
//       <formula>NOT(ISERROR(SEARCH("sday",A1)))</formula>
//     </cfRule>
//   </conditionalFormatting>
// Multiple (fully overlapping)
//   <conditionalFormatting sqref="A1:E6">
//     <cfRule type="top10" dxfId="2" priority="2" percent="1" rank="10"/>
//     <cfRule type="top10" dxfId="1" priority="1" percent="1" bottom="1" rank="10"/>
//   </conditionalFormatting>
// Intersecting
//   <conditionalFormatting sqref="A1:C6">
//     <cfRule type="top10" dxfId="2" priority="2" percent="1" bottom="1" rank="10"/>
//   </conditionalFormatting>
//   <conditionalFormatting sqref="C1:E6">
//     <cfRule type="top10" dxfId="0" priority="1" percent="1" rank="10"/>
//   </conditionalFormatting>
// Infinite range
//   <conditionalFormatting sqref="A3:XFD5">
//     <cfRule type="cellIs" dxfId="29" priority="2" operator="between">
//       <formula>11</formula>
//       <formula>15</formula>
//     </cfRule>
//   </conditionalFormatting>
//   <conditionalFormatting sqref="B1:D1048576">
//     <cfRule type="expression" dxfId="28" priority="1">
//       <formula>IF(MOD(B1,5)=3,TRUE,FALSE)</formula>
//     </cfRule>
//   </conditionalFormatting>
// Special Dates
//   <conditionalFormatting sqref="A1:G6">
//     <cfRule type="timePeriod" dxfId="0" priority="12" timePeriod="thisWeek">
//       <formula>AND(TODAY()-ROUNDDOWN(A1,0)&lt;=WEEKDAY(TODAY())-1,ROUNDDOWN(A1,0)-TODAY()&lt;=7-WEEKDAY(TODAY()))
//       </formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="1" priority="11" timePeriod="lastWeek">
//       <formula>AND(TODAY()-ROUNDDOWN(A1,0)&gt;=(WEEKDAY(TODAY())),TODAY()-ROUNDDOWN(A1,0)&lt;(WEEKDAY(TODAY())+7))
//       </formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="2" priority="10" timePeriod="nextWeek">
//       <formula>AND(ROUNDDOWN(A1,0)-TODAY()&gt;(7-WEEKDAY(TODAY())),ROUNDDOWN(A1,0)-TODAY()&lt;(15-WEEKDAY(TODAY())))
//       </formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="3" priority="9" timePeriod="yesterday">
//       <formula>FLOOR(A1,1)=TODAY()-1</formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="4" priority="8" timePeriod="today">
//       <formula>FLOOR(A1,1)=TODAY()</formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="5" priority="6" timePeriod="tomorrow">
//       <formula>FLOOR(A1,1)=TODAY()+1</formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="6" priority="5" timePeriod="last7Days">
//       <formula>AND(TODAY()-FLOOR(A1,1)&lt;=6,FLOOR(A1,1)&lt;=TODAY())</formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="7" priority="3" timePeriod="lastMonth">
//       <formula>AND(MONTH(A1)=MONTH(EDATE(TODAY(),0-1)),YEAR(A1)=YEAR(EDATE(TODAY(),0-1)))</formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="8" priority="2" timePeriod="thisMonth">
//       <formula>AND(MONTH(A1)=MONTH(TODAY()),YEAR(A1)=YEAR(TODAY()))</formula>
//     </cfRule>
//     <cfRule type="timePeriod" dxfId="9" priority="1" timePeriod="nextMonth">
//       <formula>AND(MONTH(A1)=MONTH(EDATE(TODAY(),0+1)),YEAR(A1)=YEAR(EDATE(TODAY(),0+1)))</formula>
//     </cfRule>
//   </conditionalFormatting>


function assign(definedName, attributes, name, defaultValue) {
  var value = attributes[name];

  if (value !== undefined) {
    definedName[name] = value;
  } else if (defaultValue !== undefined) {
    definedName[name] = defaultValue;
  }
}

function parseBool(value) {
  switch (value) {
    case '1':
    case 'true':
      return true;

    default:
      return false;
  }
}

function assignBool(definedName, attributes, name, defaultValue) {
  var value = attributes[name];

  if (value !== undefined) {
    definedName[name] = parseBool(value);
  } else if (defaultValue !== undefined) {
    definedName[name] = defaultValue;
  }
}

function optimiseDataValidations(model) {
  // Squeeze alike data validations together into rectangular ranges
  // to reduce file size and speed up Excel load time
  var dvList = _.map(model, function (dataValidation, address) {
    return {
      address: address,
      dataValidation: dataValidation,
      marked: false
    };
  }).sort(function (a, b) {
    return _.strcmp(a.address, b.address);
  });

  var dvMap = _.keyBy(dvList, 'address');

  var matchCol = function matchCol(addr, height, col) {
    for (var i = 0; i < height; i++) {
      var otherAddress = colCache.encodeAddress(addr.row + i, col);

      if (!model[otherAddress] || !_.isEqual(model[addr.address], model[otherAddress])) {
        return false;
      }
    }

    return true;
  };

  return dvList.map(function (dv) {
    if (!dv.marked) {
      var addr = colCache.decodeAddress(dv.address); // iterate downwards - finding matching cells

      var height = 1;
      var otherAddress = colCache.encodeAddress(addr.row + height, addr.col);

      while (model[otherAddress] && _.isEqual(dv.dataValidation, model[otherAddress])) {
        height++;
        otherAddress = colCache.encodeAddress(addr.row + height, addr.col);
      } // iterate rightwards...


      var width = 1;

      while (matchCol(addr, height, addr.col + width)) {
        width++;
      } // mark all included addresses


      for (var i = 0; i < height; i++) {
        for (var j = 0; j < width; j++) {
          otherAddress = colCache.encodeAddress(addr.row + i, addr.col + j);
          dvMap[otherAddress].marked = true;
        }
      }

      if (height > 1 || width > 1) {
        var bottom = addr.row + (height - 1);
        var right = addr.col + (width - 1);
        return _objectSpread({}, dv.dataValidation, {
          sqref: "".concat(dv.address, ":").concat(colCache.encodeAddress(bottom, right))
        });
      }

      return _objectSpread({}, dv.dataValidation, {
        sqref: dv.address
      });
    }

    return null;
  }).filter(Boolean);
}

var DataValidationsXform =
/*#__PURE__*/
function (_BaseXform) {
  _inherits(DataValidationsXform, _BaseXform);

  function DataValidationsXform() {
    _classCallCheck(this, DataValidationsXform);

    return _possibleConstructorReturn(this, _getPrototypeOf(DataValidationsXform).apply(this, arguments));
  }

  _createClass(DataValidationsXform, [{
    key: "render",
    value: function render(xmlStream, model) {
      var optimizedModel = optimiseDataValidations(model);

      if (optimizedModel.length) {
        xmlStream.openNode('dataValidations', {
          count: optimizedModel.length
        });
        optimizedModel.forEach(function (value) {
          xmlStream.openNode('dataValidation');

          if (value.type !== 'any') {
            xmlStream.addAttribute('type', value.type);

            if (value.operator && value.type !== 'list' && value.operator !== 'between') {
              xmlStream.addAttribute('operator', value.operator);
            }

            if (value.allowBlank) {
              xmlStream.addAttribute('allowBlank', '1');
            }
          }

          if (value.showInputMessage) {
            xmlStream.addAttribute('showInputMessage', '1');
          }

          if (value.promptTitle) {
            xmlStream.addAttribute('promptTitle', value.promptTitle);
          }

          if (value.prompt) {
            xmlStream.addAttribute('prompt', value.prompt);
          }

          if (value.showErrorMessage) {
            xmlStream.addAttribute('showErrorMessage', '1');
          }

          if (value.errorStyle) {
            xmlStream.addAttribute('errorStyle', value.errorStyle);
          }

          if (value.errorTitle) {
            xmlStream.addAttribute('errorTitle', value.errorTitle);
          }

          if (value.error) {
            xmlStream.addAttribute('error', value.error);
          }

          xmlStream.addAttribute('sqref', value.sqref);
          (value.formulae || []).forEach(function (formula, index) {
            xmlStream.openNode("formula".concat(index + 1));

            if (value.type === 'date') {
              xmlStream.writeText(utils.dateToExcel(new Date(formula)));
            } else {
              xmlStream.writeText(formula);
            }

            xmlStream.closeNode();
          });
          xmlStream.closeNode();
        });
        xmlStream.closeNode();
      }
    }
  }, {
    key: "parseOpen",
    value: function parseOpen(node) {
      switch (node.name) {
        case 'dataValidations':
          this.model = {};
          return true;

        case 'dataValidation':
          {
            this._address = node.attributes.sqref;
            var dataValidation = node.attributes.type ? {
              type: node.attributes.type,
              formulae: []
            } : {
              type: 'any'
            };

            if (node.attributes.type) {
              assignBool(dataValidation, node.attributes, 'allowBlank');
            }

            assignBool(dataValidation, node.attributes, 'showInputMessage');
            assignBool(dataValidation, node.attributes, 'showErrorMessage');

            switch (dataValidation.type) {
              case 'any':
              case 'list':
              case 'custom':
                break;

              default:
                assign(dataValidation, node.attributes, 'operator', 'between');
                break;
            }

            assign(dataValidation, node.attributes, 'promptTitle');
            assign(dataValidation, node.attributes, 'prompt');
            assign(dataValidation, node.attributes, 'errorStyle');
            assign(dataValidation, node.attributes, 'errorTitle');
            assign(dataValidation, node.attributes, 'error');
            this._dataValidation = dataValidation;
            return true;
          }

        case 'formula1':
        case 'formula2':
          this._formula = [];
          return true;

        default:
          return false;
      }
    }
  }, {
    key: "parseText",
    value: function parseText(text) {
      if (this._formula) {
        this._formula.push(text);
      }
    }
  }, {
    key: "parseClose",
    value: function parseClose(name) {
      var _this = this;

      switch (name) {
        case 'dataValidations':
          return false;

        case 'dataValidation':
          if (!this._dataValidation.formulae || !this._dataValidation.formulae.length) {
            delete this._dataValidation.formulae;
            delete this._dataValidation.operator;
          }

          if (this._address.includes(':')) {
            var range = new Range(this._address);
            range.forEachAddress(function (address) {
              _this.model[address] = _this._dataValidation;
            });
          } else {
            this.model[this._address] = this._dataValidation;
          }

          return true;

        case 'formula1':
        case 'formula2':
          {
            var formula = this._formula.join('');

            switch (this._dataValidation.type) {
              case 'whole':
              case 'textLength':
                formula = parseInt(formula, 10);
                break;

              case 'decimal':
                formula = parseFloat(formula);
                break;

              case 'date':
                formula = utils.excelToDate(parseFloat(formula));
                break;

              default:
                break;
            }

            this._dataValidation.formulae.push(formula);

            this._formula = undefined;
            return true;
          }

        default:
          return true;
      }
    }
  }, {
    key: "tag",
    get: function get() {
      return 'dataValidations';
    }
  }]);

  return DataValidationsXform;
}(BaseXform);

module.exports = DataValidationsXform;
//# sourceMappingURL=conditional-formatting-ext-xform.js.map
