"use strict";
/**
 * @en Registration method for the main process of Extension
 * @zh 为扩展的主进程的注册方法
 */
/**
 * // 打开panel
 * Editor.Panel.open(`${插件名}.${panel名}`);
 * // 调用普通事件
 * Editor.Message.request(插件名, 消息名, ...args);
 * // 调用场景方法
 * Editor.Message.request('scene', 'execute-scene-script', {
 *      //插件名
 *      name: string,
 *      //方法名
 *      method: string,
 *      //参数列表
 *      args: any[]
 *  });
 *
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.unload = exports.load = exports.methods = void 0;
// path.join不能正确处理'db://'结构，会把'//'变成'/'
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const utils_1 = require("./utils");
const electron = require('electron');
const adminFolderName = 'app-admin';
const controllerFolderName = 'app-controller';
const managerFolderName = 'app-manager';
const modelFolderName = 'app-model';
const soundFolderName = 'app-sound';
const viewFolderName = 'app-view';
const builtinFolderName = 'app-builtin';
const bundleFolderName = 'app-bundle';
const pkgFolderUrl = 'db://pkg/';
const pkgFolderPath = utils_1.convertUrlToPath(pkgFolderUrl);
const builtinFolderUrl = 'db://assets/' + builtinFolderName;
const builtinFolderPath = utils_1.convertUrlToPath(builtinFolderUrl);
const bundleFolderUrl = 'db://assets/' + bundleFolderName;
const bundleFolderPath = utils_1.convertUrlToPath(bundleFolderUrl);
const adminFolderUrl = builtinFolderUrl + '/' + adminFolderName;
const adminFolderPath = builtinFolderPath + '/' + adminFolderName;
const controllerFolderUrl = builtinFolderUrl + '/' + controllerFolderName;
const controllerFolderPath = builtinFolderPath + '/' + controllerFolderName;
const managerFolderUrl = builtinFolderUrl + '/' + managerFolderName;
const managerFolderPath = builtinFolderPath + '/' + managerFolderName;
const modelFolderUrl = builtinFolderUrl + '/' + modelFolderName;
const modelFolderPath = builtinFolderPath + '/' + modelFolderName;
const soundFolderUrl = bundleFolderUrl + '/' + soundFolderName;
const soundFolderPath = bundleFolderPath + '/' + soundFolderName;
const viewFolderUrl = bundleFolderUrl + '/' + viewFolderName;
const viewFolderPath = bundleFolderPath + '/' + viewFolderName;
const executorFileUrl = adminFolderUrl + '/executor.ts';
const executorFilePath = adminFolderPath + '/executor.ts';
function isExecutor(info, strict = true) {
    if (!strict) {
        if (info.path.endsWith('Controller') && info.type === 'cc.Script')
            return true;
        if (info.path.endsWith('Manager') && (info.type === 'cc.Script' || info.type === 'cc.Prefab'))
            return true;
        if ((info.name.startsWith('data.') || info.name.startsWith('config.') || info.name.startsWith('store.')) && info.type === 'cc.Script')
            return true;
        if ((info.name.startsWith('Page') || info.name.startsWith('Paper') || info.name.startsWith('Pop') || info.name.startsWith('Top'))
            && (info.type === 'cc.Script' || info.type === 'cc.Prefab' || info.type === 'cc.Scene' || info.type === 'cc.SceneAsset'))
            return true;
        if (info.type === 'cc.AudioClip')
            return true;
        return false;
    }
    if (info.path === builtinFolderUrl)
        return true;
    if (info.path === bundleFolderUrl)
        return true;
    if (info.path === managerFolderUrl)
        return true;
    if (info.path === controllerFolderUrl)
        return true;
    if (info.path === modelFolderUrl)
        return true;
    if (info.path === soundFolderUrl)
        return true;
    if (info.path === viewFolderUrl)
        return true;
    if (info.path.startsWith(controllerFolderUrl)) {
        return info.path.endsWith('Controller') && info.type === 'cc.Script';
    }
    if (info.path.startsWith(managerFolderUrl)) {
        return info.path.endsWith('Manager') && (info.type === 'cc.Script' || info.type === 'cc.Prefab');
    }
    if (info.path.startsWith(modelFolderUrl)) {
        return (info.name.startsWith('data.') || info.name.startsWith('config.') || info.name.startsWith('store.')) && info.type === 'cc.Script';
    }
    if (info.path.startsWith(viewFolderUrl)) {
        return (info.name.startsWith('Page') || info.name.startsWith('Paper') || info.name.startsWith('Pop') || info.name.startsWith('Top'))
            && (info.type === 'cc.Script' || info.type === 'cc.Prefab' || info.type === 'cc.Scene' || info.type === 'cc.SceneAsset');
    }
    if (info.path.startsWith(soundFolderUrl)) {
        return info.type === 'cc.AudioClip';
    }
}
function compareStr(str1, str2) {
    if (str1 === str2) {
        return 0;
    }
    const len = Math.max(str1.length, str2.length);
    for (let i = 0, code1 = 0, code2 = 0; i < len; i++) {
        if (str1.length <= i) {
            return -1;
        }
        else if (str2.length <= i) {
            return 1;
        }
        else {
            code1 = str1.charCodeAt(i);
            code2 = str2.charCodeAt(i);
            if (code1 > code2) {
                return 1;
            }
            else if (code1 < code2) {
                return -1;
            }
        }
    }
    return 0;
}
const viewSelect = ['Page', 'Paper', 'Pop', 'Top'];
const viewRegExp = RegExp(`^(${viewSelect.join('|')})`);
function readFileSyncByPath(url) {
    const filepath = utils_1.convertUrlToPath(url);
    return fs_1.existsSync(filepath) ? fs_1.readFileSync(filepath, 'utf8') : '';
}
function isTSDefault(value) {
    // const varname = value[0];
    const filename = value[1];
    const dirname = value[2];
    const extname = value[3];
    if (extname.endsWith('js')) {
        return false;
    }
    const filepath = path_1.default.join(utils_1.convertUrlToPath(dirname), filename + '.ts');
    const js = fs_1.readFileSync(filepath, 'utf8');
    return js.search(/export\s+default/) >= 0;
}
const keyWords = [
    'lib', 'manager', 'Manager', 'controller', 'Controller', 'data', 'config', 'store',
    'IViewName', 'IViewNames', 'IMiniViewName', 'IMiniViewNames', 'IMusicName', 'IMusicNames', 'IEffectName', 'IEffectNames',
    'ViewName', 'MiniViewName', 'MusicName', 'EffectName'
];
async function clearExecutor() {
    if (!fs_1.existsSync(executorFilePath))
        return;
    let result = '/* eslint-disable */\n' +
        'import { Component } from \'cc\';\n' +
        'import { app } from \'../../app/app\';\n' +
        'import { EDITOR,EDITOR_NOT_IN_PREVIEW } from \'cc/env\';\n\n';
    result += 'export type IReadOnly<T> = { readonly [P in keyof T]: T[P] extends Function ? T[P] : (T[P] extends Object ? IReadOnly<T[P]> : T[P]); };\n\n';
    result += 'export type IViewName = "never"\n';
    result += 'export type IViewNames = IViewName[]\n';
    result += 'export type IMiniViewName = "never"\n';
    result += 'export type IMiniViewNames = IMiniViewName[]\n';
    result += 'export type IMusicName = "never"\n';
    result += 'export type IMusicNames = IMusicName[]\n';
    result += 'export type IEffectName = "never"\n';
    result += 'export type IEffectNames = IEffectName[]\n\n';
    result += 'export type IApp = {\n';
    result += '    Controller: {},\n';
    result += '    controller: {},\n';
    result += '    Manager: {},\n';
    result += '    manager: {},\n';
    result += '    data: {},\n';
    result += '    config: {}\n';
    result += '    store: {}\n';
    result += '}\n';
    // config
    result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.config, {})\n';
    // data
    result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.data, {})\n';
    // store
    result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.store, {})\n\n';
    // controller
    result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.Controller, {})\n';
    result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.controller, {})\n\n';
    // 修正windows系统中的\为/
    result = result.replace(/\\/g, '/');
    // save
    if (readFileSyncByPath(executorFileUrl) !== result) {
        await Editor.Message.request('asset-db', 'create-asset', executorFileUrl, result, {
            overwrite: true
        });
    }
}
async function updateExecutor() {
    // app-builtin文件夹不存在, 创建
    if (!fs_1.existsSync(builtinFolderPath))
        await utils_1.createFolderByUrl(builtinFolderUrl, { readme: utils_1.getResReadme(builtinFolderName) });
    // app-admin文件夹不存在, 创建
    if (!fs_1.existsSync(adminFolderPath))
        await utils_1.createFolderByUrl(adminFolderUrl, { meta: utils_1.getResMeta(adminFolderName), readme: utils_1.getResReadme(adminFolderName) });
    const mgrList = [];
    const ctrList = [];
    const dataList = [];
    const confList = [];
    const storeList = [];
    const viewScene = {};
    const miniViewKeys = {};
    const musicKeys = {};
    const effectKeys = {};
    // app-controller app-manager app-model
    const result1 = await Editor.Message.request('asset-db', 'query-assets', { pattern: builtinFolderUrl + '/{app-controller,app-manager/*,app-model}/*.ts' })
        .then(res => {
        return res.sort((a, b) => compareStr(a.name, b.name));
    })
        .catch(() => []);
    // app-sound
    const result2 = await Editor.Message.request('asset-db', 'query-assets', { pattern: soundFolderUrl + '/{music,effect}/**/*.*' })
        .then(res => {
        return res.sort((a, b) => compareStr(a.name, b.name));
    })
        .catch(() => []);
    // app-view
    const result3 = await Editor.Message.request('asset-db', 'query-assets', { pattern: viewFolderUrl + '/{page,pop,top,paper/*}/*/native/*.{prefab,scene}' })
        .then(res => {
        return res.sort((a, b) => compareStr(a.name, b.name));
    })
        .catch(() => []);
    // manager
    const result4 = await Editor.Message.request('asset-db', 'query-assets', { pattern: 'db://app/manager/**/*.ts' })
        .then(res => {
        return res.sort((a, b) => compareStr(a.name, b.name));
    })
        .catch(() => []);
    // 集合
    const results = result1.slice().concat(result2).concat(result3).concat(result4);
    for (let index = 0; index < results.length; index++) {
        const result = results[index];
        const fileUrl = result.url;
        // 文件名.扩展名
        const basename = path_1.default.basename(result.url || '') || '';
        // 扩展名
        const extname = path_1.default.extname(result.url || '') || '';
        // 文件名
        const filename = basename.slice(0, -extname.length);
        // 文件目录名
        const dirname = path_1.default.dirname(result.url || '') || '';
        if (!basename)
            continue;
        if (!extname)
            continue;
        if (!filename)
            continue;
        if (!dirname)
            continue;
        if (extname === '.ts') {
            // 变量名
            const varname = filename.replace(/[.-]/g, '_');
            if (keyWords.indexOf(varname) >= 0) {
                console.log(`[跳过此文件] [${filename}] 原因: ${varname}与关键字中(${JSON.stringify(keyWords)})的一个重复`);
            }
            else if (fileUrl.startsWith(controllerFolderUrl)) {
                // 用户controller
                if (filename.endsWith('Controller')) {
                    ctrList.push([varname, filename, dirname, extname]);
                }
            }
            else if (fileUrl.startsWith(managerFolderUrl)) {
                // 用户manager
                if (filename.endsWith('Manager') && dirname.endsWith(utils_1.stringCaseNegate(filename.slice(0, -7)))) {
                    mgrList.push([varname, filename, dirname, extname]);
                }
            }
            else if (fileUrl.startsWith('db://app/manager/')) {
                // 系统manager(系统Mgr的文件夹命名为了美观没有那么规范，所以和用户Mgr的逻辑有区别)
                if (filename.endsWith('Manager') && dirname.endsWith(filename.slice(0, -7).toLowerCase())) {
                    mgrList.push([varname, filename, dirname, extname]);
                }
            }
            else if (fileUrl.startsWith(modelFolderUrl)) {
                // model
                if (filename.startsWith('data.')) {
                    dataList.push([varname, filename, dirname, extname]);
                }
                else if (filename.startsWith('config.')) {
                    confList.push([varname, filename, dirname, extname]);
                }
                else if (filename.startsWith('store.')) {
                    storeList.push([varname, filename, dirname, extname]);
                }
            }
        }
        else if (extname === '.prefab' || extname === '.scene') {
            if (fileUrl.startsWith(viewFolderUrl) && viewRegExp.test(filename)) {
                const dirArray = dirname.split('/');
                const index = dirArray.indexOf(viewFolderName);
                const viewDirArray = dirArray.slice(index + 1);
                if (['page', 'paper', 'pop', 'top'].indexOf(viewDirArray[0].toLowerCase()) >= 0) {
                    // 主界面
                    if (filename === `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}`) {
                        viewScene[filename] = extname === '.scene';
                    }
                    // 子界面
                    else if (filename === `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}${utils_1.stringCase(viewDirArray[2], false)}`) {
                        miniViewKeys[filename] = `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}`;
                    }
                }
                else {
                    // 主界面
                    if (filename === `${utils_1.stringCase(viewDirArray[1], false)}${utils_1.stringCase(viewDirArray[2], false)}`) {
                        viewScene[filename] = extname === '.scene';
                    }
                    // 子界面
                    else if (filename === `${utils_1.stringCase(viewDirArray[1], false)}${utils_1.stringCase(viewDirArray[2], false)}${utils_1.stringCase(viewDirArray[3], false)}`) {
                        miniViewKeys[filename] = `${utils_1.stringCase(viewDirArray[0], false)}${utils_1.stringCase(viewDirArray[1], false)}`;
                    }
                }
            }
        }
        else if (fileUrl.startsWith(soundFolderUrl)) {
            const dir = path_1.default.join(dirname.split(soundFolderName + '/').pop(), filename);
            if (dir.startsWith('music')) {
                // musicKeys
                musicKeys[dir] = dir;
            }
            else {
                // effectKeys
                effectKeys[dir] = dir;
            }
        }
    }
    // const pkgNames: string[] = [];
    // if (existsSync(pkgFolderPath)) {
    //     readdirSync(pkgFolderPath).forEach(function (item) {
    //         const item_path = path.join(pkgFolderPath, item);
    //         const item_stat = statSync(item_path);
    //         if (!item_stat.isDirectory()) return;
    //         const item_name = path.basename(item_path);
    //         if (item_name.startsWith('@')) {
    //             readdirSync(item_path).forEach(function (sub) {
    //                 const sub_path = path.join(item_path, sub);
    //                 const sub_stat = statSync(sub_path);
    //                 if (!sub_stat.isDirectory()) return;
    //                 const sub_name = path.basename(sub_path);
    //                 pkgNames.push(item_name + '/' + sub_name);
    //             });
    //         } else {
    //             pkgNames.push(item_name);
    //         }
    //     });
    // }
    let result = '/* eslint-disable */\n' +
        'import { Component,director,Director } from \'cc\';\n' +
        'import { app } from \'../../app/app\';\n' +
        'import { EDITOR,EDITOR_NOT_IN_PREVIEW } from \'cc/env\';\n\n';
    result += 'export type IReadOnly<T> = { readonly [P in keyof T]: T[P] extends Function ? T[P] : (T[P] extends Object ? IReadOnly<T[P]> : T[P]); };\n\n';
    result += `export type IViewName = ${Object.keys(viewScene).map(str => `"${str}"`).join('|') || '"never"'}\n`;
    result += 'export type IViewNames = IViewName[]\n';
    result += `export type IMiniViewName = ${Object.keys(miniViewKeys).map(str => `"${str}"`).join('|') || '"never"'}\n`;
    result += 'export type IMiniViewNames = IMiniViewName[]\n';
    result += `export type IMusicName = ${Object.keys(musicKeys).map(str => `"${str}"`).join('|') || '"never"'}\n`;
    result += 'export type IMusicNames = IMusicName[]\n';
    result += `export type IEffectName = ${Object.keys(effectKeys).map(str => `"${str}"`).join('|') || '"never"'}\n`;
    result += 'export type IEffectNames = IEffectName[]\n\n';
    // pkgNames.forEach(name => result += `import 'db://pkg/${name}'\n`);
    const writeImport = function writeImport(arr, module) {
        return arr.forEach(function (value) {
            const varname = value[0];
            const filename = value[1];
            const dirname = value[2];
            if (isTSDefault(value)) {
                result += `import ${varname} from '${path_1.default.join(path_1.default.relative(adminFolderPath, utils_1.convertUrlToPath(dirname)), filename)}'\n`;
            }
            else if (module) {
                result += `import {${varname}} from '${path_1.default.join(path_1.default.relative(adminFolderPath, utils_1.convertUrlToPath(dirname)), filename)}'\n`;
            }
            else {
                result += `import * as ${varname} from '${path_1.default.join(path_1.default.relative(adminFolderPath, utils_1.convertUrlToPath(dirname)), filename)}'\n`;
            }
        });
    };
    writeImport(confList, false);
    writeImport(dataList, false);
    writeImport(storeList, false);
    writeImport(ctrList, true);
    writeImport(mgrList, true);
    // controller
    let ctrStr = '';
    let CtrStr = '';
    ctrList.forEach(function ([varname], index, array) {
        CtrStr += `${varname.slice(0, -10)}:typeof ${varname}`;
        ctrStr += `${varname.slice(0, -10).toLowerCase()}:IReadOnly<${varname}>`;
        if (index < array.length - 1) {
            CtrStr += ',';
            ctrStr += ',';
        }
    });
    // manager
    let mgrStr = '';
    let MgrStr = '';
    mgrList.forEach(function ([varname], index, array) {
        MgrStr += `${varname.slice(0, -7)}:Omit<typeof ${varname},keyof Component>`;
        if (varname === 'UIManager') {
            mgrStr += `${varname.slice(0, -7).toLowerCase()}:Omit<${varname}<IViewName,IMiniViewName>,keyof Component>`;
        }
        else if (varname === 'SoundManager') {
            mgrStr += `${varname.slice(0, -7).toLowerCase()}:Omit<${varname}<IEffectName,IMusicName>,keyof Component>`;
        }
        else {
            mgrStr += `${varname.slice(0, -7).toLowerCase()}:Omit<${varname},keyof Component>`;
        }
        if (index < array.length - 1) {
            MgrStr += ',';
            mgrStr += ',';
        }
    });
    result += 'export type IApp = {\n';
    result += `    Controller: {${CtrStr}},\n`;
    result += `    controller: {${ctrStr}},\n`;
    result += `    Manager: {${MgrStr}},\n`;
    result += `    manager: {${mgrStr}},\n`;
    result += `    data: {${dataList.map(([varname]) => `${varname.slice(5)}:${varname}`).join(',')}},\n`;
    result += `    config: {${confList.map(([varname]) => `${varname.slice(7)}:IReadOnly<${varname}>`).join(',')}}\n`;
    result += `    store: {${storeList.map(([varname]) => `${varname.slice(6)}:IReadOnly<${varname}>`).join(',')}}\n`;
    result += '}\n\n';
    result += 'function init(){\n';
    // config
    result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.config, {${confList.map(([varname]) => `${varname.slice(7)}:new ${varname}()`).join(',')}})\n`;
    // data
    result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.data, {${dataList.map(([varname]) => `${varname.slice(5)}:new ${varname}()`).join(',')}})\n`;
    // store
    result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.store, {${storeList.map(([varname]) => `${varname.slice(6)}:new ${varname}()`).join(',')}})\n\n`;
    // controller
    result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.Controller, {${ctrList.map(([varname]) => `${varname.slice(0, -10)}:${varname}`).join(',')}})\n`;
    result += `if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) Object.assign(app.controller, {${ctrList.map(([varname]) => `${varname.slice(0, -10).toLowerCase()}:new ${varname}()`).join(',')}})\n`;
    result += '}\n';
    result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) director.on(Director.EVENT_RESET,init)\n';
    result += 'if(!EDITOR||!EDITOR_NOT_IN_PREVIEW) init()\n';
    // 修正windows系统中的\为/
    result = result.replace(/\\/g, '/');
    // save
    if (readFileSyncByPath(executorFileUrl) !== result) {
        await Editor.Message.request('asset-db', 'create-asset', executorFileUrl, result, {
            overwrite: true
        });
    }
}
let timer = null;
function callUpdateExecutor(clear = false) {
    if (timer)
        return;
    if (clear) {
        clearExecutor();
        callUpdateExecutor(false);
    }
    else {
        timer = setTimeout(() => {
            updateExecutor().finally(() => {
                timer = null;
            });
        }, 500);
    }
}
// 获得Creator主窗口
function getMainWebContents() {
    const windows = electron.BrowserWindow.getAllWindows();
    for (let i = 0; i < windows.length; i++) {
        const win = windows[i];
        if (win.webContents.getURL().includes('windows/main.html') || (win.title && win.title.includes('Cocos Creator'))) {
            return win.webContents;
        }
    }
    return;
}
function updateMark() {
    const webContents = getMainWebContents();
    if (webContents) {
        const hackCode = fs_1.readFileSync(path_1.default.join(__dirname, '../res/mark.js'), 'utf-8');
        webContents.executeJavaScript(hackCode);
    }
}
exports.methods = {
    ['open-panel']() {
        Editor.Panel.open('app.open-panel');
    },
    ['open-wiki']() {
        const url = 'https://gitee.com/cocos2d-zp/xforge/wikis/pages';
        Editor.Message.send('program', 'open-url', url);
    },
    ['open-issues']() {
        const url = 'https://gitee.com/cocos2d-zp/xforge/issues';
        Editor.Message.send('program', 'open-url', url);
    },
    ['open-github']() {
        const url = 'https://github.com/a1076559139/XForge';
        Editor.Message.send('program', 'open-url', url);
    },
    ['open-store']() {
        const url = 'https://store.cocos.com/app/search?name=xforge';
        Editor.Message.send('program', 'open-url', url);
    },
    ['refresh-executor']() {
        // 点击更新
        callUpdateExecutor();
        console.log('[executor.ts] 刷新成功');
    },
    ['scene:ready']() {
        // 
    },
    ['asset-db:ready']() {
        updateExecutor();
        updateMark();
    },
    ['asset-db:asset-add'](uuid, info) {
        if (!isExecutor(info))
            return;
        callUpdateExecutor();
    },
    ['asset-db:asset-change'](uuid, info) {
        if (!isExecutor(info, false))
            return;
        callUpdateExecutor();
    },
    ['asset-db:asset-delete'](uuid, info) {
        if (!isExecutor(info))
            return;
        callUpdateExecutor(true);
    }
};
/**
 * @en Hooks triggered after extension loading is complete
 * @zh 扩展加载完成后触发的钩子
 */
function load() {
    Editor.Message.request('asset-db', 'query-ready').then(ready => {
        if (!ready)
            return;
        updateExecutor();
    });
}
exports.load = load;
/**
 * @en Hooks triggered after extension uninstallation is complete
 * @zh 扩展卸载完成后触发的钩子
 */
function unload() { }
exports.unload = unload;
