//////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2014-present, Egret Technology.
//  All rights reserved.
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the Egret nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
//  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//  IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA,
//  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
//  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////////////

// module eui.sys {

let STATE = "eui.State";
let ADD_ITEMS = "eui.AddItems";
let SET_PROPERTY = "eui.SetProperty";
let SET_STATEPROPERTY = "eui.SetStateProperty";
let BINDING_PROPERTIES = "eui.Binding.$bindProperties";

/**
 * @private
 * 代码生成工具基类
 */
export class CodeBase {

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        return "";
    }

    /**
     * @private
     */
    public indent:number = 0;

    /**
     * @private
     * 获取缩进字符串
     */
    public getIndent(indent?:number):string {
        if (indent === void 0)
            indent = this.indent;
        let str = "";
        for (let i = 0; i < indent; i++) {
            str += "	";
        }
        return str;
    }
}


/**
 * @private
 */
export class EXClass extends CodeBase {

    /**
     * @private
     * 构造函数代码块
     */
    public constructCode:EXCodeBlock;
    /**
     * @private
     * 类名,不包括模块名
     */
    public className:string = "";

    /**
     * @private
     * 父类类名,包括完整模块名
     */
    public superClass:string = "";

    /**
     * @private
     * 内部类区块
     */
    private innerClassBlock:EXClass[] = [];

    /**
     * @private
     * 添加一个内部类
     */
    public addInnerClass(clazz:EXClass):void {
        if (this.innerClassBlock.indexOf(clazz) == -1) {
            this.innerClassBlock.push(clazz);
        }
    }

    /**
     * @private
     * 变量定义区块
     */
    private variableBlock:EXVariable[] = [];

    /**
     * @private
     * 添加变量
     */
    public addVariable(variableItem:EXVariable):void {
        if (this.variableBlock.indexOf(variableItem) == -1) {
            this.variableBlock.push(variableItem);
        }
    }

    /**
     * @private
     * 根据变量名获取变量定义
     */
    public getVariableByName(name:string):EXVariable {
        let list = this.variableBlock;
        let length = list.length;
        for (let i = 0; i < length; i++) {
            let item = list[i];
            if (item.name == name) {
                return item;
            }
        }
        return null;
    }

    /**
     * @private
     * 函数定义区块
     */
    private functionBlock:EXFunction[] = [];

    /**
     * @private
     * 添加函数
     */
    public addFunction(functionItem:EXFunction):void {
        if (this.functionBlock.indexOf(functionItem) == -1) {
            this.functionBlock.push(functionItem);
        }
    }

    /**
     * @private
     * 根据函数名返回函数定义块
     */
    public getFuncByName(name:string):EXFunction {
        let list = this.functionBlock;
        let length = list.length;
        for (let i = 0; i < length; i++) {
            let item = list[i];
            if (item.name == name) {
                return item;
            }
        }
        return null;
    }

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {

        let indent = this.indent;
        let indentStr = this.getIndent(indent);
        let indent1Str = this.getIndent(indent + 1);
        let indent2Str = this.getIndent(indent + 2);

        //打印类起始块
        let returnStr = indentStr + "(function (";
        if (this.superClass) {
            returnStr += "_super) {\n" + indent1Str + "__extends(" + this.className + ", _super);\n";
        }
        else {
            returnStr += ") {\n";
        }

        //打印内部类列表
        let innerClasses = this.innerClassBlock;
        let length = innerClasses.length;
        for (let i = 0; i < length; i++) {
            let clazz = innerClasses[i];
            clazz.indent = indent+1;
            returnStr += indent1Str+"var "+clazz.className+" = "+clazz.toCode()+"\n\n";
        }

        returnStr += indent1Str + "function " + this.className + "() {\n";
        if (this.superClass) {
            returnStr += indent2Str + "_super.call(this);\n";
        }

        //打印变量列表
        let variables = this.variableBlock;
        length = variables.length;
        for (let i = 0; i < length; i++) {
            let variable = variables[i];
            if (variable.defaultValue) {
                returnStr += indent2Str + variable.toCode() + "\n";
            }
        }

        //打印构造函数
        if (this.constructCode) {
            let codes = this.constructCode.toCode().split("\n");
            length = codes.length;
            for (let i = 0; i < length; i++) {
                let code = codes[i];
                returnStr += indent2Str + code + "\n";
            }
        }
        returnStr += indent1Str + "}\n";
        returnStr += indent1Str + "var _proto = " + this.className + ".prototype;\n\n";

        //打印函数列表
        let functions = this.functionBlock;
        length = functions.length;
        for (let i = 0; i < length; i++) {
            let functionItem = functions[i];
            functionItem.indent = indent + 1;
            returnStr += functionItem.toCode() + "\n";
        }

        //打印类结尾
        returnStr += indent1Str + "return " + this.className + ";\n" + indentStr;
        if (this.superClass) {
            returnStr += "})(" + this.superClass + ");";
        }
        else {
            returnStr += "})();";
        }
        return returnStr;
    }

}

/**
 * @private
 */
export class EXCodeBlock extends CodeBase {

    /**
     * @private
     * 添加变量声明语句
     * @param name 变量名
     * @param value 变量初始值
     */
    public addVar(name:string, value?:string):void {
        let valueStr = value ? " = " + value : "";
        this.addCodeLine("var " + name + valueStr + ";");
    }

    /**
     * @private
     * 添加赋值语句
     * @param target 要赋值的目标
     * @param value 值
     * @param prop 目标的属性(用“.”访问)，不填则是对目标赋值
     */
    public addAssignment(target:string, value:string, prop?:string):void {
        let propStr = prop ? "." + prop : "";
        this.addCodeLine(target + propStr + " = " + value + ";");
    }

    /**
     * @private
     * 添加返回值语句
     */
    public addReturn(data:string):void {
        this.addCodeLine("return " + data + ";");
    }

    /**
     * @private
     * 添加一条空行
     */
    public addEmptyLine():void {
        this.addCodeLine("");
    }

    /**
     * @private
     * 开始添加if语句块,自动调用startBlock();
     */
    public startIf(expression:string):void {
        this.addCodeLine("if(" + expression + ")");
        this.startBlock();
    }

    /**
     * @private
     * 开始else语句块,自动调用startBlock();
     */
    public startElse():void {
        this.addCodeLine("else");
        this.startBlock();
    }

    /**
     * @private
     * 开始else if语句块,自动调用startBlock();
     */
    public startElseIf(expression:string):void {
        this.addCodeLine("else if(" + expression + ")");
        this.startBlock();
    }

    /**
     * @private
     * 添加一个左大括号，开始新的语句块
     */
    public startBlock():void {
        this.addCodeLine("{");
        this.indent++;
    }

    /**
     * @private
     * 添加一个右大括号,结束当前的语句块
     */
    public endBlock():void {
        this.indent--;
        this.addCodeLine("}");
    }

    /**
     * @private
     * 添加执行函数语句块
     * @param functionName 要执行的函数名称
     * @param args 函数参数列表
     */
    public doFunction(functionName:string, args:string[]):void {
        let argsStr = args.join(",");
        this.addCodeLine(functionName + "(" + argsStr + ")");
    }

    /**
     * @private
     */
    private lines:string[] = [];

    /**
     * @private
     * 添加一行代码
     */
    public addCodeLine(code:string):void {
        this.lines.push(this.getIndent() + code);
    }

    /**
     * @private
     * 添加一行代码到指定行
     */
    public addCodeLineAt(code:string, index:number):void {
        this.lines.splice(index, 0, this.getIndent() + code);
    }

    /**
     * @private
     * 是否存在某行代码内容
     */
    public containsCodeLine(code:string):boolean {
        return this.lines.indexOf(code) != -1;
    }

    /**
     * @private
     * 在结尾追加另一个代码块的内容
     */
    public concat(cb:EXCodeBlock):void {
        this.lines = this.lines.concat(cb.lines);
    }

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        return this.lines.join("\n");
    }
}

/**
 * @private
 */
export class EXFunction extends CodeBase {

    /**
     * @private
     * 代码块
     */
    public codeBlock:EXCodeBlock = null;

    /**
     * @private
     */
    public isGet:boolean = false;

    /**
     * @private
     * 函数名
     */
    public name:string = "";

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        let indentStr:string = this.getIndent();
        let indent1Str:string = this.getIndent(this.indent + 1);
        let codeIndent:string;
        let returnStr = indentStr;
        if (this.isGet) {
            codeIndent = this.getIndent(this.indent + 2);
            returnStr += 'Object.defineProperty(_proto, "skinParts", {\n';
            returnStr += indent1Str + "get: function () {\n";
        }
        else {
            codeIndent = indent1Str;
            returnStr += "_proto." + this.name + " = function () {\n";
        }

        if (this.codeBlock) {
            let lines = this.codeBlock.toCode().split("\n");
            let length = lines.length;
            for (let i = 0; i < length; i++) {
                let line = lines[i];
                returnStr += codeIndent + line + "\n";
            }
        }
        if (this.isGet) {
            returnStr += indent1Str + "},\n" + indent1Str + "enumerable: true,\n" +
                indent1Str + "configurable: true\n" + indentStr + "});";
        }
        else {
            returnStr += indentStr + "};";
        }

        return returnStr;
    }
}

/**
 * @private
 */
export class EXVariable extends CodeBase {

    /**
     * @private
     */
    public constructor(name:string, defaultValue?:string) {
        super();
        this.indent = 2;
        this.name = name;
        this.defaultValue = defaultValue;
    }

    /**
     * @private
     * 变量名
     */
    public name:string;
    /**
     * @private
     * 默认值
     */
    public defaultValue:string;

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        if (!this.defaultValue) {
            return "";
        }
        return "this." + this.name + " = " + this.defaultValue + ";";
    }
}


/**
 * @private
 */
export class EXState extends CodeBase {

    /**
     * @private
     */
    public constructor(name:string, stateGroups?:Array<any>) {
        super();
        this.name = name;
        if (stateGroups)
            this.stateGroups = stateGroups;
    }

    /**
     * @private
     * 视图状态名称
     */
    public name:string = "";

    /**
     * @private
     */
    public stateGroups:Array<any> = [];

    /**
     * @private
     */
    public addItems:Array<any> = [];

    /**
     * @private
     */
    public setProperty:Array<any> = [];

    /**
     * @private
     * 添加一个覆盖
     */
    public addOverride(item:CodeBase):void {
        if (item instanceof EXAddItems)
            this.addItems.push(item);
        else
            this.setProperty.push(item);
    }

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        let indentStr:string = this.getIndent(1);
        let returnStr:string = "new " + STATE + " (\"" + this.name + "\",\n" + indentStr + "[\n";
        let index:number = 0;
        let isFirst:boolean = true;
        let overrides:Array<any> = this.addItems.concat(this.setProperty);
        while (index < overrides.length) {
            if (isFirst)
                isFirst = false;
            else
                returnStr += ",\n";
            let item:CodeBase = overrides[index];
            let codes:Array<any> = item.toCode().split("\n");
            let length:number = codes.length;
            for (let i:number = 0; i < length; i++) {
                let code:string = codes[i];
                codes[i] = indentStr + indentStr + code;
            }
            returnStr += codes.join("\n");
            index++;
        }
        returnStr += "\n" + indentStr + "])";
        return returnStr;
    }
}

/**
 * @private
 */
export class EXAddItems extends CodeBase {
    /**
     * @private
     */
    public constructor(target:string, property:string, position:number, relativeTo:string) {
        super();
        this.target = target;
        this.property = property;
        this.position = position;
        this.relativeTo = relativeTo;
    }

    /**
     * @private
     * 要添加的实例
     */
    public target:string;

    /**
     * @private
     * 要添加到的属性
     */
    public property:string;

    /**
     * @private
     * 添加的位置
     */
    public position:number;

    /**
     * @private
     * 相对的显示元素
     */
    public relativeTo:string;

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        let returnStr:string = "new " + ADD_ITEMS + "(\"" + this.target + "\",\"" + this.property + "\"," + this.position + ",\"" + this.relativeTo + "\")";
        return returnStr;
    }
}

/**
 * @private
 */
export class EXSetProperty extends CodeBase {
    /**
     * @private
     */
    public constructor(target:string, name:string, value:string) {
        super();
        this.target = target;
        this.name = name;
        this.value = value;
    }

    /**
     * @private
     * 要修改的属性名
     */
    public name:string;

    /**
     * @private
     * 目标实例名
     */
    public target:string;

    /**
     * @private
     * 属性值
     */
    public value:string;

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        return "new " + SET_PROPERTY + "(\"" + this.target + "\",\"" + this.name + "\"," + this.value + ")";
    }
}
/**
 * @private
 */
export class EXSetStateProperty extends CodeBase {
    /**
     * @private
     */
    public constructor(target:string, property:string, templates:string[], chainIndex:number[]) {
        super();
        if (target) {
            target = "this." + target;
        } else {
            target = "this";
        }
        this.target = target;
        this.property = property;
        this.templates = templates;
        this.chainIndex = chainIndex;
    }
    /**
     * @private
     * 目标实例名
     */
    public target:string;
    /**
     * @private
     * 目标属性名
     */
    public property:string;

    /**
     * @private
     * 绑定的模板列表
     */
    public templates:string[];

    /**
     * @private
     * chainIndex是一个索引列表，每个索引指向templates中的一个值，该值是代表属性链。
     */
    public chainIndex:number[];

    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        let expression = this.templates.join(",");
        let chain = this.chainIndex.join(",");
        return "new " + SET_STATEPROPERTY + "(this, [" + expression + "]," + "[" + chain + "]," +
            this.target + ",\"" + this.property + "\")";

    }
}
/**
 * @private
 */
export class EXBinding extends CodeBase {

    /**
     * @private
     */
    public constructor(target:string, property:string, templates:string[], chainIndex:number[]) {
        super();
        this.target = target;
        this.property = property;
        this.templates = templates;
        this.chainIndex = chainIndex;
    }

    /**
     * @private
     * 目标实例名
     */
    public target:string;
    /**
     * @private
     * 目标属性名
     */
    public property:string;
    /**
     * @private
     * 绑定的模板列表
     */
    public templates:string[];

    /**
     * @private
     * chainIndex是一个索引列表，每个索引指向templates中的一个值，该值是代表属性链。
     */
    public chainIndex:number[];


    /**
     * @private
     *
     * @returns
     */
    public toCode():string {
        let expression = this.templates.join(",");
        let chain = this.chainIndex.join(",");
        return BINDING_PROPERTIES + "(this, [" + expression + "]," + "[" + chain + "]," +
            this.target + ",\"" + this.property + "\")";

    }
}
// }