/*
 * Copyright (c) 2014,egret.com
 * 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.com 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-LABS.ORG 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-LABS.ORG 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.
 */

/// <reference path="../types.d.ts" />

import file = require("../FileUtil");
import xml = require("../xml/index");
import CodeUtil = require("./code_util");
import create_manifest = require("./createManifest");
var properties = {};
var stylesMap = {};

class EXMLConfig{

    /**
     * Egret命名空间
     */
    public static E:string = "http://ns.egret-labs.org/egret";
    /**
     * Wing命名空间
     */
    public static W:string = "http://ns.egret-labs.org/wing";
    /**
     * 构造函数
     */
    public constructor() {
        var exmlPath: string = egret.root + "/tools/lib/exml/";
        exmlPath = exmlPath.split("\\").join("/");
        var str:string = file.read(exmlPath+"egret-manifest.xml");
        var manifest:any = xml.parse(str);
        this.parseManifest(manifest);

        str = file.read(exmlPath+"properties.json");
        properties = JSON.parse(str);
        this.findStyles(properties);
    }

    public static getInstance():EXMLConfig {

        if (!exmlConfig) {
            exmlConfig = new EXMLConfig();
        }
        return exmlConfig;
    }

    private findStyles(properties:any):void{
        var data:any = properties["styles"];
        if(!data){
            return;
        }
        for(var key in data){
            var classData:any = properties[key];
            if(!classData){
                continue;
            }
            var list:Array<string> = data[key];
            var length:number = list.length;
            for(var i:number=0;i<length;i++){
                var prop:string = list[i];
                var type:string = classData[prop];
                if(type){
                    stylesMap[prop] = type;
                }
            }
        }
    }

    private _srcPath:string;

    public get srcPath():string{
        return this._srcPath;
    }

    public set srcPath(value:string){
        this._srcPath = value;
        this.classNameToPath = create_manifest.getClassToPathInfo(this.srcPath);
        this.parseModules();
    }


    private classNameToPath:any;

    private classNameToModule:any;

    /**
     * 组件清单列表
     */
    public componentDic:any = {};

    public idMap:any = {};

    private parseModules():void{
        this.classNameToModule = {};
        for(var className in this.classNameToPath){
            var index:number = className.lastIndexOf(".");
            var ns:string = "";
            if(index!=-1){
                ns = className.substring(0,index);
                className = className.substring(index+1);
            }
            var list:Array<string> = this.classNameToModule[className];
            if(!list){
                list = this.classNameToModule[className] = [];
            }
            if(ns&&list.indexOf(ns)==-1){
                list.push(ns);
            }
        }
    }
    /**
     * 解析框架清单文件
     */
    public parseManifest(manifest:any):void{

        var children:Array<any> = manifest.children;
        var length:number = children.length;
        for(var i:number=0;i<length;i++){
            var item:any = children[i];
            var component:Component = new Component(item);
            this.componentDic[component.className] = component;
            this.idMap[component.id] = component.className;
        }
        for(var className in this.componentDic){
            var component:Component = this.componentDic[className];

            if(!component.defaultProp)
                this.findProp(component);
        }
    }
    /**
     * 递归查找默认属性
     */
    private findProp(component:Component):string{
        if(component.defaultProp)
            return component.defaultProp;
        var superComp:Component = this.componentDic[component.superClass];
        if(superComp){
            var prop:string = this.findProp(superComp);
            if(prop){
                component.defaultProp = prop;
            }
        }
        return component.defaultProp;
    }

    /**
     * @inheritDoc
     */
    public getClassNameById(id:string, ns:string):string{
        var name:string = "";
        if(this.basicTypes.indexOf(id)!=-1){
            return id;
        }
        if(ns==EXMLConfig.W){

        }
        else if(!ns||ns==EXMLConfig.E){
            name = this.idMap[id];
        }
        else{
            name = ns.substring(0,ns.length-1)+id
            if(!this.classNameToPath[name]){
                name = "";
            }
        }
        return name;
    }
    /**
     * 检查一个类名是否存在
     */
    public checkClassName(className:string):boolean{
        if(!className){
            return false;
        }
        if(this.componentDic[className]){
            return true;
        }
        if(this.classNameToPath[className]){
            return true;
        }
        return false;
    }
    /**
     * @inheritDoc
     */
    public getDefaultPropById(id:string, ns:string):string{
        var className:string = this.getClassNameById(id,ns);
        var component:Component = this.componentDic[className];
        if(!component&&className){
            component = this.findDefaultProp(className);
        }
        if(!component)
            return "";
        return component.defaultProp;
    }

    private findDefaultProp(className:string):Component{
        var classData:any = properties[className];
        if(!classData){
            var path:string = this.classNameToPath[className];
            var ext:string = file.getExtension(path).toLowerCase();
            var text:string = file.read(path);
            if(ext=="ts"){
                classData = this.getPropertiesFromTs(text,className,"");
            }
            else if(ext=="exml"){
                classData = this.getPropertiesFromExml(text);
            }
            if(classData){
                properties[className] = classData;
            }
            else{
                return null;
            }
        }
        var superClass:string = classData["super"];
        var component:Component = this.componentDic[superClass];
        if(!component){
            component = this.findDefaultProp(superClass);
        }
        return component;
    }
    /**
     * 指定的属性是否为样式属性
     */
    public isStyleProperty(prop:string,className:string):boolean
    {
        var type:String = this.findType(className,prop);
        return !type&&this.checkStyleProperty(prop,className);
    }

    private checkStyleProperty(prop:string,className:string):boolean
    {
        return (this.isInstanceOf(className,"egret.gui.IStyleClient")&&stylesMap[prop]);
    }

    /**
     * 获取指定类指定属性的类型
     */
    public getPropertyType(prop:string,className:string):string{
        if(className=="Object"){
            return "any";
        }
        var type:string = this.findType(className,prop);
        if(!type){
            if(this.checkStyleProperty(prop,className)){
                return stylesMap[prop];
            }
        }
        return type;
    }

    private findType(className:string,prop:string):string{
        var classData:any = properties[className];
        if(!classData){
            var path:string = this.classNameToPath[className];
            var ext:string = file.getExtension(path).toLowerCase();
            var text:string = file.read(path);
            if(ext=="ts"){
                text = CodeUtil.removeComment(text,path);
                classData = this.getPropertiesFromTs(text,className,"");
            }
            else if(ext=="exml"){
                classData = this.getPropertiesFromExml(text);
            }
            if(classData){
                properties[className] = classData;
            }
            else{
                return "";
            }
        }
        var type:string = classData[prop];
        if(!type){
            type = this.findType(classData["super"],prop);
        }
        return type;
    }

    /**
     * 读取一个exml文件引用的类列表
     */
    private getPropertiesFromExml(text:string):any{
        var exml:any = xml.parse(text);
        if(!exml){
            return null;
        }
        var superClass:string = this.getClassNameById(exml.localName,exml.namespace);
        if(superClass){
            var data:any = {};
            data["super"] = superClass;
            return data;
        }
        return null;
    }

    /**
     * 获取属性列表
     */
    private getPropertiesFromTs(text:string,className:string,nsPrefiex:string):any {
        index = className.lastIndexOf(".");
        var moduleName:string = "";
        if (index != -1) {
            moduleName = className.substring(0, index);
            className = className.substring(index+1);
        }
        var data:any;
        if(moduleName){
            while(text.length>0){
                var index:number = CodeUtil.getFirstVariableIndex("module",text);
                if(index==-1){
                    break;
                }
                text = text.substring(index+6);
                index = text.indexOf("{");
                if(index==-1){
                    continue;
                }
                var ns:string = text.substring(0,index).trim();
                text = text.substring(index);
                index = CodeUtil.getBracketEndIndex(text);
                if(index==-1) {
                    break;
                }
                var block:string = text.substring(0,index);
                text = text.substring(index);
                if(ns==moduleName){
                    if(nsPrefiex) {
                        ns = nsPrefiex+"."+moduleName;
                    }
                    data = this.getPropFromBlock(block,className,ns);
                    break;
                }
                else if(moduleName.indexOf(ns)==0&&moduleName.charAt(ns.length)=="."){
                    var tail:string = moduleName.substring(ns.length+1);
                    var prefix:string = moduleName.substring(0,ns.length);
                    if(nsPrefiex){
                        prefix = nsPrefiex+"."+prefix;
                    }
                    data = this.getPropertiesFromTs(block,tail+"."+className,prefix);
                    if(data) {
                        break;
                    }
                }
            }
        }
        else{
            data = this.getPropFromBlock(text,className,nsPrefiex);
        }

        return data;
    }

    private getPropFromBlock(block:string,targetClassName:string,ns:string):any{
        var data:any;
        while(block.length>0){
            var index:number = CodeUtil.getFirstVariableIndex("class",block);
            if(index==-1){
                break;
            }
            block = block.substring(index+5);
            index = block.indexOf("{");
            var preStr:string = block.substring(0,index);
            block = block.substring(index);
            var className:string = CodeUtil.getFirstVariable(preStr);
            if(className!=targetClassName){
                continue;
            }
            data = {};
            preStr = CodeUtil.removeFirstVariable(preStr,className);
            var word:string = CodeUtil.getFirstVariable(preStr);
            if(word=="extends"){
                preStr = CodeUtil.removeFirstVariable(preStr);
                word = CodeUtil.getFirstWord(preStr);
                if(word){
                    word = this.getFullClassName(word,ns);
                    data["super"] = word;
                }
                preStr = CodeUtil.removeFirstWord(preStr);
                word = CodeUtil.getFirstVariable(preStr);
            }
            if(word=="implements"){
                preStr = CodeUtil.removeFirstVariable(preStr);
                var arr:Array<any> = preStr.split(",");
                for(var i:number = arr.length-1;i>=0;i--){
                    var inter:string = arr[i];
                    inter = inter.trim();
                    if(inter){
                        inter = this.getFullClassName(inter,ns);
                        arr[i] = inter;
                    }
                    else{
                        arr.splice(i,1);
                    }
                }
                data["implements"] = arr;
            }
            index = CodeUtil.getBracketEndIndex(block);
            if(index==-1)
                break;
            var text:string = block.substring(0,index+1);
            this.readProps(text,data,ns);
            break;
        }
        return data;
    }
    /**
     * 根据类类短名，和这个类被引用的时所在的module名来获取完整类名。
     */
    private getFullClassName(word:string,ns:string):string {
        if (!ns||!word) {
            return word;
        }
        var prefix:string = "";
        var index:number = word.lastIndexOf(".");
        var nsList;
        if(index==-1){
            nsList = this.classNameToModule[word];
        }
        else{
            prefix = word.substring(0,index);
            nsList = this.classNameToModule[word.substring(index+1)];
        }
        if(!nsList){
            return word;
        }
        var length = nsList.length;
        var targetNs = "";
        for(var k=0;k<length;k++){
            var superNs = nsList[k];
            if(prefix){
                var tail:string = superNs.substring(superNs.length-prefix.length);
                if(tail==prefix){
                    superNs = superNs.substring(0,superNs.length-prefix.length-1);
                }
                else{
                    continue;
                }
            }
            if(ns.indexOf(superNs)==0){
                if(superNs.length>targetNs.length){
                    targetNs = superNs;
                }
            }
        }
        if(targetNs){
            word = targetNs+"."+word;
        }
        return word;
    }

    private basicTypes:Array<any> = ["void","any","number","string","boolean","Object","Array","Function"];

    private readProps(text:string,data:any,ns:string):void{
        var lines:Array<any> = text.split("\n");
        var length:number = lines.length;
        for(var i:number=0;i<length;i++){
            var line:string = lines[i];
            var index:number = CodeUtil.getFirstVariableIndex("public",line);
            if(index==-1)
                continue;
            line = line.substring(index+7);
            var word:string = CodeUtil.getFirstVariable(line);
            if(!word||word.charAt(0)=="_")
                continue;
            if(word=="get"){
                continue;
            }
            else if(word=="set"){
                line = CodeUtil.removeFirstVariable(line);
                word = CodeUtil.getFirstVariable(line);
                if(!word||word.charAt(0)=="_"){
                    continue;
                }
                line = CodeUtil.removeFirstVariable(line);
                line = line.trim();
                if(line.charAt(0)=="("){
                    index = line.indexOf(":");
                    if(index!=-1){
                        line = line.substring(index+1);
                        index = line.indexOf(")");
                        type = line.substring(0,index);
                        index = type.indexOf("<");
                        if(index!=-1)
                        {
                            type = type.substring(0,index);
                        }
                        type = CodeUtil.trimVariable(type);
                        if(this.basicTypes.indexOf(type)==-1){
                            type = this.getFullClassName(type,ns);
                        }
                        data[word] = type;
                    }
                    else{
                        data[word] = "any";
                    }
                }
            }
            else{
                line = CodeUtil.removeFirstVariable(line);
                line = line.trim();
                var firstChar:string = line.charAt(0);
                if(firstChar==":"){
                    var type:string = CodeUtil.getFirstWord(line.substring(1));
                    index = type.indexOf("=");
                    if(index!=-1){
                        type = type.substring(0,index);
                    }
                    index = type.indexOf("<");
                    if(index!=-1)
                    {
                        type = type.substring(0,index);
                    }
                    type = CodeUtil.trimVariable(type);
                    if(this.basicTypes.indexOf(type)==-1){
                        type = this.getFullClassName(type,ns);
                    }
                    data[word] = type;
                }
                else if(!line||firstChar==";"||firstChar=="="){
                    data[word] = "any";
                }

            }
        }
    }
    /**
     * 检查classNameA是否是classNameB的子类或classNameA实现了接口classNameB
     */
    public isInstanceOf(classNameA:string,classNameB:string):boolean{
        if(classNameB=="any"||classNameB=="Class"){
            return true;
        }
        if(classNameA==classNameB){
            return true;
        }
        var dataA:any = properties[classNameA];
        if(!dataA){
            return false;
        }
        var list:Array<string> = dataA["implements"];
        if(list){
            var length:number = list.length;
            for(var i:number=0;i<length;i++){
                if(list[i]==classNameB){
                    return true;
                }
            }
        }
        return this.isInstanceOf(dataA["super"],classNameB);
    }

}

class Component{
    /**
     * 构造函数
     */
    public constructor(item?:any){
        if(item){
            this.id = item.$id;
            this.className = item["$module"] +"."+ this.id;
            if(item["$super"])
                this.superClass = item.$super;
            if(item["$default"])
                this.defaultProp = item.$default;
        }
    }
    /**
     * 短名ID
     */
    public id:string;
    /**
     * 完整类名
     */
    public className:string;
    /**
     * 父级类名
     */
    public superClass:string = "";
    /**
     * 默认属性
     */
    public defaultProp:string = "";
}

var exmlConfig:EXMLConfig;

export = EXMLConfig;
