"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sync = sync;
exports.rawSync = rawSync;
const fs_1 = require("fs");
const fs_extra_1 = require("fs-extra");
const glob = __importStar(require("glob"));
const path = __importStar(require("path"));
const key_1 = require("../client/event/keyboard/key");
const removeLastSegment_1 = require("../util/removeLastSegment");
/* eslint-disable no-console */
function syncSystem(systemDir) {
    const system_specs = {};
    const system_ids_name_set = new Set();
    const system_id_to_name = {};
    const system_id_to_folder_path = {};
    glob
        .sync(`**/**/spec.json`, {
        cwd: systemDir,
    })
        .map((path) => (0, removeLastSegment_1.removeLastSegment)(path))
        .forEach((_) => {
        const spec_file_path = `${systemDir}/${_}/spec.json`;
        const spec = (0, fs_extra_1.readJSONSync)(spec_file_path);
        system_specs[spec.id] = spec;
        const { name } = spec;
        let _name = name;
        let i = 0;
        while (system_ids_name_set.has(_name)) {
            _name = name + ' ' + i;
            i++;
        }
        system_ids_name_set.add(_name);
        system_id_to_name[spec.id] = _name;
        const folder_path = `${systemDir}/${_}`;
        system_id_to_folder_path[spec.id] = folder_path;
    });
    return {
        system_specs,
        system_id_to_name,
        system_ids_name_set,
        system_id_to_folder_path,
    };
}
async function sync(systemDir, outputDir, specIdWhitelist) {
    const { specs, ids, classes, components } = rawSync(systemDir, specIdWhitelist);
    await (0, fs_extra_1.ensureDir)(outputDir);
    await Promise.all([
        (0, fs_extra_1.writeFile)(path.join(outputDir, '_ids.ts'), ids),
        (0, fs_extra_1.writeFile)(path.join(outputDir, '_classes.ts'), classes),
        (0, fs_extra_1.writeFile)(path.join(outputDir, '_components.ts'), components),
        (0, fs_extra_1.writeFile)(path.join(outputDir, '_specs.ts'), specs),
    ]);
}
function rawSync(systemDir, specIdWhitelist) {
    const { system_specs, system_id_to_name, system_ids_name_set, system_id_to_folder_path, } = syncSystem(systemDir);
    const _specs = {};
    let specs = '';
    let ids = '';
    let classes = '';
    let classes_import = '';
    let classes_export = 'export default {\n';
    let components = '';
    let components_import = '';
    let components_export = 'export default {\n';
    const class_name_set = new Set();
    const component_name_set = new Set();
    const ids_name_set = new Set(system_ids_name_set);
    const ids_visited = new Set();
    const id_to_name = { ...system_id_to_name };
    glob
        .sync(`**/**/spec.json`, {
        cwd: systemDir,
    })
        .map((path) => (0, removeLastSegment_1.removeLastSegment)(path))
        .forEach((_) => {
        const spec_file_path = `${systemDir}/${_}/spec.json`;
        const spec = (0, fs_extra_1.readJSONSync)(spec_file_path);
        const segments = _.split('/');
        const l = segments.length;
        const tags = segments.slice(0, l - 1);
        const id = spec.id;
        spec.metadata = spec.metadata || {};
        spec.metadata.tags = tags;
        if (!id) {
            console.log(`id not specified at ${spec_file_path}`);
        }
        if (specIdWhitelist && !specIdWhitelist.has(id)) {
            return;
        }
        const include = (id, spec, spec_folder_path) => {
            if (ids_visited.has(id)) {
                return;
            }
            ids_visited.add(id);
            spec.system = true;
            const { name } = spec;
            let _name = id_to_name[id];
            if (!_name) {
                let i = 0;
                while (ids_name_set.has(_name)) {
                    _name = name + ' ' + i;
                    i++;
                }
                ids_name_set.add(_name);
                id_to_name[id] = _name;
            }
            const NAME = _name
                .split('')
                .filter(key_1.isNotSymbol)
                .join('')
                .toUpperCase()
                .split(' ')
                .join('_');
            ids += `export const ID_${NAME} = '${id}'\n`;
            _specs[id] = spec;
            let name_init = segments[l - 1];
            const class_file_path = `${spec_folder_path}/index.ts`;
            const component_file_path = `${spec_folder_path}/Component.ts`;
            const relative_folder_path = spec_folder_path.slice(systemDir.length + 1);
            if ((0, fs_1.existsSync)(class_file_path)) {
                let name = name_init;
                let i = 0;
                while (class_name_set.has(name)) {
                    name = name_init + i;
                    i++;
                }
                class_name_set.add(name);
                classes_import += `import ${name} from './${relative_folder_path}'\n`;
                classes_export += `\t'${id}': ${name},\n`;
            }
            if ((0, fs_1.existsSync)(component_file_path)) {
                let name = name_init;
                let i = 0;
                while (component_name_set.has(name)) {
                    name = name_init + i;
                    i++;
                }
                component_name_set.add(name);
                components_import += `import ${name} from './${relative_folder_path}/Component'\n`;
                components_export += `\t'${id}': ${name},\n`;
            }
            for (const id of spec.deps ?? []) {
                const system_spec = system_specs[id];
                if (!system_spec) {
                    throw new Error(`Could not find dep ${id}`);
                }
                const system_folder_path = system_id_to_folder_path[system_spec.id];
                include(id, system_spec, system_folder_path);
            }
        };
        include(id, spec, `${systemDir}/${_}`);
    });
    classes_export += '}';
    components_export += '}';
    components = components_import + '\n' + components_export;
    classes = classes_import + '\n' + classes_export;
    specs = `export default JSON.parse(\`${JSON.stringify(_specs, null, 2)
        .replace(/\\/g, '\\\\')
        .replace(/`/g, '\\`')
        .replace(/\$/g, '\\$')}\`)\n`;
    return { specs, classes, components, ids };
}
//# sourceMappingURL=sync.js.map