const path = require("path")
/** 包名 */
const PACKAGE_NAME = require('../package.json').name;
const scriptTemplate = require('../package.json').scriptTemplate;
const StringUtil = require("./../src/utils/string-util")
const fs = require("fs");
let wait = ms =>  new Promise(async resolve => setTimeout(resolve, ms));

let old = cc._decorator.ccclass;
const TOOL_CACHE_KEY = '__TOOL_CACHE_KEY__';
const CACHE_KEY = '__ccclassCache__';
if (!cc._decorator.ccclass["isnew"]) {
    cc._decorator.ccclass = function (ctor, name) {
        let s = ctor[CACHE_KEY];
        ctor[TOOL_CACHE_KEY] = ctor[CACHE_KEY];
        let wu = ctor[TOOL_CACHE_KEY];
        let ret = old.call(this, ctor, name);
        return ret;
    };
}
cc._decorator.ccclass["isnew"] = true; 
 

let HeadMenu = {
    "node": "cc.Node",
    "list": "cc.ScrollView",
    "lb": "cc.Label",
    "spr": "cc.Sprite",
    "rich": "cc.RichText",
    "btn": "cc.Button",
    "bar": "cc.ProgressBar",
    "edit": "cc.EditBox",
    "video": "cc.VideoPlayer",
    "bone": "dragonBones.ArmatureDisplay",
    "spine": "sp.Skeleton"
};


let replace = '\n\
    /*自动生成*/\n\
    @property({type:{type}, displayName:""})\n\
    private {name}:{type} = null;\n\
    ';


const PattStart = '===========================自动绑定组件开始===========================';
const PattEnd   = '===========================自动绑定组件结束===========================';

const PattStart_BTN =  '===========================自动生成按钮事件开始==========================';
const PattEnd_BTN   =  '===========================自动生成按钮事件结束==========================';

//缓存全部需要绑定的节点
let caches = {};
let oldCodeMap = {}
let oldCodeFunction = {}
module.exports = {
    // 新建ts模板路径
 	getTsTemplatePath(){
        return Editor.url(`packages://${PACKAGE_NAME}/${scriptTemplate}`, 'utf8')
    }, 
 
    
    print:function(nodes){ 
        for (const key in nodes) { 
            const node = nodes[key];
            Editor.log(key,node);
        } 
    },
    
    /**
     * 等待3次检查 要检查绑定了才刷新 
     */
    async bindRef(data){ 
        await wait(1000);
        this.bindSciprt(data);
        await wait(1000);
        this.bindSciprt(data);  
        //刷新脚本
        Editor.assetdb.refresh(data.jsPath,(err, results)=>{
            if(err){
                data.event.reply(null,"刷新失败"+err);
            }else{
                data.event.reply("刷新成功",results);
            }  
        }); 
        
        Editor.Scene.callSceneScript(PACKAGE_NAME, 'add-script',function(error,obj){
			if(obj){
			  return Editor.log(obj);
			} 
			//执行回来的操作 
		}); 
   }, 

   /**
     * 绑定脚本到Node上 
     */ 
    bindSciprt(data){  
        let node = this.findNode(data.uuid)
        let comp = node.getComponent(data.jsFileName);
        if(!comp){
            try {
                comp = cc.require(data.jsFileName)
            } catch(t) {}
            // 添加组件
            if (comp){ 
                node.addComponent(data.jsFileName);
            }  
        }  
    }, 

    async createTs(node,event){  
        let self = this;
        let uuid = node.uuid;
        let file_type = ".ts";
        let jsFileName = node.name;  
        jsFileName = StringUtil.firstLowerCase(jsFileName);
        let templateTs = fs.readFileSync(this.getTsTemplatePath(),'utf-8'); 
        //获取当前节点的路径
        let obj = await this.getCurrSceneUrl();
        let url = this.getNewFileSavePath(obj.url); 
        //创建文件夹 
        this.createDir(Editor.url(url));
        templateTs = templateTs.replace(/ScriptTemplate/g, jsFileName);
        let jsPath = `${url}${jsFileName}${file_type}`;  
        //Editor.log("创建脚本位置",jsPath) 
        Editor.assetdb.createOrSave( jsPath, templateTs, ( err, results )=> 
        {    
            if(err) return event.reply(null,"创建脚本失败");
             self.bindRef({
                uuid:uuid,
                node:node,
                jsFileName:jsFileName,
                jsPath:jsPath,
                event:event
            }); 
        }); 
    },
     
    /**
     * 添加脚本
     * @param {*} event 
     * @param {{ uuids: string[], name: string }} data 数据
     */
    'add-script':async function(event) {
        oldCodeMap = {}
        let obj = await this.getCurrSceneUrl(); 
        if(!obj) return event.reply("当前没有选中任何节点"); 
        let self = this;
		
        // 发消息给主进程进行关键词匹配
        let file_type 	= localStorage.getItem("newFileType")|| ".ts"
        //当前选中的Node
		let head_node   = Editor.Selection.curSelection('node')[0];
        //当前选中Node之下的全部节点 
		let nodes  = this.findNode(head_node);  
        //只挂载脚本 
        let jsFile = this.isHasJsFile(nodes);
        if (jsFile)
        {  
            let node_uids = Editor.Selection.curSelection("node");
            //Editor.log(node_uids, node_uids.length); 
            for (let i = 0; i < node_uids.length; i++) {
                let id = node_uids[i]; 
                Editor.Scene.callSceneScript(PACKAGE_NAME, "auto_code_binder", id);
            }  
        }else   
        {   
            //创建挂载脚本
            this.createTs(nodes,event); 
           
        }  
    },

    /**
     * 生成代码属性
     * 
     * 属性 写入文件
     */
    "auto_code_binder":function(event,uuid){   
        caches = {};
        let node = cc.engine.getInstanceById(uuid);
        if (!node) {
            Editor.log("没有找到需要的节点");
            return;
        } 
         //开始整个文件查找节点
        this.findChildren(node);
        //获取组件脚本绑定
        let coms = node.getComponents(cc.Component);
        if(coms && coms.length <= 0){
            Editor.warn("该节点下没有脚本组件");
            return;
        } 
        for (let com of coms) {
            let name = cc.js.getClassName(com); 
            let btns  = {};
            //排除系统组件
            if (!name.startsWith("cc.") && !name.startsWith("sp.")) { 
               // Editor.warn("自定义脚本",name);
                let comUuid = com.__scriptUuid;
                let path = Editor.remote.assetdb.uuidToFspath(comUuid);
                let tsScriptData = fs.readFileSync(path, "utf-8");
                let patt = new RegExp(`${PattStart}[\\s\\S]*${PattEnd}\*`, "gm");
                let res = tsScriptData.match(patt); 
                let data = res === null || res === void 0 ? void 0 : res.toString();  

               // Editor.warn("代码内容",data);
                //结束
                if (!res) {
                    Editor.log("=====脚本中没有发现替代注释  请在需要添加组件的地方复制粘贴以下注释======");
                    Editor.log(`/*${PattStart}*/`);
                    Editor.log(`/*${PattEnd}*/`);
                    return;
                }
                this.parseText(data);
                let str = `${PattStart}*/`;
                let seq = 0;
                let funcSeq = 0;
                for (const key in caches) { 
                    const node = caches[key]; 
                    if(node){
                       let n = this.getHeadString(key); 
                       let type = HeadMenu[n]; 
                       if(type == "cc.Button"){
                            btns[key] = key; 
                       }
					   if(!type){
						   Editor.warn("发现不知道的类型",key);
						   continue;
					   }  
                       let old = oldCodeMap[key];  
                       if (old) { 
                            if(seq == 0){str += "\n"}
                            str +=  "\t" + old.content + "\n";
                            seq +=1;
                       }else{
                            let txmp = replace;
                            if (type) { 
                                txmp = txmp.replace(new RegExp(/({type})/g,"gm"),type) 
                                txmp = txmp.replace("{name}", key);
                            } 
                            str += txmp;   
                       }   
                    }
                }
                str += "\n\t/*" + PattEnd + ""; 
                tsScriptData = tsScriptData.replace(res.toString(), str); 

                
                //生成按钮方法属性 
                let patt_btn = new RegExp(`${PattStart_BTN}[\\s\\S]*${PattEnd_BTN}\*`, "gm");
                let result = tsScriptData.match(patt_btn); 
                let btnFuncs = result === null || result === void 0 ? void 0 : result.toString(); 
                 //结束
                 if (!btnFuncs) {
                    //Editor.log("=====脚本中没有发现替代注释  请在需要添加组件的地方复制粘贴以下注释======");
                    //Editor.log(`/*${PattStart}*/`);
                    //Editor.log(`/*${PattEnd}*/`);
                    return;
                }
                oldCodeFunction = {};
                this.parseFunction(btnFuncs);
                let newStr = `${PattStart_BTN}*/\n`; 
                for (const key in btns) { 
                    const name = btns[key];
                    let funcName = `on${this.getFunctionString(name)}TouchEnd`;
                    let old = oldCodeFunction[funcName]; 
                    if (old) { 
                            if(funcSeq == 0){newStr += "\n"}
                            newStr +=  "\t" + old + "\n";
                            funcSeq +=1;
                    }else{  
                        let str = `\t${funcName}(){}\n\n`
                        newStr += str ; 
                    }
                }  
                newStr += "\n\t/*" + PattEnd_BTN;
                tsScriptData = tsScriptData.replace(result.toString(), newStr); 

                fs.writeFileSync(path, tsScriptData);
                this.waitRefreshScript(comUuid,node);
            }
        } 
    }, 

    parseFunction(data){
        if (!data) {
            return;
        }  
        let reg = data.match(/on*([\s\S]*?)}/g);
        if (!reg) {
            return;
        }  
        for (let i = 0; i < (reg === null || reg === void 0 ? void 0 : reg.length); i++) {
            let str = reg[i];
            let type = str.match(/on([\s\S]*?)TouchEnd/)[0];
            if (!type) {
                continue;
            } 
            oldCodeFunction[type] = str;
        }  
    },


 
    parseText(data){   
        if (!data) {
            return;
        } 
        let reg = data.match(/\/\*自动生成\*\/([\s\S]*?);/g);
        if (!reg) {
            //Editor.log("没有匹配");
            return;
        }  
        for (let i = 0; i < (reg === null || reg === void 0 ? void 0 : reg.length); i++) {
            let str = reg[i];
            let type = str.match(/type:([\s\S]*?),/)[0];
            if (!type) {
                continue;
            }
            type = type === null || type === void 0 ? void 0 : type.replace("type:", "");
            type = type === null || type === void 0 ? void 0 : type.replace(",", "");
            type = type.trim();
            let name = str.match(/(private|public)([\s\S]*?):/)[0];
            if (!name) {
                continue;
            }
            name = name === null || name === void 0 ? void 0 : name.replace(/(private|public)/, "");
            name = name === null || name === void 0 ? void 0 : name.replace(":", "");
            name = name.trim();
            let old =  {};  
            old.content = str;
            old.name = name;
            old.type = type; 
            oldCodeMap[name] = old;
        }  
    },



    //执行刷新
    async waitRefreshScript(comUuid,node){
        let obj  = await this.getCurrSceneUrl();  
        let assetPath = Editor.remote.assetdb.uuidToUrl(comUuid); 
        Editor.assetdb.refresh(assetPath);
        Editor.assetdb.refresh(obj.url);

        //wait(2000)
        //Editor.Scene.callSceneScript(PACKAGE_NAME, "bind_Code_ByNode");
    },


    //生成属性到脚本
    "generate_attribute":function(){
       
        let head_node = Editor.Selection.curSelection('node')[0];
        if(!head_node) return  Editor.log("没有选中节点");  
        //当前选中Node之下的全部节点 
		let nodes  = this.findNode(head_node);  
        //只挂载脚本 
        let jsFile = this.isHasJsFile(nodes);
        if (jsFile)
        { 
            //Editor.log("当前有脚本",jsFile.__classname__)
            let node_uids = Editor.Selection.curSelection("node");
            Editor.log(node_uids, node_uids.length); 
            for (let i = 0; i < node_uids.length; i++) {
                let id = node_uids[i]; 
                Editor.Scene.callSceneScript(PACKAGE_NAME, "auto_code_binder", id);
            }  
        }else   
        {   
            //创建挂载脚本
            Editor.log("当前节点没有挂载节点") 
        }
    },

    /**
     * 绑定节点到代码
     */
    "bind_Code_ByNode":async function(event){  
        let uuid = Editor.Selection.curSelection('node'); 
        let node = cc.engine.getInstanceById(uuid);   
        caches = {}; 
        if (!node) {
            Editor.log("没有找到需要的节点");
            return;
        }  
         //开始整个文件查找节点
        this.findChildren(node); 
        this.bindCodeByNode();
    },

    //开始关联
    bindCodeByNode(){
        let uuid = Editor.Selection.curSelection('node'); 
        let node = cc.engine.getInstanceById(uuid); 
        if(!node)return  Editor.warn("====请选择一个节========"); ;
        //记录绑定信息 
        let coms = node.getComponents(cc.Component);  
        for (let com of coms) {
            let name = cc.js.getClassName(com);  
            //过滤
            if (name.startsWith("cc.") || name.startsWith("sp.")) { continue }; 
            let comUuid = com.__scriptUuid; 
            let cache = com.constructor[TOOL_CACHE_KEY];  
            if(!cache){ 
                return;
            } 
            for (let key in com) { 
                if (caches[key]) {  
                    if(key == "constructor") continue;
					let data = cache.proto.properties[key];  
					let children = caches[key];  
					if (children) { 
						let nodes = children.filter(node => {
							if (data.type == cc.Node) {
								return node;
							} 
							return node.getComponent(data.type);
						});
						if (nodes && nodes.length) { 
							let first = nodes[0];
							let needCom = null;
							if (data.type == cc.Node) {
								needCom = first;
							} else {
								needCom = first.getComponent(data.type);
							}
							com[key] = needCom; 

                            //按钮统一格式
                            if(cc.Button == data.type){  
                                let clickEvents = needCom.clickEvents; 
                                //查找是否已经绑定过了
                                let funcName = `on${this.getFunctionString(key)}TouchEnd` 
                                let isBind = false;

                                //查找是否绑定了规定函数
                                for (let index = 0; index < clickEvents.length; index++) {
                                    const clickEvent = clickEvents[index]; 
                                    if(clickEvent.handler == funcName){
                                        isBind = true;
                                    } 
                                } 
                                //绑定自动生成的函数
                                if(!isBind){
                                    var clickEventHandler = new cc.Component.EventHandler();
                                    //这个 node 节点是你的事件处理代码组件所属的节点
                                    clickEventHandler.target = node;
                                    clickEventHandler.component = name;//这个是代码文件名
                                    clickEventHandler.handler = funcName;  //回调函数  
                                    needCom.clickEvents.push(clickEventHandler); 
                                }
                                //设置默认的按钮样式
                               // needCom
                               needCom.transition = cc.Button.Transition.SCALE;
                               needCom.duration = 0.1;
                               needCom.zoomScale = 1.2; 
                            }
							continue;
						} 
					} 
                }
            }   
            let assetPath = Editor.remote.assetdb.uuidToUrl(comUuid); 
            Editor.assetdb.refresh(assetPath);
            //Editor.Ipc.sendToMain('assets:open-text-file', comUuid);
        } 
        
        Editor.log("=======绑定结束========");
    },
 
    getHeadString:function(str, fortmat = "_") {
        if (!str) {
            return str;
        }
        let res = str.split(fortmat);
        return res[1];
    },


    getFunctionString:function(str, fortmat = "_") {
        if (!str) {
            return str;
        }
        let res = str.split(fortmat);
        let name = res[0]
        if(name.indexOf("$") > -1){
           name  = name.substring(name.indexOf("$")+1,name.length);
           return StringUtil.firstUpperCase(name);
        } 
        return StringUtil.firstUpperCase(name);
    },
 
    findChildren:function(node) {
        let children = node.children; 
        for (let i = 0; i < children.length; i++) {
            let child = children[i]; 
            if(child.name.indexOf("@") > -1) continue; 
            let idx = child.name.indexOf("$"); 
            if(idx > -1){
                if(caches[child.name] == null){
                    caches[child.name] = [];
                }  
                caches[child.name].push(child);
            }
            if(child.childrenCount > 0){
                this.findChildren(child)
            } 
        }
    },  
  
    dump(obj){
        let str = ""
        let i = 0
        for(let name in obj) {
            try{ 
                str += name + ": " + obj[name] + "\n"
            }catch(err){
                str += name + ": " + typeof obj[name] + "\n"
            }
            i ++;
            if (i>100){
                str+="...more"
                break;
            }
        }
        Editor.log("dump:"+obj,str)
    },
 
    // 保存新建脚本路径
 	getNewFileSavePath(node_url){ 
        // 如果是预制体  脚本就生成到Sciprt下
        if(node_url.lastIndexOf(".prefab") > -1){
            let str = node_url.substr(0,node_url.lastIndexOf("/"))
            return str.replace("prefab","Script") + "/";
        }else{ 
           return node_url.substr(0,node_url.lastIndexOf("/")) + '/'; //保存到预制节点的同级目录
        }
        // return node_url.substr(0,node_url.lastIndexOf("/")) + '/'; 
    },

    // 创建目录,绝对路径
	createDir(dirPath){
		if ( fs.existsSync(dirPath) ) return;
		let paths = dirPath.split(path.sep);//分割路径
		let path_ = "";
		for (let n = 0; n < paths.length; n++) {
			path_ += paths[n] + path.sep;
			if(!fs.existsSync(path_)){
				fs.mkdirSync(path_);
			}
		}
	}, 

    // 获得当前打开的场景文件路径 	
    async getCurrSceneUrl(callFunc){
        return new Promise((resolve)=>{
            let obj = {};
            let scene = cc.director.getScene();
            if(scene){  
                let url = Editor.remote.assetdb.uuidToUrl(scene.uuid);  
                if (url){
                    obj.url = url
                    obj.isScene = true
                    obj.uuid = scene.uuid;
                    return resolve(obj);
                }else{
                    // 当前打开的预制节点路径
                Editor.Ipc.sendToMain(`${PACKAGE_NAME}:getPrefabUuid`,{}, function (error, answer) 
                {
                    if (answer != null){
                        obj.url = Editor.remote.assetdb.uuidToUrl(answer)
                        obj.isScene = false
                        obj.uuid = answer;
                        return resolve(obj); 
                    }else{     
                        resolve(null);
                    }
                });  
                }
            }else{ 
                // 当前打开的预制节点路径
                Editor.Ipc.sendToMain(`${PACKAGE_NAME}:getPrefabUuid`,{}, function (error, answer) 
                {
                    if (answer != null){
                        obj.url = Editor.remote.assetdb.uuidToUrl(answer)
                        obj.isScene = false
                        obj.uuid = answer;
                        return resolve(obj); 
                    }else{
                        resolve(null);
                    }
                });  
            }  
        }) 
    },


    // 检测场景是否存在该子节点并返回相关信息
    findNode(select_uuid)
    {
        var canvas  = cc.director.getScene();
        var ret_node 
        if (canvas && select_uuid) {
            this.getNodeChildren(canvas,(node)=>{
                if (node.uuid == select_uuid){
                    ret_node = node;
                    return ret_node;
                }
            })
        }
        return ret_node;
    },

    // 遍历所有深层子节点
    getNodeChildren(node,callFunc)
    {
        if (!node) return;
        let nodes = node.getChildren();
        nodes.forEach((v)=>{
            v._path_str = (node._path_str || node.name)+"/" + v.name;
            this.getNodeChildren(v,callFunc)
        });
        callFunc(node)
    },

    getNodeReChildren(node,callFunc)
    {
        if (!node) return;
        let nodes = node.getChildren();
        callFunc(node)
        nodes.forEach((v)=>{
            v._path_str = (node._path_str || node.name)+"/" + v.name;
            this.getNodeReChildren(v,callFunc)
        });
    },


    isHasJsFile(node){
        if(!node) {return false};
        return this.getJsFileList(node)[0];
    },

    getJsFileList(node){
        if(!node) {return []};
        let list = [];		
        node.getComponents(cc.Component).forEach((v)=>{
            if(v.__classname__ && v.__classname__.indexOf(".") == -1) list.push(v);       //js脚本
        });
        return list;


    }, 
}

