import * as CSS from 'css';
import * as VueParser from 'vue-parser';
import * as FS from 'fs';
import * as PATH from 'path';
import { ACUtils } from './cube-utils';
import * as VueAST from 'vue-eslint-parser';
import * as EsAST from '@babel/parser';

const pkg = require('../package.json');

export class ACPackTemplateLoader {

    async build(path: string): Promise<ACPackTemplateResult> {
        const content = FS.readFileSync(path).toString();
        const result = new ACPackTemplateResult();
        result.compilerVersion = pkg.version;

        let matched = content.match(/<template>([\s\S]*)<\/template>/);
        const templateContent = matched ? matched[0] : '';
        matched = content.match(/<script>([\s\S]*)<\/script>/);
        const scriptContent = matched ? matched[1] : undefined;

        //  解析 <script>
        if (scriptContent) {
            result.logic = scriptContent;
            const ast = EsAST.parse(scriptContent, { sourceType: 'module' });
            if (ast.program.body) {
                ast.program.body.forEach(ele => {
                    if (ele.type === 'ExportDefaultDeclaration') {
                        if (ele.declaration.type === 'ObjectExpression' && ele.declaration.properties) {
                            ele.declaration.properties.forEach((ele:any) => {
                                if (ele.key.type === 'Identifier') {
                                    if (ele.key.name === 'beforeCreate') {
                                        result.hasBeforeCreate = true;
                                    }
                                }
                            });
                        }
                    }
                });
            }
        }
        
        //  解析 <template>
        const program = VueAST.parse(templateContent, {});
        const struct = new ACParseStruct(program.templateBody, templateContent);
        struct.tag = 'body';
        result.struct = struct;

        //  解析 <style>
        let styles = new Array();
        styles = styles.concat(this.pickDependentStyles(path));
        styles.push(CSS.parse(this.trimming(VueParser.parse(content, 'style'), 'style')));
        styles.forEach(ele => {
            const astyle = new ACParseStyle(ele);
            if (result.style) {
                if (result.style.entries) {
                    result.style.entries = result.style.entries.concat(astyle.entries ? astyle.entries : []);
                } else {
                    result.style.entries = astyle.entries;
                }
            } else {
                result.style = astyle;
            }
        });
        const finals = new Array();
        if (result.style && result.style.entries) {
            result.style.entries.forEach(ele => {
                let found = -1;
                for (let index = 0; index < finals.length; index++) {
                    const element = finals[index];
                    if (ele.key === element.key) {
                        found = index;
                        break;
                    }
                }
                if (found !== -1) {
                    //  已存在 - 层叠
                    finals[found].assign(ele);
                } else {
                    //  不存在 - 追加
                    finals.push(ele);
                }
            });
        }
        if (result.style && finals.length > 0) {
            result.style.entries = finals;
        }

        return Promise.resolve(result);
    }
    private trimming(content: string, key: string): string {
        const splits = content.split('tslint:enable');
        if (splits.length > 1) {
            content = content.replace(`${splits[0]}tslint:enable`, '');
        }
        
        let cursor = -1;
        const lines = content.split('\n');
        for (let index = 0; index < lines.length; index++) {
            const element = lines[index];
            if (element.indexOf(`<${key}`) !== -1) {
                cursor = index;
                break;
            }
        }
        return lines.slice(cursor+1).join('\n');
    }
    private pickDependentStyles(path: string): Array<CSS.Stylesheet> {
        const content = FS.readFileSync(path).toString();
        const root = PATH.dirname(path);
        const depends = content.split('\n').filter(value => {
            const trimming = value.split(' ').join('');
            return trimming.indexOf('<stylesrc=') !== -1 ? true : false;
        });
        const deppaths = new Array();
        depends.forEach(value => {
            let matched = value.match(/src="([\s\S]*)"/);
            if (!matched) {
                matched = value.match(/src="([\s\S]*)"/);
            }
            if (matched) {
                deppaths.push(PATH.resolve(root, matched[1]));
            }
        });
        const styles = new Array();
        deppaths.forEach(value => {
            styles.push(CSS.parse(FS.readFileSync(value).toString()));
        });
        return styles;
    }
}

export class ACParseStruct {
    tag: string | undefined;
    text: string | undefined;
    textBound: boolean = false;
    textBoundType: ACParseItemBoundType | undefined;
    inlineStyle: ACParseEntry | undefined;
    vIf: string | undefined;
    vElse: boolean = false;
    vElseIf: string | undefined;
    vfor: string | undefined;
    vShow: string | undefined;
    attributes: ACParseEntry | undefined;
    children: Array<ACParseStruct> | undefined;
    constructor(meta?: any, content?:string) {
        if (meta) {
            this.tag = meta.rawName;    //  name字段为小写，rawName为原始字符串

            //  attribute
            const attrs = new ACParseEntry();
            attrs.entries = new Array();
            meta.startTag.attributes.forEach((ele:any) => {
                if (ele.type === 'VAttribute') {
                    if (ele.directive) {
                        //  运算类
                        if (ele.key.name.name === 'bind') {
                            const item = new ACParseItem();
                            if (ele.key.argument && ele.key.argument.type === 'VIdentifier') {
                                item.key = `${ACUtils.dash2hump(ele.key.argument.rawName).trim()}`;
                                item.bound = true;
                            }
                            if (ele.value && ele.value.type === 'VExpressionContainer' && ele.value.expression) {
                                const start = ele.value.expression.loc.start;
                                const end = ele.value.expression.loc.end;
                                item.value = ACUtils.substring(content, start.line, start.column, end.line, end.column);
                                if (item.value) {
                                    const envValue = ACUtils.pickBindEnvValue(item.value);
                                    if (envValue) {
                                        item.value = envValue;
                                        item.boundType = ACParseItemBoundType.Env;
                                    }
                                }
                            }
                            // tslint:disable-next-line:no-unused-expression
                            attrs.entries && attrs.entries.push(item);
                        } else if (ele.key.name.name === 'on') {
                            const item = new ACParseItem();
                            if (ele.key.argument && ele.key.argument.type === 'VIdentifier') {
                                item.key = `${ACUtils.dash2hump(ele.key.argument.rawName).trim()}`;
                                item.isEvent = true;
                                item.bound = true;
                            }
                            if (ele.value && ele.value.type === 'VExpressionContainer' && ele.value.expression) {
                                const start = ele.value.expression.loc.start;
                                const end = ele.value.expression.loc.end;
                                const event = ACUtils.substring(content, start.line, start.column, end.line, end.column);
                                if (event && item.key) {
                                    const params = ACUtils.pickFuncParams(event);
                                    item.value = params;

                                    //  模板二期功能：完整提取事件信息
                                    const eventInfo: any = new Object(null);
                                    eventInfo.eventName = item.key;
                                    eventInfo.funcParams = new Array();
                                    const funcInfo = EsAST.parseExpression(event);
                                    if (funcInfo.type === 'CallExpression') {
                                        if (funcInfo.callee.type === 'Identifier') {
                                            eventInfo.funcName = funcInfo.callee.name;
                                        }
                                        if (funcInfo.arguments) {
                                            funcInfo.arguments.forEach(ele => {
                                                if (ele.loc) {
                                                    const start = ele.loc.start;
                                                    const end = ele.loc.end;
                                                    eventInfo.funcParams.push(ACUtils.substring(event, start.line, start.column, end.line, end.column));
                                                }
                                            });
                                        }
                                    } else if (funcInfo.type === 'Identifier') {
                                        eventInfo.funcName = funcInfo.name;
                                    }
                                    if (eventInfo.eventName) {
                                        item.extraParam = eventInfo;
                                    }
                                }
                            }
                            // tslint:disable-next-line:no-unused-expression
                            attrs.entries && attrs.entries.push(item);
                        } else if (ele.key.name.name === 'if') {
                            let value: any = '';
                            if (content && ele.value) {
                                const start = ele.value.expression.loc.start;
                                const end = ele.value.expression.loc.end;
                                value = ACUtils.substring(content, start.line, start.column, end.line, end.column);
                            }
                            this.vIf = value;
                        } else if (ele.key.name.name === 'else-if') {
                            let value: any = '';
                            if (content && ele.value) {
                                const start = ele.value.expression.loc.start;
                                const end = ele.value.expression.loc.end;
                                value = ACUtils.substring(content, start.line, start.column, end.line, end.column);
                            }
                            this.vElseIf = value;
                        } else if (ele.key.name.name === 'else') {
                            this.vElse = true;
                        } else if (ele.key.name.name === 'show') {
                            let value: any = '';
                            if (content && ele.value) {
                                const start = ele.value.expression.loc.start;
                                const end = ele.value.expression.loc.end;
                                value = ACUtils.substring(content, start.line, start.column, end.line, end.column);
                            }
                            this.vShow = value;
                        } else if (ele.key.name.name === 'for') {
                            if (ele.value && ele.value.type === 'VExpressionContainer' && ele.value.expression) {
                                const start = ele.value.expression.loc.start;
                                const end = ele.value.expression.loc.end;
                                this.vfor = ACUtils.substring(content, start.line, start.column, end.line, end.column);
                            }
                        }
                    } else {
                        //  值传类
                        const item = new ACParseItem();
                        item.key = ACUtils.dash2hump(ele.key.rawName).trim();
                        if (ele.value) {
                            //  针对内联样式进行驼峰转换
                            if (item.key === 'style' && ele.value.value) {
                                const inlineStyle = new ACParseEntry();
                                inlineStyle.entries = new Array();
                                ele.value.value.split(';').forEach((st:any) => {
                                     const astyle = new ACParseItem();
                                     const asplit = st.split(':');
                                     astyle.key = asplit.length > 0 ? ACUtils.dash2hump(asplit[0]).trim() : undefined;
                                     astyle.value = asplit.length > 1 ? asplit[1].trim() : undefined;
                                     // tslint:disable-next-line:no-unused-expression
                                     (astyle.key && astyle.value && inlineStyle.entries) && inlineStyle.entries.push(astyle);
                                });
                                // tslint:disable-next-line:no-unused-expression
                                (inlineStyle.entries.length > 0) && (this.inlineStyle = inlineStyle);
                            } else {
                                item.value = ele.value.value;
                            }
                        }
                        // tslint:disable-next-line:no-unused-expression
                        attrs.entries && attrs.entries.push(item);
                    }
                }
            });
            if (attrs.entries.length > 0) {
                this.attributes = attrs;
            }

            //  children
            if (['template', 'div', 'scroller', 'list', 'slider'].indexOf(meta.name) !== -1) {
                //  只有容器节点支持子节点
                meta.children.forEach((ele:any) => {
                    if (ele.type === 'VElement') {
                        if (!this.children) {
                            this.children = new Array();
                        }
                        this.children.push(new ACParseStruct(ele, content));
                    }
                });
            }

            //  text
            if (meta.type === 'VElement' && meta.name === 'text') {
                //  仅处理text标签
                const plus = `^^CUBE-PLUS-FALCON^^`;
                let value = '';
                meta.children.forEach((ele:any) => {
                    if (ele.type === 'VText') {
                        //  拼接文本值
                        let encoded:string = ele.value.split('\\').join('\\\\');
                        encoded = encoded.split("'").join("\\'");
                        value = `${value}${plus}'${encoded}'`;
                    } else if (ele.type === 'VExpressionContainer' && content) {
                        //  拼接表达式
                        //  模板一期功能：提取falcon_env()数据路径，表达式内仅允许同源路径，匹配到env时认为该表达式内全部为环境数据源
                        let expression = ACUtils.substring(content, ele.loc.start.line, ele.loc.start.column, ele.loc.end.line, ele.loc.end.column);
                        if (expression) {
                            const envValue = ACUtils.pickBindEnvValue(expression);
                            if (envValue) {
                                expression = `{{${envValue.trim()}}}`;
                                this.textBoundType = ACParseItemBoundType.Env;
                            }
                        }
                        value = `${value}${expression}`;
                    }
                    //  忽略其他节点
                });
                if (value.length > 0) {
                    let result = value;
                    const convert = (v:string) => {
                        //  提取变量
                        const matched = v.match(/{{([\s\S]*?)}}/);
                        if (matched) {
                            result = v.split(matched[0]).join(`${plus}${matched[1].trim()}`);
                            this.textBound = true;
                            convert(result);
                        }
                    };
                    convert(result);
                    //  转移操作符
                    result = result.startsWith(plus) ? result.substr(plus.length) : result;
                    if (result.indexOf(plus) !== -1) {
                        result = result.split(plus).join(' + ');
                    }
                    this.text = result;
                }
            }
        }
    }

}
export class ACParseStyle {
    entries: Array<ACParseStyleEntry> | undefined;
    constructor(meta?: any) {
        if (meta.stylesheet) {
            if (!(meta.stylesheet.parsingErrors && meta.stylesheet.parsingErrors.length > 0)) {
                for (let index = 0; index < meta.stylesheet.rules.length; index++) {
                    const rule = meta.stylesheet.rules[index];
                    if (rule.type && rule.type === 'rule') {
                        if (!this.entries) {
                            this.entries = new Array();
                        }
                        this.entries.push(new ACParseStyleEntry(rule));
                    } else if (rule.type && rule.type === 'media') {
                        const media: string = rule.media ? rule.media : '';
                        if (rule.rules) {
                            for (let index = 0; index < rule.rules.length; index++) {
                                const element = rule.rules[index];
                                if (element.type && element.type === 'rule') {
                                    const entry = new ACParseStyleEntry(element);
                                    entry.key = `${entry.key}@${media}`;
                                    if (!this.entries) {
                                        this.entries = new Array();
                                    }
                                    this.entries.push(entry);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
export class ACParseStyleEntry {
    key: string | undefined;
    value: ACParseEntry | undefined;
    constructor(meta?: any) {
        if (meta) {
            if (meta.selectors) {
                this.key = meta.selectors.join(', ');
            }
            if (meta.declarations) {
                this.value = new ACParseEntry(meta.declarations);
            }
        }
    }
    assign(other: ACParseStyleEntry): void {
        if (this.value && other.value) {
            this.value.assign(other.value);
        }
    }
}
export class ACParseEntry {
    entries: Array<ACParseItem> | undefined;
    constructor(meta?: any) {
        if (meta) {
            for (let index = 0; index < meta.length; index++) {
                const element = meta[index];
                if (!this.entries) {
                    this.entries = new Array();
                }
                this.entries.push(new ACParseItem(element));
            }
        }
    }
    assign(other: ACParseEntry): void {
        if (this.entries) {
            if (other.entries) {
                this.entries = this.entries.concat(other.entries);
                const finals = new Array();
                this.entries.forEach(value => {
                    let found = -1;
                    for (let index = 0; index < finals.length; index++) {
                        const element = finals[index];
                        if (element.key === value.key) {
                            found = index;
                            break;
                        }
                    }
                    if (found !== -1) {
                        //  已存在 - 替换
                        finals[found] = value;
                    } else {
                        //  不存在 - 追加
                        finals.push(value);
                    }
                });
                this.entries = finals;
            }
        } else {
            this.entries = other.entries;
        }
    }
}
export enum ACParseItemBoundType {
    Normal = 0,
    Env = 1
}
export class ACParseItem {
    key: string | undefined;
    value: string | undefined;
    bound: boolean = false;
    boundType: ACParseItemBoundType | undefined;
    isEvent: boolean = false;
    extraParam: object | undefined;
    constructor(meta?: any) {
        if (meta) {
            if (meta.property) {
                this.key = ACUtils.dash2hump(meta.property);
            }
            if (meta.value) {
                this.value = meta.value;
            }
        }
    }
}

export class ACPackTemplateResult {
    error ?: any;
    struct ?: ACParseStruct;
    meta ?: string;
    style ?: ACParseStyle;
    logic ?: string;
    hasBeforeCreate ?: boolean;
    compilerVersion ?: string;
}