/**
 * Convert the text in html format to the object that can be assigned to the egret.TextField#textFlow property
 * @see http://edn.egret.com/cn/docs/page/146 Text mixed in a variety of style
 * @version Egret 2.4
 * @platform Web,Native
 * @includeExample egret/text/HtmlTextParser.ts
 * @language en_US
 */
/**
 * 将html格式文本转换为可赋值给 egret.TextField#textFlow 属性的对象
 * @see http://edn.egret.com/cn/docs/page/146 多种样式文本混合
 * @version Egret 2.4
 * @platform Web,Native
 * @includeExample egret/text/HtmlTextParser.ts
 * @language zh_CN
 */
export class HtmlTextParser {

	/**
	 * @version Egret 2.4
	 * @platform Web,Native
	 */
    constructor() {
        this.initReplaceArr();
        this.initPreReplaceArr();
    }

    private replaceArr: any[] = [];

    private initReplaceArr(): void {
        const arr = this.replaceArr = [];
        arr.push([/&lt;/g, "<"]);
        arr.push([/&gt;/g, ">"]);
        arr.push([/&amp;/g, "&"]);
        arr.push([/&quot;/g, "\""]);
        arr.push([/&apos;/g, "\'"]);
    }

    private preReplaceArr: any[] = [];

    private initPreReplaceArr() {
        const arr = this.preReplaceArr = [];
        arr.push([/\\\"/g, "\""]);
        arr.push([/<br>/g, "\n"]);
    }

	/**
	 * @private
	 *
	 * @param value
	 * @returns
	 */
    private replaceSpecial(value: string): string {
        // tslint:disable-next-line:prefer-for-of
        for (let i = 0; i < this.replaceArr.length; i++) {
            const k = this.replaceArr[i][0];
            const v = this.replaceArr[i][1];

            value = value.replace(k, v);
        }
        return value;
    }

	/**
	 * @private
	 */
    private resutlArr: Array<egret.ITextElement> = [];

	/**
	 * Convert the text in html format to the object that can be assigned to the egret.TextField#textFlow property
	 * @param htmltext {string} Text in html
	 * @returns {Array<egret.ITextElement>} 可赋值给 egret.TextField#textFlow Object that can be assigned to the egret.TextField#textFlow property
	 * @version Egret 2.4
	 * @platform Web,Native
	 * @language en_US
	 */
	/**
	 * 将html格式文本转换为可赋值给 egret.TextField#textFlow 属性的对象
	 * @param htmltext {string} html文本
	 * @returns {Array<egret.ITextElement>} 可赋值给 egret.TextField#textFlow 属性的对象
	 * @version Egret 2.4
	 * @platform Web,Native
	 * @language zh_CN
	 */
    public parse(htmltext: string): egret.ITextElement[] {
        this.preReplaceArr.forEach(p => {
            htmltext = htmltext.replace(p[0], p[1]);
        });

        this.stackArray = [];
        this.resutlArr = [];

        let firstIdx = 0;//文本段开始位置
        const length: number = htmltext.length;
        while (firstIdx < length) {
            const starIdx: number = htmltext.indexOf("<", firstIdx);
            if (starIdx < 0) {
                this.addToResultArr(htmltext.substring(firstIdx));
                firstIdx = length;
            }
            else {
                this.addToResultArr(htmltext.substring(firstIdx, starIdx));

                let fontEnd = htmltext.indexOf(">", starIdx);
                if (fontEnd == -1) {
                    egret.$error(1038);
                    fontEnd = starIdx;
                }
                else if (htmltext.charAt(starIdx + 1) == "\/") {//关闭
                    this.stackArray.pop();
                }
                else {
                    this.addToArray(htmltext.substring(starIdx + 1, fontEnd));
                }

                firstIdx = fontEnd + 1;
            }
        }

        return this.resutlArr;
    }

    public parser(htmltext: string): Array<egret.ITextElement> {
        return this.parse(htmltext);
    }

	/**
	 * @private
	 *
	 * @param value
	 */
    private addToResultArr(value: string): void {
        if (value == "") {
            return;
        }

        value = this.replaceSpecial(value);

        if (this.stackArray.length > 0) {
            this.resutlArr.push({ text: value, style: this.stackArray[this.stackArray.length - 1] })
        }
        else {
            this.resutlArr.push(<egret.ITextElement>{ text: value });
        }
    }

    //将字符数据转成Json数据
    private changeStringToObject(str: string): egret.ITextStyle {
        str = this.replaceSpecial(str.trim());
        const info: any = {};

        let header = [];
        if (str.charAt(0) == "i" || str.charAt(0) == "b" || str.charAt(0) == "u") {
            this.addProperty(info, str, "true");
        }
        // tslint:disable-next-line:no-conditional-assignment
        else if (header = str.match(/^(font|a)\s/)) {
            str = str.substring(header[0].length).trim();

            let next = 0;
            let titles;
            // tslint:disable-next-line:no-conditional-assignment
            while (titles = str.match(this.getHeadReg())) {
                const title = titles[0];
                let value = "";
                str = str.substring(title.length).trim();
                if (str.charAt(0) == "\"") {
                    next = str.indexOf("\"", 1);
                    value = str.substring(1, next);
                    next += 1;
                }
                else if (str.charAt(0) == "\'") {
                    next = str.indexOf("\'", 1);
                    value = str.substring(1, next);
                    next += 1;
                }
                else {
                    value = str.match(/(\S)+/)[0];
                    next = value.length;
                }

                this.addProperty(info, title.substring(0, title.length - 1).trim(), value.trim());

                str = str.substring(next).trim();
            }
        }

        return info;
    }

	/**
	 * @private
	 *
	 * @returns
	 */
    private getHeadReg(): RegExp {
        return /^(color|textcolor|strokecolor|stroke|b|bold|i|italic|u|size|fontfamily|href|target)(\s)*=/;
    }

	/**
	 * @private
	 *
	 * @param info
	 * @param head
	 * @param value
	 */
    private addProperty(info: egret.ITextStyle, head: string, value: string): void {

        switch (head.toLowerCase()) {
            case "color":
            case "textcolor":
                value = value.replace(/#/, "0x");
                info.textColor = parseInt(value);
                break;
            case "strokecolor":
                value = value.replace(/#/, "0x");
                info.strokeColor = parseInt(value);
                break;
            case "stroke":
                info.stroke = parseInt(value);
                break;
            case "b":
            case "bold":
                info.bold = value == "true";
                break;
            case "u":
                info.underline = value == "true";
                break;
            case "i":
            case "italic":
                info.italic = value == "true";
                break;
            case "size":
                info.size = parseInt(value);
                break;
            case "fontfamily":
                info.fontFamily = value;
                break;
            case "href":
                info.href = this.replaceSpecial(value);
                break;
            case "target":
                info.target = this.replaceSpecial(value);
                break;
        }
    }

	/**
	 * @private
	 */
    private stackArray: Array<egret.ITextStyle>;

	/**
	 * @private
	 *
	 * @param infoStr
	 */
    private addToArray(infoStr: string): void {
        const info: egret.ITextStyle = this.changeStringToObject(infoStr);

        if (this.stackArray.length == 0) {
            this.stackArray.push(info);
        }
        else {
            const lastInfo: Object = this.stackArray[this.stackArray.length - 1];
            for (const key in lastInfo) {
                if (info[key] == null) {
                    info[key] = lastInfo[key];
                }
            }
            this.stackArray.push(info);
        }
    }
}

const parser = new HtmlTextParser();
export const parseHtmlText = (content) => {
    return parser.parse(content);
}
