/* global OpenDocument,Currency,XmlWriter */

/**
 * 
 * @constructor
 * @param {type} options
 */
OpenDocument.XmlWriter = function (options) {
    XmlWriter.call(this, options);
};

OpenDocument.XmlWriter.prototype = Object.create(XmlWriter.prototype);

OpenDocument.XmlWriter.prototype.constructor = OpenDocument.XmlWriter;

OpenDocument.XmlWriter.prototype.openDocument = function (mimetype) {
    this
            .startOpenTag("office:document")
            .addXmlnsAttributes()
            .addAttribute("office:mimetype", mimetype)
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeDocument = function () {
    this
            .closeTag("office:document");
    return this;
};


OpenDocument.XmlWriter.prototype.openDocumentContent = function () {
    this
            .startOpenTag("office:document-content")
            .addXmlnsAttributes()
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.addXmlnsAttributes = function () {
    this
            .addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0")
            .addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0")
            .addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0")
            .addAttribute("xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0")
            .addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0")
            .addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0")
            .addAttribute("xmlns:config", "urn:oasis:names:tc:opendocument:xmlns:config:1.0")
            .addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink")
            .addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/")
            .addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")
            .addAttribute("xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0")
            .addAttribute("xmlns:presentation", "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0")
            .addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0")
            .addAttribute("xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0")
            .addAttribute("xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0")
            .addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML")
            .addAttribute("xmlns:form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0")
            .addAttribute("xmlns:script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0")
            .addAttribute("xmlns:ooo", "http://openoffice.org/2004/office")
            .addAttribute("xmlns:ooow", "http://openoffice.org/2004/writer")
            .addAttribute("xmlns:oooc", "http://openoffice.org/2004/calc")
            .addAttribute("xmlns:dom", "http://www.w3.org/2001/xml-events")
            .addAttribute("xmlns:xforms", "http://www.w3.org/2002/xforms")
            .addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema")
            .addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
            .addAttribute("xmlns:rpt", "http://openoffice.org/2005/report")
            .addAttribute("xmlns:of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2")
            .addAttribute("xmlns:xhtml", "http://www.w3.org/1999/xhtml")
            .addAttribute("xmlns:grddl", "http://www.w3.org/2003/g/data-view#")
            .addAttribute("xmlns:tableooo", "http://openoffice.org/2009/table")
            .addAttribute("xmlns:drawooo", "http://openoffice.org/2010/draw")
            .addAttribute("xmlns:calcext", "urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0")
            .addAttribute("xmlns:loext", "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0")
            .addAttribute("xmlns:field", "urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0")
            .addAttribute("xmlns:formx", "urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0")
            .addAttribute("xmlns:css3t", "http://www.w3.org/TR/css3-text/")
            .addAttribute("office:version", "1.2");
    return this;
};

OpenDocument.XmlWriter.prototype.openDocumentContentVersion1 = function () {
    this
            .startOpenTag("office:document-content")
            .addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0")
            .addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0")
            .addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0")
            .addAttribute("xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0")
            .addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0")
            .addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0")
            .addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink")
            .addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/")
            .addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")
            .addAttribute("xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0")
            .addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0")
            .addAttribute("xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0")
            .addAttribute("xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0")
            .addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML")
            .addAttribute("xmlns:form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0")
            .addAttribute("xmlns:script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0")
            .addAttribute("xmlns:ooo", "http://openoffice.org/2004/office")
            .addAttribute("xmlns:ooow", "http://openoffice.org/2004/writer")
            .addAttribute("xmlns:oooc", "http://openoffice.org/2004/calc")
            .addAttribute("xmlns:dom", "http://www.w3.org/2001/xml-events")
            .addAttribute("xmlns:xforms", "http://www.w3.org/2002/xforms")
            .addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema")
            .addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
            .addAttribute("office:version", "1.0")
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeDocumentContent = function () {
    this
            .closeTag("office:document-content");
    return this;
};

OpenDocument.XmlWriter.prototype.openStyles = function () {
    this
            .openTag("office:styles");
    return this;
};

OpenDocument.XmlWriter.prototype.closeStyles = function () {
    this
            .closeTag("office:styles");
    return this;
};

OpenDocument.XmlWriter.prototype.openAutomaticStyles = function () {
    this
            .openTag("office:automatic-styles");
    return this;
};

OpenDocument.XmlWriter.prototype.closeAutomaticStyles = function () {
    this
            .closeTag("office:automatic-styles");
    return this;
};

OpenDocument.XmlWriter.prototype.openBody = function () {
    this
            .openTag("office:body");
    return this;
};

OpenDocument.XmlWriter.prototype.closeBody = function () {
    this
            .closeTag("office:body");
    return this;
};

OpenDocument.XmlWriter.prototype.openSpreadsheet = function () {
    this
            .openTag("office:spreadsheet");
    return this;
};

OpenDocument.XmlWriter.prototype.closeSpreadsheet = function () {
    this
            .closeTag("office:spreadsheet");
    return this;
};

OpenDocument.XmlWriter.prototype.openStyle = function (styleName, styleFamily, parentStyleName) {
    this
            .startStyleOpenTag(styleName, styleFamily, parentStyleName)
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.startStyleOpenTag = function (styleName, styleFamily, parentStyleName) {
    this
            .startOpenTag("style:style")
            .addAttribute("style:name", styleName);
    if (styleName.indexOf("_20_") >  -1) {
        let displayName = styleName.replace("_20_", " ");
        this
                .addAttribute("style:display-name", displayName);
    }
    this
            .addAttribute("style:family", styleFamily)
            .addAttribute("style:parent-style-name", parentStyleName);
    return this;
};

OpenDocument.XmlWriter.prototype.closeStyle = function () {
    this
            .closeTag("style:style");
    return this;
};

OpenDocument.XmlWriter.prototype.addDateStyle = function (datePattern, dataStyleName) {
    var xmlWriter = this;
    var length = datePattern.length;
    var buffer = "";
    xmlWriter
            .startOpenTag("number:date-style")
            .addAttribute("style:name", dataStyleName)
            .endOpenTag();
    for(let i = 0; i < length; i++) {
        let char = datePattern.charAt(i);
        let jump = 0;
        switch(char) {
            case 'M':
                jump = _parseMonth(i);
                break;
            case 'D':
                jump = _parseDay(i);
                break;
            case 'Y':
                jump = _parseYear(i);
                if (jump === 0) {
                    _buf(char);
                }
                break;
            default:
                _buf(char);
        }
        i = i + jump;
    }
    _flush();
    xmlWriter
            .closeTag("number:date-style");
    return this;
    
    function _buf(char) {
        buffer = buffer + char;
    }
    
    function _parseMonth(index) {
        _flush();
        let jump = 0;
        let next = index + 1;
        if ((next < length) && (datePattern.charAt(next) === 'M')) {
            jump = 1;
        }
        _addNumberElement("number:month", (jump === 1));
        return jump;
    }
    
    function _parseDay(index) {
        _flush();
        let jump = 0;
        let next = index + 1;
        if ((next < length) && (datePattern.charAt(next) === 'D')) {
            jump = 1;
        }
        _addNumberElement("number:day", (jump === 1));
        return jump;
    }
    
    function _parseYear(index) {
        let next = index + 1;
        if ((next === length) || (datePattern.charAt(next) !== 'Y')) {
            return 0;
        }
        _flush();
        let jump = 1;
        if (next < (length -2)) {
            if ((datePattern.charAt(next + 1) === 'Y') && (datePattern.charAt(next + 2) === 'Y')) {
                jump = 3;
            }
        }
        _addNumberElement("number:year", (jump === 3));
        return jump;
    }
    
    function _flush() {
        if (buffer) {
            xmlWriter.addSimpleElement("number:text", buffer);
            buffer = "";
        }
    }
    
    function _addNumberElement(name, isLong) {
        xmlWriter
            .startOpenTag(name);
        if (isLong) {
            xmlWriter
                .addAttribute("number:style", "long");
        }
        xmlWriter
                .closeEmptyTag();
    }
};

OpenDocument.XmlWriter.prototype.addCurrencyStyle = function (currencyCode, dataStyleName) {
    var currency = Currency.get(currencyCode);
    this
            .startOpenTag("number:currency-style")
            .addAttribute("style:name", dataStyleName + "P0")
            .addAttribute("style:volatile", "true")
            .endOpenTag();
    if (currency.symbolBefore) {
        this
                .addSimpleElement("number:currency-symbol", currency.symbol);
    }
    this
            .startOpenTag("number:number")
            .addAttribute("number:decimal-places", currency.fractionDigits)
            .addAttribute("number:min-integer-digits", "1")
            .addAttribute("number:grouping", "true")
            .closeEmptyTag();
    if (!currency.symbolBefore) {
        this
                .addSimpleElement("number:text", " ")
                .addSimpleElement("number:currency-symbol", currency.symbol);
    }
    this
            .closeTag("number:currency-style")
            .startOpenTag("number:currency-style")
            .addAttribute("style:name", dataStyleName)
            .endOpenTag()
            .startOpenTag("style:text-properties")
            .addAttribute("fo:color", "#ff0000")
            .closeEmptyTag()
            .addSimpleElement("number:text", "-");
    if (currency.symbolBefore) {
        this
                .addSimpleElement("number:currency-symbol", currency.symbol);
    }
    this
            .startOpenTag("number:number")
            .addAttribute("number:decimal-places", currency.fractionDigits)
            .addAttribute("number:min-integer-digits", "1")
            .addAttribute("number:grouping", "true")
            .closeEmptyTag();
    if (!currency.symbolBefore) {
        this
                .addSimpleElement("number:text", " ")
                .addSimpleElement("number:currency-symbol", currency.symbol);
    }
    this
            .startOpenTag("style:map")
            .addAttribute("style:condition", "value()>=0")
            .addAttribute("style:apply-style-name", dataStyleName + "P0")
            .closeEmptyTag()
            .closeTag("number:currency-style");
    return this;
};


OpenDocument.XmlWriter.prototype.openNamedExpressions = function () {
    this
            .openTag("table:named-expressions");
    return this;
};

OpenDocument.XmlWriter.prototype.closeNamedExpressions = function () {
    this
            .closeTag("table:named-expressions");
    return this;
};

OpenDocument.XmlWriter.prototype.addNamedRange = function (name, address) {
    this
            .startOpenTag("table:named-range")
            .addAttribute("table:name", name)
            .addAttribute("table:base-cell-address", address)
            .addAttribute("table:cell-range-address", address)
            .closeEmptyTag();
    return this;
};

OpenDocument.XmlWriter.prototype.openTable = function (name) {
    this
            .startOpenTag("table:table")
            .addAttribute("table:name", OpenDocument.checkSheetName(name))
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeTable = function () {
    this
            .closeTag("table:table");
    return this;
};

OpenDocument.XmlWriter.prototype.addTableColumn = function (tableColumn) {
    this
            .startOpenTag("table:table-column")
            .addAttribute("table:style-name", tableColumn.styleName);
    if (tableColumn.columnsRepeated > 1) {
        this
                .addAttribute("table:number-columns-repeated", tableColumn.columnsRepeated);
    }
    this
            .addAttribute("table:default-cell-style-name", tableColumn.defaultCellStyleName)
            .closeEmptyTag();
    return this;
};

OpenDocument.XmlWriter.prototype.openTableRow = function (rowStyleName) {
    this
            .startOpenTag("table:table-row")
            .addAttribute("table:style-name", rowStyleName)
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeTableRow = function () {
    this
            .closeTag("table:table-row");
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.addEmptyTableCell = function (cell) {
    if (!cell) {
        cell = OpenDocument.Elements.Cell.EMPTY;
    }
    this
            .startCell(cell)
            .endCell(cell, true);
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.addNumberTableCell = function (cell) {
    this
            .startCell(cell)
            .addAttribute("office:value-type", "float")
            .addAttribute("office:value", cell.getValue())
            .endCell(cell, true);
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.addPercentageTableCell = function (cell) {
    this
            .startCell(cell)
            .addAttribute("office:value-type", "percentage")
            .addAttribute("office:value", cell.getValue())
            .endCell(cell, true);
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.addDateTableCell = function (cell) {
    this
            .startCell(cell)
            .addAttribute("office:value-type", "date")
            .addAttribute("office:date-value", cell.getValue())
            .endCell(cell, true);
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @param {String} currency
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.addCurrencyTableCell = function (cell, currency) {
    this
            .startCell(cell)
            .addAttribute("office:value-type", "currency")
            .addAttribute("office:currency", currency)
            .addAttribute("office:value", cell.getValue())
            .endCell(cell, true);
    return this;
};

OpenDocument.XmlWriter.prototype.addLinkStringTableCell = function (cell, linkString) {
    this
            .startCell(cell)
            .addAttribute("office:value-type", "string")
            .endOpenTag();
    if ((linkString) && (linkString.length > 0)) {
        this
                .startOpenTag("text:p")
                .endOpenTag()
                .XmlWriter.prototype.addLink(linkString)
                .closeTag("text:p", false);
    }
    this
        .endCell(cell, false);
            
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.addStringTableCell = function (cell) {
    this
            .startCell(cell)
            .addAttribute("office:value-type", "string")
            .endOpenTag()
            .splitText(cell.textArray)
            .endCell(cell, false);
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @param {String} valueType
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.addFormulaTableCell = function (cell,  valueType) {
    this
            .startCell(cell)
            .addAttribute("table:formula", cell.getValue())
            .addAttribute("office:value-type", valueType)
            .endCell(cell, true);
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.splitText = function (array) {
    for(let text of array) {
        this
            .startOpenTag("text:p")
            .endOpenTag()
            .addText(text)
            .closeTag("text:p", false);
    }
    return this;
};

OpenDocument.XmlWriter.prototype.addLink = function (link) {
    this
            .startOpenTag("text:a", false)
            .addAttribute("xlink:href", link)
            .endOpenTag()
            .addText(link)
            .closeTag("text:a", false);
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.startCell = function (cell) {
    if (cell.emptyPrevious) {
        for(let i = 0; i < cell.emptyPrevious; i++) {
            this
                .addEmptyTableCell();
        }
    }
    var jump = cell.jump;
    if (jump > 0) {
        this
                .startOpenTag("table:covered-table-cell");
        if (jump > 1) {
                this
                    .addAttribute("table:number-columns-repeated", jump);
        }
            this
                .closeEmptyTag();
    }
    this
            .startOpenTag("table:table-cell")
            .addAttribute("table:style-name", cell.styleName);
    if (cell.rowSpan > 1) {
        this.addAttribute("table:number-rows-spanned", cell.rowSpan);
    }
    if (cell.colSpan > 1) {
        this
            .addAttribute("table:number-columns-spanned", cell.colSpan);
    }
    return this;
};

/**
 * 
 * @param {OpenDocument.Elements.Cell} cell
 * @param {Boolean} emptyTag
 * @returns {OpenDocument.XmlWriter}
 */
OpenDocument.XmlWriter.prototype.endCell = function (cell, emptyTag) {
    if (cell.hasAnnotation()) {
        if (emptyTag) {
            this
                .endOpenTag();
        }
        this
            .startOpenTag("office:annotation")
            .endOpenTag()
            .splitText(cell.annotationArray)
            .closeTag("office:annotation");
        this
            .closeTag("table:table-cell");
    } else if (emptyTag) {
        this
            .closeEmptyTag();
    } else {
        this
            .closeTag("table:table-cell");
    }
    if (cell.colSpan > 1) {
        this
                .startOpenTag("table:covered-table-cell");
        if (cell.colSpan > 2) {
            this
                .addAttribute("table:number-columns-repeated", (cell.colSpan - 1));
        }
        this
            .closeEmptyTag();
    }
    if (cell.emptyNext) {
        for(let i = 0; i < cell.emptyNext; i++) {
            this
                .addEmptyTableCell();
        }
    }
    return this;
};

OpenDocument.XmlWriter.prototype.openSettings = function () {
    this
            .openTag("office:settings");
    return this;
};

OpenDocument.XmlWriter.prototype.closeSettings = function () {
    this
            .closeTag("office:settings");
    return this;
};

OpenDocument.XmlWriter.prototype.openConfigItemSet = function (name) {
    this
            .startOpenTag("config:config-item-set")
            .addAttribute("config:name", name)
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeConfigItemSet = function () {
    this
            .closeTag("config:config-item-set");
    return this;
};

OpenDocument.XmlWriter.prototype.openConfigItemMapIndexed = function (name) {
    this
            .startOpenTag("config:config-item-map-indexed")
            .addAttribute("config:name", name)
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeConfigItemMapIndexed = function () {
    this
            .closeTag("config:config-item-map-indexed");
    return this;
};

OpenDocument.XmlWriter.prototype.openConfigItemMapNamed = function (name) {
    this
            .startOpenTag("config:config-item-map-named")
            .addAttribute("config:name", name)
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeConfigItemMapNamed = function () {
    this
            .closeTag("config:config-item-map-named");
    return this;
};

OpenDocument.XmlWriter.prototype.openConfigItemMapEntry = function (name) {
    this
            .startOpenTag("config:config-item-map-entry")
            .addAttribute("config:name", name)
            .endOpenTag();
    return this;
};

OpenDocument.XmlWriter.prototype.closeConfigItemMapEntry = function () {
    this
            .closeTag("config:config-item-map-entry");
    return this;
};

OpenDocument.XmlWriter.prototype.addConfigItem = function (name, type, value) {
    this
            .startOpenTag("config:config-item", true)
            .addAttribute("config:name", name)
            .addAttribute("config:type", type)
            .endOpenTag()
            .addText(value)
            .closeTag("config:config-item", false);
    return this;
};

