/*!
 * 
 * 4DView Pro library 0.0.0
 * 
 * Copyright(c) 4D SAS.  All rights reserved.
 * 
 * 4D (the "Software") and the corresponding source code remain
 * the exclusive property of 4D and/or its licensors and are protected by national
 * and/or international legislations.
 * 
 * This file is part of the source code of the Software provided under the relevant
 * 4D License Agreement available on http://www.4D.com/license whose compliance
 * constitutes a prerequisite to any use of this file and more generally of the
 * Software and the corresponding source code.
 * 
 */

Utils.addCommand('import-json', function (doc) {
  Utils.currentDocument = {};

  if (doc.version) Utils.currentDocument.d4Version = doc.version;
  if (doc.dateCreation) Utils.currentDocument.d4DateCreation = doc.dateCreation;
  if (doc.dateModified) Utils.currentDocument.d4DateModified = doc.dateModified;
  if (doc.meta) Utils.currentDocument.d4Meta = doc.meta;

  Utils.spread.suspendPaint();

   try {

    if (useRibbon) {
      Utils.spread.fromJSON(doc.spreadJS);
      designer.ribbon.updateRibbonBarStyle();
    } else if (useToolbar) {
      importJson(doc.spreadJS);
    } else {
      Utils.spread.fromJSON(doc.spreadJS);
    }
  } catch (e) { }

  Utils.spread.resumePaint();
});

Utils.addCommand('export-json', function (params) {
  let doc = {};
  let document = Utils.currentDocument;

  if (document.d4Version) doc.version = document.d4Version;
  if (document.d4DateCreation) doc.dateCreation = document.d4DateCreation;
  if (document.d4DateModified) doc.dateModified = document.d4DateModified;
  if (document.d4Meta) doc.meta = document.d4Meta;

  let serializationOption = {};
  if (params["valuesOnly"] != null)
    serializationOption.ignoreFormula = params.valuesOnly;

  doc.spreadJS = Utils.spread.toJSON(serializationOption);

    Utils.addFormatedText(doc.spreadJS);

  return doc;
});

Utils.addCommand('export-excel', function (params) {
  let serializationOption = {};
  if (params["valuesOnly"] != null)
    serializationOption.ignoreFormula = params.valuesOnly;

  let json = Utils.spread.toJSON(serializationOption);

  let options = {};
  if (params["password"] != null)
    options.password = params.password;

  var excelIO = new GC.Spread.Excel.IO();
  excelIO.save(json,
    function (blob) {
      var reader = new FileReader();
      reader.onloadend = function () {
        params.content = reader.result.substr(reader.result.indexOf(',') + 1);
        $4d._vp_callback(params);
      }
      reader.readAsDataURL(blob);
    },
    function (e) {
      params.error = e;
      $4d._vp_callback(params);
    },
    options);
});

Utils.addCommand('import-excel', function (params) {
  var blob = Utils.b64ToBlob(params.content);

  let options = {};
  if (params["password"] != null)
    options.password = params.password;

  var excelIO = new GC.Spread.Excel.IO();
  excelIO.open(blob,
    function (json) {

      Utils.spread.suspendPaint();

      try {
        if (useRibbon) {
          Utils.spread.fromJSON(json);
          designer.ribbon.updateRibbonBarStyle();
        }
        else if (useToolbar) {
          importJson(json);
        } else {
          Utils.spread.fromJSON(json);
        }
      } catch (e) {
        params.error = e;
        $4d._vp_callback(params);
      }

      Utils.spread.resumePaint();

      $4d._vp_callback(params);
    },
    function (e) {
      params.error = e;
      $4d._vp_callback(params);
    },
    options);
});

Utils.addCommand('export-pdf', function (params) {
  var sheetIndex = Utils.resolveSheetIndex(params.sheetIndex);

  var options = null;
  if (('pdfOptions' in params) && (typeof (params.pdfOptions) === 'object')) {
    options = params.pdfOptions;
  }

  Utils.computePdfFonts(sheetIndex, function (pictures) {

    Utils.spread.savePDF(function (blob) {
      Utils.restorePictures(pictures);
      var reader = new FileReader();
      reader.onloadend = function () {
        params.content = reader.result.substr(reader.result.indexOf(',') + 1);
        $4d._vp_callback(params);
      }
      reader.readAsDataURL(blob);
    },
      function (e) {
        params.error = e;
        $4d._vp_callback(params);
      },
      options,
      sheetIndex);
  });
});

Utils.addCommand('register-pdf-fonts', function (params) {

  params.fonts.forEach(element => {
    if (element.font === null) {
      GC.Spread.Sheets.PDF.PDFFontsManager.registerFont(element.name);
    } else {
      GC.Spread.Sheets.PDF.PDFFontsManager.registerFont(element.name, element.font);
    }
  });

});

Utils.restorePictures = function (pictures) {
  pictures.forEach(element => {
    let cell = Utils.spread.getSheet(element.sheet).getCell(element.row, element.column);
    cell.backgroundImage(null);
    cell.formula(element.formula);
    cell.cellType(element.cellType);
  });
}

Utils.computePdfFonts = function (sheetIndex, callback) {

  let json = Utils.spread.toJSON();
  let fonts = {};
  let pictures = [];

  function handleNamedStyles(namedStyles) {
    if (namedStyles.constructor == Array) {
      namedStyles.forEach(namedStyle => {
        if (typeof namedStyle == 'object') {
          if ('font' in namedStyle) {
            fonts[namedStyle.font] = "";
          }
        }
      });
    }
  }

  for (var sheetName in json.sheets) {
    let sheet = json.sheets[sheetName];
    if ((sheetIndex == null) || (sheet.index == sheetIndex)) {
      if (('data' in sheet) && ('dataTable' in sheet.data)) {
        let dataTable = sheet.data.dataTable;
        for (var row in dataTable) {
          for (var column in dataTable[row]) {
            let cell = dataTable[row][column];
            if (('style' in cell) && (typeof cell.style == 'object')) {

              if ('cellType' in cell.style) {
                if ('name' in cell.style.cellType) {
                  if (cell.style.cellType.name === 'D4Picture') {
                    let liveCell = Utils.spread.getSheet(sheet.index).getCell(Number(row), Number(column));

                    pictures.push({
                      sheet: sheet.index,
                      row: Number(row),
                      column: Number(column),
                      formula: liveCell.formula()
                     });

                    liveCell.formula("");
                    liveCell.text("");
                    liveCell.backgroundImage(cell.style.cellType.pict);
                  }
                }
              }

              if ('font' in cell.style) {
              fonts[cell.style.font] = "";
            }
          }
        }
      }
      }
      if ('namedStyles' in sheet) {
        handleNamedStyles(sheet.namedStyles);
      }
    }
  }

  if ('namedStyles' in json) {
    handleNamedStyles(json.namedStyles);
  }

  $4d._vp_computeFonts(fonts, function (toEmbed) {
    for (var font in toEmbed) {
      GC.Spread.Sheets.PDF.PDFFontsManager.registerFont(font, toEmbed[font]);
    }
    callback(pictures);
  });
}

// parse through all the cells of the document and add the formated values
// to allow to convert the document to SVG without messing with computing them outside SpreadJS
Utils.addFormatedText = function (json) {

  let formatterPool = {};
  let generalDateFormatter = null;
  let styleSheetPoolFormat = {};
  let cell = {};
  let row = "";
  let column = "";
  let sheet = null;
  let sheetIndex = 0;
  let longDatePattern = GC.Spread.Common.CultureManager.getCultureInfo().DateTimeFormat.defaultDatePattern;
  let shortDatePattern = GC.Spread.Common.CultureManager.getCultureInfo().DateTimeFormat.shortDatePattern;


  function _getFormatInfo(format) {

    // use a pool for formatter to avoid to create a new formatter for the same format
    if (formatterPool[format] == undefined) {
      formatterPool[format] = new GC.Spread.Formatter.GeneralFormatter(format);
    }

    let formatter = formatterPool[format];

    let val;
    if ((typeof (cell.value) === 'string') && (cell.value.startsWith("/OADate(")) && (cell.value.endsWith(")/"))) {
      val = Utils.spread.getSheet(sheetIndex).getCell(row, column).value();
      if (format === 'General') {
        if (generalDateFormatter == null) {
          let culture = GC.Spread.Common.CultureManager.getCultureInfo();
          generalDateFormatter = new GC.Spread.Formatter.GeneralFormatter(culture.DateTimeFormat.shortDatePattern);
        }
        formatter = generalDateFormatter;
      }
    } else {
      val = cell.value;
    }

    let formatInfo = {};
    let formated = formatter.format(val, formatInfo);
    let result = {};

    if (typeof (formatInfo) == 'object') {
      if ('conditionalForeColor' in formatInfo) {
        result.color = formatInfo.conditionalForeColor;
      }
      if (('content' in formatInfo) && (formatInfo.content.some(element => {
        return element.type === 'fillingChar';
      }))) {
        result.content = formatInfo.content;
      } else {
        result.text = formated;
      }
    } else {
      result.text = formated;
    }
    return result;
  }

  function _handleStyleSheet(stylesheetName, lookAtSheetLevel) {
    let aFormatter = null;

    function __resolveStyleSheetByName(name) {

      function ___resolveStyleSheet(styleSheet) {
        styleSheet.some(style => {
          if (style.name === name) {
            if ('formatter' in style) {
              aFormatter = style.formatter;
            } else if ('parentName' in style) {
              aFormatter = __resolveStyleSheetByName(style.parentName)
            }
            return true;
          } else {
            return false;
          }
        });
      }

      if (lookAtSheetLevel && ('namedStyles' in sheet) && (sheet.namedStyles.constructor == Array)) {
        ___resolveStyleSheet(sheet.namedStyles);
      }

      if ((aFormatter == null) && ('namedStyles' in json) && (json.namedStyles.constructor == Array)) {
        lookAtSheetLevel = false;
        ___resolveStyleSheet(json.namedStyles);
      }
    }

    // use a pool of format per stylesheet to avoid to compute the format for each stylesheet
    if (stylesheetName in styleSheetPoolFormat) {
      aFormatter = styleSheetPoolFormat[stylesheetName];
    } else {
      __resolveStyleSheetByName(stylesheetName);
      styleSheetPoolFormat[stylesheetName] = aFormatter;
    }
    return aFormatter;
  }

  function _getRowColumnFormatter(name, value) {
    let aFormatter = null;
    if (name in sheet.data) {
      if (sheet.data[name].constructor == Array) {
        if (value < sheet.data[name].length) {
          let aStyle = sheet.data[name][value];
          if (typeof aStyle === 'object') {
            if ('style' in aStyle) {
              if (typeof aStyle.style === 'string') {
                aFormatter = _handleStyleSheet(aStyle.style, true);
              } else if (typeof aStyle.style == 'object') {
                if ('formatter' in aStyle.style) {
                  aFormatter = aStyle.style.formatter;
                }
              }
            }
          }
        }
      }
    }
    return aFormatter;
  }

  function _noFormatInfo() {
    return !(('style' in cell) && (typeof cell.style == 'object') && ('formatInfo' in cell.style));
  }

  function _createFormatInfo(aFormatter) {
    if (!('style' in cell)) {
      cell.style = {};
    }
    cell.style.formatInfo = _getFormatInfo(aFormatter);
  }

  for (let sheetName in json.sheets) {
    sheet = json.sheets[sheetName];
    styleSheetPoolFormat = {};
    if (('data' in sheet) && ('dataTable' in sheet.data)) {

      let defaultFormatter = null;
      if (('defaultDataNode' in sheet.data) && ('style' in sheet.data.defaultDataNode))
        if (typeof sheet.data.defaultDataNode.style == 'string') {
          defaultFormatter = _handleStyleSheet(sheet.data.defaultDataNode.style, false);
        } else if (typeof sheet.data.defaultDataNode.style == 'object') {
          if ('formatter' in sheet.data.defaultDataNode.style) {
            defaultFormatter = sheet.data.defaultDataNode.style.formatter;
          }
          }

      let dataTable = sheet.data.dataTable;
      for (row in dataTable) {

        let rowFormatter = _getRowColumnFormatter('rowDataArray', row);

        for (column in dataTable[row]) {

          let columnFormatter = _getRowColumnFormatter('columnDataArray', column);

          cell = dataTable[row][column];
          if ('value' in cell) {
            if ('style' in cell) {
              let formatter;
              if (typeof cell.style == 'object') {
                if ('formatter' in cell.style) {
                  cell.style.formatInfo = _getFormatInfo(cell.style.formatter);
                } else if (('autoFormatter' in cell.style)
                  && (typeof cell.style.autoFormatter == 'object')
                  && ('formatCached' in cell.style.autoFormatter)) {
                  cell.style.formatInfo = _getFormatInfo(cell.style.autoFormatter.formatCached);
                } else if (('name' in cell.style) && (typeof cell.style.name === 'string') && (cell.style.name.length > 0)) {
                  // stylesheet name can be in "name" attribute of the style object
                  formatter = _handleStyleSheet(cell.style.name, true);
                  if (formatter != null) {
                    cell.style.formatInfo = _getFormatInfo(formatter);
                  }
                } else if (('parentName' in cell.style) && (typeof cell.style.parentName === 'string') && (cell.style.parentName.length > 0)) {
                  // stylesheet name can be also in "parentName" attribute of the style object
                  formatter = _handleStyleSheet(cell.style.parentName, true);
                  if (formatter != null) {
                    cell.style.formatInfo = _getFormatInfo(formatter);
                  }
                }
              } else if (typeof cell.style == 'string') {
                // this is a very particular case where the style is not an object 
                // but a string that contains the name of the stylesheet
                let styleName = cell.style
                cell.style = { 'parentName': styleName };
                formatter = _handleStyleSheet(styleName, true);
                if (formatter != null) {
                  cell.style.formatInfo = _getFormatInfo(formatter);
                }
              }
            }


            // apply column formatter if any and if no previous formatting has been applied
            if ((columnFormatter != null) && _noFormatInfo()) {
              _createFormatInfo(columnFormatter);
            }

            // apply row formatter if any and if no previous formatting has been applied
            if ((rowFormatter != null) && _noFormatInfo()) {
              _createFormatInfo(rowFormatter);
            }

            // apply default formatter if any and if no previous formatting has been applied
            if ((defaultFormatter != null) && _noFormatInfo()) {
              _createFormatInfo(defaultFormatter);
            }

            // apply default date formatting
            if (_noFormatInfo()
              && ((typeof (cell.value) === 'string')
                && (cell.value.startsWith("/OADate("))
                && (cell.value.endsWith(")/")))) {
              if (cell.value.indexOf(".") > -1) {
                _createFormatInfo(longDatePattern);
              } else {
                _createFormatInfo(shortDatePattern);
              }
            }
          }
        }
      }
    }
    sheetIndex++;
  }
}