/* global OpenDocument */

OpenDocument.StyleManager = function () {
    this.maps = new Map([
       ["cell-named", new Map()], 
       ["row-named", new Map()],
       ["cell-automatic", new Map()],
       ["column-named", new Map()],
       ["column-automatic", new Map()]
    ]);
    this.matcherMap = new Map([
       ["cell", new OpenDocument.StyleMatcher()], 
       ["row", new OpenDocument.StyleMatcher()],
       ["column", new OpenDocument.StyleMatcher()]
    ]);
    for(let object of OpenDocument.StyleManager.DEFAULT_STYLES ) {
        this.putStyle(object.type + "-named", _buildDefault(object));
    }
    this.automaticCellStyleNumber = 1;
    this.dataStyleNumber = 1;
    this.currencyDataStyleMap = new Map();
    this.dateDataStyleMap = new Map();
    OpenDocument.StyleManager.readDocumentStyleSheets(this);
    
    function _buildDefault(object) {
        let style = new OpenDocument.Style(object.type, object.name);
        if (object.parent) {
            style.setParent(object.parent);
        }
        for(let propertyName in object.properties) {
            let stylePropertyDef = OpenDocument.Style.getStylePropertyDef(object.type, propertyName);
            if (stylePropertyDef) {
                style.putStyleProperty(stylePropertyDef, object.properties[propertyName]);
            }
        }
        return style;
    }
};

OpenDocument.StyleManager.prototype.putStyle = function (mapName, style, styleKey) {
    if (!styleKey) {
        styleKey = style.styleName;
    }
    var map = this.maps.get(mapName);
    if (map.has(styleKey)) {
        let existingStyle = map.get(styleKey);
        existingStyle.copyProperties(style);
    } else {
        map.set(styleKey, style);
    }
};

OpenDocument.StyleManager.prototype.hasStyle = function (mapName, styleKey) {
    var map = this.maps.get(mapName);
    if (map) {
        return map.has(styleKey);
    } else {
        return false;
    }
};


OpenDocument.StyleManager.prototype.getStyle = function (mapName, styleKey) {
    var map = this.maps.get(mapName);
    if (map) {
        return map.get(styleKey);
    } else {
        return null;
    }
    return this.map.get(styleKey);
};

OpenDocument.StyleManager.prototype.getAutomaticCellStyleName = function (type, parentStyleName, cellOption) {
    var styleKey = _buildStyleKey();
    if (this.hasStyle("cell-automatic", styleKey)) {
        return this.getStyle("cell-automatic", styleKey).styleName;
    }
    var automaticCellStyleName = this.getNewAutomaticeCellStyleName();
    var dataStyleName = null;
    switch(type) {
        case "date":
            dataStyleName = this.getMatchingDateDataStyleName(cellOption);
            break;
        case "currency":
            dataStyleName = this.getMatchingCurrencyDataStyleName(cellOption);
            break;
    }
    var cellStyle = new OpenDocument.Style("cell", automaticCellStyleName);
    cellStyle.setParent(parentStyleName);
    if (dataStyleName) {
        cellStyle.setDataStyle(dataStyleName);
    }
    this.putStyle("cell-automatic", cellStyle, styleKey);
    return automaticCellStyleName;
    
    function _buildStyleKey() {
        let key = type + ":";
        if (cellOption) {
            key += cellOption + ":";
        }
        key += parentStyleName;
        return key;
    }
};

OpenDocument.StyleManager.prototype.getNewAutomaticeCellStyleName = function () {
    var name = OpenDocument.CELLSTYLE_PREFIX + this.automaticCellStyleNumber;
    this.automaticCellStyleNumber++;
    return name;
};

OpenDocument.StyleManager.prototype.getMatchingCurrencyDataStyleName = function (currencyCode) {
    if (this.currencyDataStyleMap.has(currencyCode)) {
        return this.currencyDataStyleMap.get(currencyCode);
    } else {
        let dataStyleName = OpenDocument.DATASTYLE_PREFIX + this.dataStyleNumber;
        this.dataStyleNumber++;
        this.currencyDataStyleMap.set(currencyCode, dataStyleName);
        return dataStyleName;
    }
};

OpenDocument.StyleManager.prototype.getMatchingDateDataStyleName = function (datePattern) {
    if (this.dateDataStyleMap.has(datePattern)) {
        return this.dateDataStyleMap.get(datePattern);
    } else {
        let dataStyleName = OpenDocument.DATASTYLE_PREFIX + this.dataStyleNumber;
        this.dataStyleNumber++;
        this.dateDataStyleMap.set(datePattern, dataStyleName);
        return dataStyleName;
    }
};

OpenDocument.StyleManager.prototype.getMatchingStyleName = function (type, element) {
    var styleMatcher = this.matcherMap.get(type);
    if (styleMatcher) {
        return styleMatcher.getMatchingStyle(element);
    } else {
        return "";
    }
};
/**
 * 
 * @param {OpenDocument.XmlWriter} xmlWriter
 * @returns {undefined}
 */
OpenDocument.StyleManager.prototype.writeStyles = function (mapName, xmlWriter) {
    var map = this.maps.get(mapName);
    if (map) {
        for(let style of map.values()) {
            style.write(xmlWriter);
        }
    }
};

OpenDocument.StyleManager.prototype.writeDataStyles = function (xmlWriter) {
    for(let entry of this.dateDataStyleMap) {
        xmlWriter
                .addDateStyle(entry[0], entry[1]);
    }
    for(let entry of this.currencyDataStyleMap) {
        xmlWriter
                .addCurrencyStyle(entry[0], entry[1]);
    }
};

OpenDocument.StyleManager.readDocumentStyleSheets = function (styleManager) {
    var styleSheetList = document.styleSheets;
    for(let i = 0, len = styleSheetList.length; i < len; i++) {
        OpenDocument.StyleManager.readStyleSheet(styleManager, styleSheetList[i]);
    }
};

OpenDocument.StyleManager.readStyleSheet = function (styleManager, styleSheet) {
    var ruleList = styleSheet.cssRules;
    for(let i = 0, len = ruleList.length;  i < len; i++) {
        let rule = ruleList[i];
        if ((rule.selectorText)  && (rule.style)) {
            let selectorArray = _parseSelectorText(rule.selectorText);
            for(let selector of selectorArray) {
                _addStyle(selector, rule.style);
            }
            
        }
    }
       
    function _parseSelectorText(selectorText) {
        let resultArray = new Array();
        for(let token of selectorText.split(',')) {
            let  result = __parseToken(token);
            if (result) {
                switch(result[0]) {
                    case "cell":
                    case "row":
                    case "column":
                        resultArray.push(result);
                        break;
                }
            }
        }
        return resultArray;
        
        
        function __parseToken(token) {
            token = token.trim();
            let idx = token.indexOf(' ');
            let followingPart = "";
            if (idx !== -1) {
                followingPart = token.substring(idx +1).trim();
                token = token.substring(0, idx);
            }
            let dotIndex = token.indexOf('.');
            if (dotIndex === -1) {
                return null;
            }
            let type = token.substring(0, dotIndex);
            let name = token.substring(dotIndex + 1);
            return [type, name, followingPart];
        }
    }
    
    function _addStyle(selector, cssStyleDeclaration) {
        let type = selector[0];
        let style = new OpenDocument.Style(type, selector[1]);
        let borderBuffer = false;
        if (type === "cell") {
            borderBuffer = new OpenDocument.StyleManager.BorderBuffer();
        }        
        for(let i = 0, len = cssStyleDeclaration.length; i < len; i++) {
            let propertyName = cssStyleDeclaration[i];
            let propertyValue = cssStyleDeclaration.getPropertyValue(propertyName);
            if (borderBuffer) {
                let borderTest = propertyName.match(/^border-([a-z]+)-([a-z]+)/);
                if (borderTest) {
                    borderBuffer.putSubproperty(borderTest[1], borderTest[2], propertyValue);
                    continue;
                }
            }
            if (propertyName === "content") {
                __parseDeclarationText(propertyValue);
            } else {
                let stylePropertyDef = OpenDocument.Style.getStylePropertyDef(type, propertyName);
                if (stylePropertyDef) {
                    style.putStyleProperty(stylePropertyDef, propertyValue);
                }
            }
        }
        if (borderBuffer) {
            borderBuffer.fillStyle(style);
        }
        _parseFollowingPart(selector[2]);
        styleManager.putStyle(type + "-named", style);
        
        function __parseDeclarationText(declarationText) {
            let start = declarationText.indexOf('"');
            if (start === -1) {
                return;
            }
            let end = declarationText.lastIndexOf('"');
            if (end === start) {
                return;
            }
            let tokens = declarationText.substring(start + 1, end).split(';');
            for(let token of tokens) {
                let paire = token.split(':');
                if (paire.length === 2) {
                    let name = paire[0].trim();
                    let value = paire[1].trim();
                    let stylePropertyDef = OpenDocument.Style.getStylePropertyDef(type, name);
                    if (stylePropertyDef) {
                        style.putStyleProperty(stylePropertyDef, value);
                    }
                }
            }
        }
        
        function _parseFollowingPart(followingPart) {
            if (!followingPart) {
                return;
            }
            let tokens = new Array();
            for(let token of followingPart.split(" ")) {
                token = token.trim();
                if (token.length > 0) {
                    tokens.push(token);
                }
            }
            let index = 0;
            let length = tokens.length - 1;
            while(index < length) {
                let operatorToken = tokens[index];
                switch(operatorToken) {
                    case '~':
                        for(let i = index +1; i <= length; i++)  {
                            __addMatchingClasses(tokens[i]);
                        }
                        index = length;
                        break;
                    case '+':
                        __setParent(tokens[index +1]);
                        index ++;
                        break;
                }
                index ++;
            }
        }
        
        function __addMatchingClasses(token) {
            let styleMatcher = styleManager.matcherMap.get(type);
            if (styleMatcher) {
                styleMatcher.putStyle(style.styleName, token)
            }
        }
        
        function __setParent(token) {
            if (!token) {
                return;
            }
            let dotIndex = token.indexOf('.');
            if (dotIndex !== -1) {
                style.setParent(token.substring(dotIndex + 1));
            }
        }
        
    }

};

OpenDocument.StyleManager.BorderBuffer = function () {
    this.map = new Map([
        ["bottom", false],
        ["left", false],
        ["right", false],
        ["top", false]
    ]);
};

OpenDocument.StyleManager.BorderBuffer.prototype.putSubproperty = function (position, subproperty, value) {
    if (!this.map.has(position)) {
        return;
    }
    let positionObject = this.map.get(position);
    if (!positionObject) {
        positionObject = {
            width: "0.75pt",
            color: "rgb(0, 0, 0)",
            style: "solid"
        };
        this.map.set(position, positionObject);
    }
    if (positionObject.hasOwnProperty(subproperty)) {
        if (subproperty === "color") {
            value = OpenDocument.Style.formatColor(value);
        }
        positionObject[subproperty] = value;
    };
};

OpenDocument.StyleManager.BorderBuffer.prototype.fillStyle = function (style) {
    for(let entry of this.map) {
        let position = entry[0];
        let object = entry[1];
        if (object) {
            let stylePropertyDef = OpenDocument.Style.getStylePropertyDef("type", "border-" + position);
            if (stylePropertyDef) {
                let value = object.width + " " + object.style + " " + object.color;
                style.putStyleProperty(stylePropertyDef, value);
            }
        }
    }
};

OpenDocument.StyleManager.DEFAULT_STYLES = [
    {
        type: "cell",
        name: "Standard",
        parent: "",
        properties: {
        }
    },
    {
        type: "cell",
        name: "Bold",
        parent: "Standard",
        properties: {
            "font-weight": "bold"
        }
    },
    {
        type: "cell",
        name: "Italic",
        parent: "Standard",
        properties: {
            "font-style": "italic"
        }
    },
    {
        type: "cell",
        name: "BoldItalic",
        parent: "Standard",
        properties: {
            "font-weight": "bold",
            "font-style": "italic"
        }
    },
    {
        type: "cell",
        name: "Header",
        properties: {
            "font-weight": "bold",
            "text-align": "center",
            "vertical-align": "middle"
        }
    },
    {
        type: "row",
        name: "Standard",
        parent: "",
        properties: {
        }
    },
    {
        type: "row",
        name: "Header",
        parent: "",
        properties: {
        }
    }
];
