/* global Currency,OpenDocument,Node */

/**
 * 
 * @constructor
 * @param {OpenDocument.XmlWriter} xmlWriter
 * @param {Object} options
 */
OpenDocument.OdsConverter = function (xmlWriter, options) {
    this.xmlWriter = xmlWriter;
    this.sheetName = "";
    this.nameMap = {};
    this.flatOds = true;
    this.styleManager = new OpenDocument.StyleManager();
    this.cellStyleNumber = 1;
    this.currencyMap = {};
    this.fixedRows = 0;
    this.fixedColumns = 0;
    this.textDataAttribute = "text";
    this.defaultCurrencyCode = "";
    this.defaultDatePattern = "YYYY-MM-DD";
    this.defaultCellStyle = "Standard";
    this.odHiddenFunctions = {
        row: null,
        cell: null,
        content: null
    };
    this.styleNameFunctions = {
        row: null,
        cell: null,
        column: null
    };
    if (options) {
        if (options.datePattern) {
            this.defaultDatePattern = options.datePattern;
        }
        if (options.currency) {
            this.defaultCurrencyCode = options.currency;
        }
        if (options.textAlias) {
            this.textDataAttribute = options.textAlias;
        }
        if (options.cellStyle) {
            this.defaultCellStyle = options.cellStyle;
        }
        if (options.functions) {
            if (options.functions.odHidden) {
                for(let odHiddenFunctionName in options.functions.odHidden) {
                    this.odHiddenFunctions[odHiddenFunctionName] = options.functions.odHidden[odHiddenFunctionName];
                }
            }
            if (options.functions.styleName) {
                for(let styleNameFunctionName in options.functions.styleName) {
                    this.styleNameFunctions[styleNameFunctionName] = options.functions.styleName[styleNameFunctionName];
                }
            }
        }
    }
};

OpenDocument.OdsConverter.convertToBlob = function (table, options) {
    var xml = OpenDocument.OdsConverter.convertToXml(table, options);
    return new Blob([xml], {type: OpenDocument.SPREADSHEET_MIMETYPE });
};

OpenDocument.OdsConverter.convertToXml = function (table, options) {
    var xmlWriter = new OpenDocument.XmlWriter({prettyXml: true});
    var odsConverter = new OpenDocument.OdsConverter(xmlWriter, options);
    odsConverter.convert(table);
    return xmlWriter.xml;
};

OpenDocument.OdsConverter.prototype.checkOdData = function (table) {
    let odCurrency = table.dataset.odCurrency;
    if (odCurrency) {
        this.defaultCurrencyCode = odCurrency;
    }
    let sheetName = table.dataset.odSheetname;
    if (sheetName) {
        this.sheetName = sheetName;
    }
    let fixedRows = table.dataset.odFixedRows;
    if (fixedRows) {
        this.fixedRows = fixedRows;
    }
    let fixedColumns = table.dataset.odFixedColumns;
    if (fixedColumns) {
        this.fixedColumns = fixedColumns;
    }
    let odDatePattern = table.dataset.odDatePattern;
    if (odDatePattern) {
        this.defaultDatePattern = odDatePattern;
    }
    let odCellStyle = table.dataset.odCellStyle;
    if (odCellStyle) {
        this.defaultCellStyle = odCellStyle;
    }
};


OpenDocument.OdsConverter.prototype.convert = function (table) {
    if (typeof table === "string") {
        table = document.getElementById(table);
    }
    this.checkOdData(table);
    var converter = this;
    var styleManager = this.styleManager;
    var cellBuilderOptions = {
        textDataAttribute: converter.textDataAttribute,
        hiddenFunction: _getContentHiddenFunction(),
        styleNameFunction: converter.styleNameFunctions.cell
    };
    var cellCounter = new OpenDocument.CellCounter();
    var columnArray = converter.readTableColumns(table, styleManager);
    var cellStyleByColumnArray = [];
    var xw = new OpenDocument.XmlWriter({indentLength: 3});
    xw
            .openTable(converter.sheetName);
    for(let tableColumn of columnArray) {
        tableColumn.populateCellStyleArray(cellStyleByColumnArray);
        xw
                .addTableColumn(tableColumn);
    }
    for(let row of table.rows) {
        if (_isOdHidden(converter.odHiddenFunctions.row, row)) {
            continue;
        }
        cellCounter.newRow();
        xw
                .openTableRow(_getRowStyleName(row));
        for(let cell of row.cells) {
            _addCell(cell);
        }
        xw
                .closeTableRow();
    }
    xw
            .closeTable(xw);
    this.ods(xw.xml);

    
    function _addCell(cell) {
        if (_isOdHidden(converter.odHiddenFunctions.cell, cell)) {
            return;
        }
        let odCell = OpenDocument.Elements.Cell.build(cell, styleManager, cellBuilderOptions);
        _updateCounter(odCell);
        if (odCell.isEmpty()) {
            xw
                    .addEmptyTableCell(odCell);
        } else {
            switch(odCell.getType()) {
                case "number":
                   xw
                           .addNumberTableCell(odCell);
                   break;
                case "date":
                   let datePattern = odCell.getDatePattern(converter.defaultDatePattern);
                   odCell.styleName = styleManager.getAutomaticCellStyleName("date", _getParentStyleName(odCell), datePattern);
                   xw
                           .addDateTableCell(odCell);
                   break;
               case "currency":
                   let currencyCode = odCell.getCurrencyCode(converter.defaultCurrencyCode);
                   odCell.styleName = styleManager.getAutomaticCellStyleName("currency", _getParentStyleName(odCell), currencyCode);
                   xw
                           .addCurrencyTableCell(odCell, currencyCode);
                   break;
                default:
                    xw
                            .addStringTableCell(odCell);
            }
        }
    }
    
    function _isOdHidden(odHiddenFunction, element) {
        let display = window.getComputedStyle(element).display;
        let odHidden;
        if (odHiddenFunction) {
            odHidden = odHiddenFunction(element);
        }
        if ((!odHidden) && (odHidden !== 0)) {
            odHidden = OpenDocument.checkOdHidden(element);
        }
        if (((display === "none") && (odHidden !== -1)) || (odHidden === 1)) {
            return true;
        } else {
            return false;
        }
    }
    
    function _getRowStyleName(row) {
        if (converter.styleNameFunctions.row) {
            let customStyleName = converter.styleNameFunctions.row(row, styleManager);
            if (customStyleName) {
                return customStyleName;
            }
        }
        let styleName = row.dataset.odStyle;
        if (styleName) {
            return styleName;
        }
        styleName = styleManager.getMatchingStyleName("row", row);
        if (styleName) {
            return styleName;
        }
        if ((row.parentNode.tagName) && (row.parentNode.tagName.toLowerCase() === "thead")) {
            return "Header";
        } else {
            return "Standard";
        }
    }
    
    function _getParentStyleName(odCell) {
        let styleName = odCell.styleName;
        if (styleName) {
            return styleName;
        };
        let columnIndex = odCell.columnIndex;
        if ((columnIndex > -1) && (columnIndex < cellStyleByColumnArray.length)) {
            return cellStyleByColumnArray[columnIndex];
        }
        return converter.defaultCellStyle;
    }
    
    function _updateCounter(odCell) {
        for(let i = 0; i < odCell.emptyPrevious; i++) {
            cellCounter.newCell(1,1);
        }
        odCell.columnIndex = cellCounter.column - 1;
        odCell.jump = cellCounter.newCell(odCell.rowSpan, odCell.colSpan);
        for(let i = 0; i < odCell.emptyNext; i++) {
            cellCounter.newCell(1,1);
        }
    }
    
    function _getContentHiddenFunction() {
        if (converter.odHiddenFunctions.content) {
            return converter.odHiddenFunctions.content;
        } else {
            return OpenDocument.checkOdHidden;
        }
    }

};

OpenDocument.OdsConverter.prototype.ods = function (tableXml) {
    var xw = this.xmlWriter;
    xw.appendXMLDeclaration();
    if (this.flatOds) {
        xw
                .openDocument(OpenDocument.SPREADSHEET_MIMETYPE);
    } else {
        xw
                .openDocumentContent();
    }
    if ((this.fixedColumns > 0) || (this.fixedRows > 0)) {
        xw
                .openSettings()
                .openConfigItemSet("ooo:view-settings")
                .openConfigItemMapIndexed("Views")
                .openConfigItemMapEntry( "")
                .addConfigItem("ViewId", "string", "view1")
                .openConfigItemMapNamed("Tables")
                .openConfigItemMapEntry(this.sheetName);
        if (this.fixedColumns > 0) {
            xw
                    .addConfigItem("HorizontalSplitMode", "short", "2")
                    .addConfigItem("HorizontalSplitPosition", "int", this.fixedColumns)
                    .addConfigItem("PositionLeft", "int", "0")
                    .addConfigItem("PositionRight", "int", this.fixedColumns);
        }
        if (this.fixedRows > 0) {
            xw
                    .addConfigItem("VerticalSplitMode", "short", "2")
                    .addConfigItem("VerticalSplitPosition", "int", this.fixedRows)
                    .addConfigItem("PositionTop", "int", "0")
                    .addConfigItem("PositionBottom", "int", this.fixedRows);
        }
        xw
                .closeConfigItemMapEntry()
                .closeConfigItemMapNamed()
                .closeConfigItemMapEntry()
                .closeConfigItemMapIndexed()
                .closeConfigItemSet()
                .closeSettings();
    }
    xw
            .openStyles()
    this.styleManager.writeStyles("cell-named", xw);
    xw
            .closeStyles();
    xw
            .openAutomaticStyles();
    this.styleManager.writeStyles("row-named", xw);
    this.styleManager.writeStyles("column-automatic", xw);
    this.styleManager.writeDataStyles(xw);
    this.styleManager.writeStyles("cell-automatic", xw);
    xw
            .closeAutomaticStyles()
            .openBody()
            .openSpreadsheet(xw)
            .write(tableXml)
            .closeSpreadsheet()
            .closeBody();
    if (this.flatOds) {
        xw
                .closeDocument();
    } else {
        xw
                .closeDocumentContent();
    }
};

OpenDocument.OdsConverter.prototype.checkSheetName = function (name) {
    if (!name) {
        name = "sheet";
    }
    name = OpenDocument.checkSheetName(name);
    if (!this.nameMap.hasOwnProperty(name)) {
        this.nameMap[name] = true;
        return name;
    } else {
        var p = 2;
        while (true) {
            var newName = name + " (" + p + ")";
            if (!this.nameMap.hasOwnProperty(newName)) {
                this.nameMap[newName] = true;
                return newName;
            }
            p++;
        }
    }
};

OpenDocument.OdsConverter.prototype.readTableColumns = function(table, styleManager) {
    var converter = this;
    let colElementArray = new Array();
    let result = new Array();
    let colgroupList = table.getElementsByTagName("colgroup");
    for(let colgroup of colgroupList) {
        let colList = colgroup.getElementsByTagName("col");
        if (colList.length > 0) {
            for(let col of colList) {
                colElementArray.push(col);
            }
        } else {
            colElementArray.push(colgroup);
        }
    }
    let columnNumber = 1;
    for(let col of colElementArray) {
        let columnName = OpenDocument.COLUMNSTYLE_PREFIX + columnNumber;
        columnNumber++;
        let columnStyle = new OpenDocument.Style("column", columnName);
        let originalStyleName = _getColumnStyleName(col);
        if (originalStyleName) {
            let originalStyle = styleManager.getStyle("column-named", originalStyleName);
            if (originalStyle) {
                columnStyle.copyProperties(originalStyle);
            }
        }
        let customWidth = col.dataset.odWidth;
        if (customWidth) {
            columnStyle.putStyleProperty(OpenDocument.Style.STYLEPROPERTYDEFS["column-width"], customWidth);
        }
        styleManager.putStyle("column-automatic", columnStyle);
        let customCellStyle = col.dataset.odCellStyle;
        if (!customCellStyle) {
            customCellStyle = converter.defaultCellStyle;
        }
        result.push(new OpenDocument.Elements.TableColumn(columnName, col.span, customCellStyle));
    }
    return result;
    
    
    function _getColumnStyleName(col) {
        if (converter.styleNameFunctions.column) {
            let customStyleName = converter.styleNameFunctions.column(col, styleManager);
            if (customStyleName) {
                return customStyleName;
            }
        }
        let styleName =  col.dataset.odStyle;
        if (styleName) {
            return styleName;
        }
        styleName = styleManager.getMatchingStyleName("column", col);
        if (styleName) {
            return styleName;
        }
        return "";
    }
    
};
