import FairyGUI = CS.FairyGUI;
import FairyEditor = CS.FairyEditor;
import System = CS.System;
import CodeWriter, { ICodeWriterConfig } from './CodeWriter';
import genSetting, { ComponentData, MemberData } from './GenCodeSettings'
import languageSetting, { LanguageComponentChildData } from './LanguageSettings'

const File = System.IO.File;
const Directory = System.IO.Directory;
const App = FairyEditor.App;

var allSuperClassNameByUrl = {}
var allCustomNameByUrl = {}

var namespaceName = "";
var setNamespaceName = false
var exportCodePath = ""
var existScriptPaths = {};
var packageName = ''

//获取所有的文件的路径
function getAllDirector(path: string, list: Array<string>) {
    let dirs = Directory.GetDirectories(path)
    for (let index = 0; index < dirs.Length; index++) {
        let dir = dirs.get_Item(index);
        getAllFile(dir, list)
        let dirs2 = Directory.GetDirectories(dir)
        if (dirs2.Length > 0) {
            getAllDirector(dir, list)
        }
    }
}

function getAllFile(dir: string, list: Array<string>) {
    let files = Directory.GetFiles(dir, "*.cs")
    for (let f = 0; f < files.Length; f++) {
        list.push(files.get_Item(f))
    }
}

class WhootCodeWriterConfig implements ICodeWriterConfig {
    blockStart?: string;
    blockEnd?: string;
    blockFromNewLine?: boolean;
    usingTabs?: boolean;
    endOfLine?: string;
    fileMark?: string;
}


function genCSCode(handler: FairyEditor.PublishHandler) {
    console.log("gencode==========--------------========")

    let settings = (<FairyEditor.GlobalPublishSettings>handler.project.GetSettings("Publish")).codeGeneration;
    let codePkgName = handler.ToFilename(handler.pkg.name);
    exportCodePath = handler.exportCodePath // + '/' + codePkgName;
    console.log("exportCodePath=", exportCodePath)

    let pkgId = handler.pkg.id;
    packageName = handler.pkg.name
    existScriptPaths = {};

    let list: Array<string> = []
    getAllFile(exportCodePath, list)
    //开始前，先遍历整个生成目录。找寻所有文件。用以判断生成的文件存放地址
    getAllDirector(exportCodePath, list);

    for (let index = 0; index < list.length; index++) {
        let path = list[index];

        var newPath = path.replace(/\\/g, '/');
        let arrs = newPath.split('/')
        let key = arrs[arrs.length - 1]
        existScriptPaths[key] = path
    }


    namespaceName = "";
    setNamespaceName = false
    if (settings.packageName) {
        namespaceName = settings.packageName
        setNamespaceName = true
    }
    let classes = handler.CollectClasses(settings.ignoreNoname, settings.ignoreNoname, null);
    let getMemberByName = true;//默认使用名称获取对象//settings.getMemberByName;
    console.log(codePkgName, namespaceName, getMemberByName)
    let classCnt = classes.Count;

    allCustomNameByUrl = {}
    allSuperClassNameByUrl = {}
    let dependPackages: Array<string> = []

    //因为没有文档，同时返回内容限制，先保存子类基类关系。用于后续判断是否使用自定义类
    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        let url = classInfo.res.GetURL();
        allSuperClassNameByUrl[url] = classInfo.superClassName
        let config: ComponentData = genSetting.getComponentSetting(url)
        if (config != null) {
            if (config.isCustomName) {
                allCustomNameByUrl[url] = settings.classNamePrefix + config.customName
            }
        }

        //获取依赖的包
        for (let index = 0; index < classInfo.members.Count; index++) {
            let element = classInfo.members.get_Item(index);
            if (element.res != null && element.res.owner != null) {
                let pname = element.res.owner.name;
                if (dependPackages.indexOf(pname) < 0 && pname != codePkgName) {
                    dependPackages.push(pname)
                }
            }
        }
    }


    let componentClassNameArr: Array<string> = []

    for (let i: number = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        let url = classInfo.res.GetURL();
        let config: ComponentData = genSetting.getComponentSetting(url)
        if (config != null) {
            console.log(config.name, config.scriptType)
            if (config.scriptType == "panel") {
                genPanelCode(classInfo, config, codePkgName, pkgId, dependPackages);
            } else if (config.scriptType == "component") {
                let ComponentClassName = genComponentCode(classInfo, config, codePkgName, pkgId)
                componentClassNameArr.push(ComponentClassName)
            }

        } else {
            console.log(classInfo.resName + "未配置导出，忽略")
        }
    }

    genBinder(codePkgName, componentClassNameArr);
    genLanguage();
}

function getScriptName(className: string, comUrl: string) {
    if (allCustomNameByUrl.hasOwnProperty(comUrl)) {
        className = allCustomNameByUrl[comUrl]
    }
    return className
}


function genPanelCode(classInfo: FairyEditor.PublishHandler.ClassInfo, config: ComponentData, codePkgName: string, pkgId: string, dependPackages?: Array<string>) {
    let codeConfig: WhootCodeWriterConfig = new WhootCodeWriterConfig();
    codeConfig.fileMark = "/**本脚本为自动生成，每次生成会覆盖！请勿手动修改，生成插件文档及项目地址：https://git.whoot.com/whoot-games/whoot.fguieditorplugin**/"

    let comUrl = classInfo.res.GetURL();
    //先生成 .Designer部分
    let writer = new CodeWriter(codeConfig);
    writer.writeln('using FairyGUI;');
    writer.writeln('using FairyGUI.Utils;');
    writer.writeln('using NBC;');
    writer.writeln('using System.Collections.Generic;');

    writer.writeln();
    let className = getScriptName(classInfo.className, comUrl)
    if (setNamespaceName) {
        writer.writeln('namespace %s', namespaceName);
        writer.startBlock();
        writer.writeln("/// <summary> %s </summary>", config.annotation);
        writer.writeln('public partial class %s', className);
        writer.startBlock();
    } else {
        writer.writeln("/// <summary> %s </summary>", config.annotation);
        writer.writeln('public partial class %s', className);
        writer.startBlock();
    }



    writer.writeln("public GObject this[string aKey] => ContentPane.GetChild(aKey);");

    let members = classInfo.members;
    let memberCnt = members.Count
    for (let j: number = 0; j < memberCnt; j++) {
        let memberInfo = members.get_Item(j);

        if (memberInfo.res != null) {

            let superClassName = getComponentUseClassName(memberInfo, comUrl)

            writer.writeln('[AutoFind(Name = "%s")]', memberInfo.name)
            //判断配置，是否需要使用生成的类
            writer.writeln('public %s %s;', superClassName, memberInfo.varName);
        } else {
            writer.writeln('[AutoFind(Name = "%s")]', memberInfo.name)
            writer.writeln('public %s %s;', memberInfo.type, memberInfo.varName);
        }
    }

    let url = `ui://${pkgId}${classInfo.resId}`

    if (dependPackages != null) {
        let str = "";
        let len = dependPackages.length - 1;
        for (let index = 0; index <= len; index++) {
            let pname = dependPackages[index];
            str += `"${pname}"`;
            if (index < len) {
                str += ",";
            }
        }
        writer.writeln(`public override string[] GetDependPackages(){ return new string[] {${str}}; }`)
        writer.writeln();
    }


    writer.writeln();

    writer.endBlock();
    if (setNamespaceName) {
        writer.endBlock();
    }

    let fileName = className + ".Designer" + '.cs'
    let savePath = exportCodePath + '/' + fileName
    if (existScriptPaths.hasOwnProperty(fileName)) {
        console.log("脚本已经有同名的，替换地址=", fileName, existScriptPaths[fileName])
        savePath = existScriptPaths[fileName]
    }

    writer.save(savePath);

    // 判断模块脚本是否存在。如果不存在，创建默认模板脚本
    let defFileName = className + '.cs'
    if (!existScriptPaths.hasOwnProperty(defFileName)) {
        let mainSavePath = exportCodePath + '/' + defFileName
        console.log("Panel的主脚本不存在，生成默认=", mainSavePath)
        writer.reset();
        codeConfig.fileMark = "// 本脚本只在不存在时会生成一次。已存在不会再次生成覆盖"
        writer = new CodeWriter(codeConfig);
        writer.writeln('using UnityEngine;');
        writer.writeln('using NBC;');

        writer.writeln();
        if (setNamespaceName) {
            writer.writeln('namespace %s', namespaceName);
            writer.startBlock();
            writer.writeln('public partial class %s : UIPanel', className);
            writer.startBlock();
        } else {
            writer.writeln('public partial class %s : UIPanel', className);
            writer.startBlock();
        }
        writer.writeln('public override string UIPackName => "%s";', codePkgName);
        writer.writeln('public override string UIResName => "%s";', classInfo.res.name);
        writer.writeln();

        writer.writeln('protected override void OnInit()');
        writer.startBlock();
        writer.writeln('base.OnInit();');
        writer.endBlock();
        writer.writeln();

        writer.writeln('protected override void OnShow()');
        writer.startBlock();
        writer.writeln('base.OnShow();');
        writer.endBlock();
        writer.writeln();

        writer.writeln('protected override void OnHide()');
        writer.startBlock();
        writer.writeln('base.OnHide();');
        writer.endBlock();

        writer.writeln();
        writer.writeln('protected override void OnDestroy()');
        writer.startBlock();
        writer.writeln('base.OnDestroy();');
        writer.endBlock();


        writer.endBlock();
        if (setNamespaceName) {
            writer.endBlock();
        }

        writer.save(mainSavePath);
    }
}

function getComponentUseClassName(memberInfo: FairyEditor.PublishHandler.MemberInfo, comUrl: string) {
    let name = memberInfo.type
    let useCustomScript = false
    if (memberInfo.res != null) {
        let comUrl = memberInfo.res.GetURL();
        //是自定义组件，改为使用原生，不适用自定义
        let superClassName = allSuperClassNameByUrl[comUrl]

        let config: ComponentData = genSetting.getComponentSetting(comUrl)
        if (config != null && config.scriptType == "component") {
            superClassName = memberInfo.res.name
            useCustomScript = true
        }

        if (superClassName == null || superClassName == undefined) {
            superClassName = memberInfo.type
        }
        name = superClassName
    }
    return name;//{ name: name, useCustomScript: useCustomScript }
}

function genComponentCode(classInfo: FairyEditor.PublishHandler.ClassInfo, config: ComponentData, codePkgName: string, pkgId: string) {
    let codeConfig: WhootCodeWriterConfig = new WhootCodeWriterConfig();
    codeConfig.fileMark = "/**本脚本为自动生成，每次生成会覆盖！请勿手动修改，生成插件文档及项目地址：https://git.whoot.com/whoot-games/whoot.fguieditorplugin**/"
    let comUrl = classInfo.res.GetURL();
    let className = getScriptName(classInfo.className, comUrl)

    let writer = new CodeWriter(codeConfig);

    writer.writeln();
    writer.writeln('using FairyGUI;');
    writer.writeln('using FairyGUI.Utils;');
    writer.writeln('using NBC;');
    writer.writeln();
    if (setNamespaceName) {
        writer.writeln('namespace %s', namespaceName);
        writer.startBlock();
        writer.writeln('public partial class %s', className);
        writer.startBlock();
    } else {
        writer.writeln('public partial class %s', className);
        writer.startBlock();
    }

    writer.writeln('public const string URL = "ui://%s%s";', pkgId, classInfo.resId);
    writer.writeln();
    //生成成员变量
    let members = classInfo.members;
    let memberCnt = members.Count
    for (let j: number = 0; j < memberCnt; j++) {
        let memberInfo = members.get_Item(j);
        if (memberInfo.res != null) {
            let superClassName = getComponentUseClassName(memberInfo, comUrl)
            writer.writeln('public %s %s;', superClassName, memberInfo.varName);
        } else {
            writer.writeln('public %s %s;', memberInfo.type, memberInfo.varName);
        }
    }
    writer.writeln();
    writer.writeln('public override void ConstructFromXML(XML xml)');
    writer.startBlock();
    writer.writeln('base.ConstructFromXML(xml);');
    writer.writeln();

    for (let j: number = 0; j < memberCnt; j++) {
        let memberInfo = members.get_Item(j);
        if (memberInfo.group == 0) {
            let superClassName = memberInfo.type
            let useExtend = false

            if (memberInfo.res != null) {
                superClassName = getComponentUseClassName(memberInfo, comUrl)
            }
            writer.writeln('%s = (%s)GetChild("%s");', memberInfo.varName, superClassName, memberInfo.name);
        }
        else if (memberInfo.group == 1) {
            writer.writeln('%s = GetController("%s");', memberInfo.varName, memberInfo.name);
        }
        else {
            writer.writeln('%s = GetTransition("%s");', memberInfo.varName, memberInfo.name);
        }
    }
    writer.writeln('OnInited();');
    writer.writeln('UILanguage.TrySetComponentLanguage(this);');
    writer.endBlock();

    writer.endBlock();
    if (setNamespaceName) {
        writer.endBlock();
    }

    let fileName = className + ".Designer" + '.cs'
    let savePath = exportCodePath + '/' + fileName
    if (existScriptPaths.hasOwnProperty(fileName)) {
        console.log("脚本已经有同名的，替换地址=", fileName, existScriptPaths[fileName])
        savePath = existScriptPaths[fileName]
    }

    writer.save(savePath);


    // 判断模块脚本是否存在。如果不存在，创建默认模板脚本
    let defFileName = className + '.cs'
    if (!existScriptPaths.hasOwnProperty(defFileName)) {
        let mainSavePath = exportCodePath + '/' + defFileName

        console.log("Component的主脚本不存在，生成默认")
        writer.reset();
        codeConfig.fileMark = "// 本脚本只在不存在时会生成一次。组件逻辑写在当前脚本内。已存在不会再次生成覆盖 "
        writer = new CodeWriter(codeConfig);
        writer.writeln('using UnityEngine;');
        writer.writeln('using FairyGUI;');
        writer.writeln('using NBC;');

        writer.writeln();
        if (setNamespaceName) {
            writer.writeln('namespace %s', namespaceName);
            writer.startBlock();
            writer.writeln('public partial class %s : %s', className, classInfo.superClassName);
            writer.startBlock();
        } else {
            writer.writeln('public partial class %s : %s', className, classInfo.superClassName);
            writer.startBlock();
        }


        writer.writeln('private void OnInited()');
        writer.startBlock();
        writer.endBlock();

        writer.endBlock();

        if (setNamespaceName) {
            writer.endBlock();
        }

        writer.save(mainSavePath);
    }

    return className
}

/**
 * 生成自定义脚本绑定关系
 * @param codePkgName 
 * @param componentClassNameArr 
 */
function genBinder(codePkgName: string, componentClassNameArr: Array<string>) {

    let binderName = codePkgName + 'Binder';


    let codeConfig: WhootCodeWriterConfig = new WhootCodeWriterConfig();
    codeConfig.fileMark = "/**注册组件绑定关系。本脚本为自动生成，每次生成会覆盖！请勿手动修改，生成插件文档及项目地址：https://git.whoot.com/whoot-games/whoot.fguieditorplugin**/"

    let writer = new CodeWriter(codeConfig);
    writer.reset();



    writer.writeln('using FairyGUI;');
    writer.writeln();
    if (setNamespaceName) {
        writer.writeln('namespace %s', namespaceName);
        writer.startBlock();
    }
    writer.writeln('public class %s', binderName);
    writer.startBlock();

    writer.writeln('public static void BindAll()');
    writer.startBlock();
    for (let i: number = 0; i < componentClassNameArr.length; i++) {
        //let classInfo = classes.get_Item(i);
        let name = componentClassNameArr[i]
        writer.writeln('UIObjectFactory.SetPackageItemExtension(%s.URL, typeof(%s));', name, name);
    }
    writer.endBlock(); //bindall

    writer.endBlock(); //class
    if (setNamespaceName) {
        writer.endBlock(); //namespace
    }

    let fileName = binderName + '.cs'
    let savePath = exportCodePath + '/' + fileName
    console.log("生成binder 1=", fileName)
    if (existScriptPaths.hasOwnProperty(fileName)) {
        savePath = existScriptPaths[fileName]
    }

    writer.save(savePath);
}

/**
 * 生成多语言
 */
function genLanguage() {
    let binderName = 'UILangeageConfig';

    let languageMap = languageSetting.getAllPackage()
    //getPackage

    let codeConfig: WhootCodeWriterConfig = new WhootCodeWriterConfig();
    codeConfig.fileMark = "/**注册组件多语言绑定。本脚本为自动生成，每次生成会覆盖！请勿手动修改，生成插件文档及项目地址：https://git.whoot.com/whoot-games/whoot.fguieditorplugin**/"

    let writer = new CodeWriter(codeConfig);
    writer.reset();

    writer.writeln('using System.Collections.Generic;');
    writer.writeln('using FairyGUI;');
    writer.writeln('using NBC;');
    writer.writeln();
    if (setNamespaceName) {
        writer.writeln('namespace %s', namespaceName);
        writer.startBlock();
    }
    writer.writeln('public class %s : UIComponentLanguagePack', binderName);
    writer.startBlock();

    writer.writeln('public UILangeageConfig()');
    writer.startBlock();
    writer.writeln('AddData();');
    writer.endBlock();


    writer.writeln('private void AddData()');
    writer.startBlock();


    let keys = languageMap.keys();

    for (let key of keys) {
        let pack = languageMap.get(key);
        let languageData = pack.components;

       
        for (let comUrl in languageData) {
            let comObj = languageData[comUrl];
            if (comObj == undefined) continue;

            writer.writeln('Add("%s", new UIComponentLanguage()', comUrl);
            writer.startBlock();
            for (let key2 in comObj) {
                var cfg = comObj[key2]
                let useable = cfg['useable']
                let languageKey = cfg["key"]
                if (useable != 1) continue;
                writer.writeln('{ "%s", "%s" },', key2, languageKey);
            }
            writer.endBlock();
            writer.writeln(');');
        }

        writer.writeln();
    }


    // for (let comUrl in languageData) {
    //     let comObj = languageData[comUrl];
    //     if (comObj != undefined) {
    //         for (let key in comObj) {
    //             var cfg = comObj[key]
    //             let useable = cfg['useable']
    //             let languageKey = cfg["key"]
    //             if (useable == 1) {
    //                 console.log("comurl:" + comUrl + "  key:" + key + " lan:" + languageKey)
    //             }
    //         }
    //     }
    // }


    writer.endBlock(); //bindall

    writer.endBlock(); //class
    if (setNamespaceName) {
        writer.endBlock(); //namespace
    }



    let fileName = binderName + '.cs'
    let savePath = exportCodePath + '/' + fileName
    console.log("生成 language=", fileName)
    if (existScriptPaths.hasOwnProperty(fileName)) {
        savePath = existScriptPaths[fileName]
    }

    writer.save(savePath);
}

export { genCSCode };