import { Converter, Converters } from ".";
import path from 'path';
import fs from 'fs';
import { keywords_1 } from "./utils";

type Asset = {
    url: string,
    class: string,
    hashCode: number
}
type GameObject_v1 = {
    hashCode: number,
    class: string
}
type Scene_v1 = {
    assets: Asset[],
    objects: GameObject_v1[]
}
type Material_v1 = {
    asset: number
};



type GameObject_v2 = {
    uuid: string,
}
type Component_v2 = {
    uuid: string,
    _materials?: Material_v1[],
}
type Scene_v2 = {
    assets: string[],
    objects: GameObject_v2[],
    components: Component_v2[],
};

type Material_v2 = {
    class: string,
    _glTFMaterialIndex: number,
    _glTFAsset: { asset: number },
}

type Component_v3 = {
    _materials?: Material_v2[],
    _mesh?: { asset: number },
    _animations?: { asset: number }[]
}

type Scene_v3 = {
    assets: string[],
    components: Component_v3[],
}


type AssetsConfig_v2 = {
    [url: string]: { resName: string, class: string }
}


type ResourceConfig = {

    group: string[],

    resources: { name: string, type: string, url: string, uuid: string }[]
}

class SceneConverter implements Converter {

    /**
     * 这个变量只是用来记录URL是否已经存储过，没存储过的才会加入 assets 列表
     */
    private temp_assets: string[] = []

    private assets: AssetsConfig_v2 = {};


    private resourceConfigJson!: ResourceConfig;
    onStart(rootDir: string) {
        const resourceFile = path.join(rootDir, 'default.res.json');
        const content = fs.readFileSync(resourceFile, 'utf-8');
        this.resourceConfigJson = JSON.parse(content) as ResourceConfig;
    }

    convert(filename: string, config: Scene_v1): Scene_v2 {

        const config_2 = Object.assign(config);

        const hashcodeToUUid: { [hashCode: number]: string } = {}

        const hashcodeToResname: { [hashCode: number]: string } = {};

        const convertUUIDforAssets = (item: Asset) => {

            const absoluteUrl = !(item.url in keywords_1)
                ? path.join(path.dirname(filename), item.url).split("\\").join("/")
                : keywords_1[item.url];

            let resName = '';
            if (this.temp_assets.indexOf(absoluteUrl) == -1) {
                this.temp_assets.push(absoluteUrl);
                resName = this.getResName(absoluteUrl);

                this.assets[absoluteUrl] = { resName, class: item.class };
            }
            else {
                resName = this.assets[absoluteUrl].resName;
            }
            if (!resName) {
                throw 'what'
            }
            hashcodeToResname[item.hashCode] = resName;
        }
        config_2.assets.forEach(convertUUIDforAssets);

        const convertUUIDforGameObject = (object: GameObject_v1) => {
            const obj = object as any as GameObject_v2;
            const hashCode = object.hashCode;
            delete object.hashCode
            obj.uuid = createUUID();
            hashcodeToUUid[hashCode] = obj.uuid;
        }

        config_2.objects.forEach(convertUUIDforGameObject);

        const createAssets = (): any[] => {
            let returnList = [];
            for (let key in hashcodeToResname) {
                returnList.push(hashcodeToResname[key]);
            }
            return returnList;
        }
        config_2.assets = createAssets();

        function walk(json: any) {

            if (typeof json == 'object') {
                const removes = new Array<number>();

                for (let key in json) {
                    var value = json[key];
                    if (value) {
                        delete value._parent;

                        if (value.hashCode && Object.keys(value).length == 1) {
                            const hashCode = value.hashCode;

                            if (hashcodeToResname[hashCode]) {
                                delete value.hashCode;
                                let url: any = hashcodeToResname[hashCode];
                                let assetIndex = config_2.assets.indexOf(url);
                                value.asset = assetIndex;
                            }
                            else if (hashcodeToUUid[hashCode]) {
                                delete value.hashCode;
                                value.uuid = hashcodeToUUid[hashCode];
                            }
                            else {
                                if (json instanceof Array) {
                                    removes.unshift(Number(key));
                                }
                            }
                        }

                        walk(value);
                    }
                }

                if (json instanceof Array && removes.length > 0) {
                    for (const index of removes) {
                        json.splice(index, 1);
                    }
                }
            }
        }
        walk(config_2.objects);

        (config_2 as any).components = [];
        for (let i: number = config_2.objects.length - 1; i >= 0; i--) {
            const obj = config_2.objects[i];
            //可能有mesh不兼容的数据，这里处理一下
            if (obj["_mesh"] && obj["_mesh"]["uuid"]) {
                const uuid = obj["_mesh"]["uuid"];
                let findMeshIndex = -1;
                for (let j = 0; j < config_2.objects.length; j++) {
                    const obj1 = config_2.objects[j];
                    if (obj1["uuid"] === uuid) {
                        findMeshIndex = j;
                        break;
                    }
                }

                if (findMeshIndex !== -1) {
                    const mesh = config_2.objects[findMeshIndex];
                    obj["_mesh"] = { class: mesh["class"], _drawMode: mesh["_drawMode"], _glTFMeshIndex: mesh["_glTFMeshIndex"], _glTFAsset: { asset: mesh["_glTFAsset"]["asset"] } };
                    config_2.objects.splice(findMeshIndex, 1);
                }
                else {
                    console.error("找不到对应的mesh：" + uuid);
                }
            }
        }


        for (let i: number = config_2.objects.length - 1; i >= 0; i--) {
            const obj = config_2.objects[i];
            const classCode = obj.class;
            if (classCode !== '0' && classCode !== '1') {
                (config_2 as any).components.push(config_2.objects[i]);
                config_2.objects.splice(i, 1);
            }
        }

        return config_2 as any;
    }
    onFinish(): void {

    }

    private getResName(url: string) {
        const result = this.resourceConfigJson.resources.find((item) => item.url == url);
        if (result) {
            return result.name;
        }

        return url;
    }

}

class Scene2To3Converter implements Converter {
    private rootDir: string = "";
    private absoluteRootDir: string = "";
    onStart(rootDir: string) {
        this.rootDir = rootDir;
        this.absoluteRootDir = path.join(process.cwd(), rootDir);
    }

    convert(filename: string, config: Scene_v2): Scene_v3 {
        console.log('convert scene', filename)
        //把组件中引用材质的地方换成材质结构体
        for (const component of config.components) {
            if (component._materials) {
                for (const material of component._materials) {
                    const newMat = material as any as Material_v2;
                    newMat.class = "14";
                    newMat._glTFMaterialIndex = 0;
                    newMat._glTFAsset = { asset: material.asset };
                    delete material.asset;
                }
            }
        }

        return config as any;
    }


    onFinish() {

    }
}

//
class Scene3To4Converter implements Converter {
    private _rootDir: string;
    onStart(rootDir: string) {
        this._rootDir = rootDir;
    }

    convert(filename: string, config: Scene_v3): Scene_v3 {
        const assets = config.assets;
        for (let key in assets) {
            if (assets[key].indexOf(".imgdesc.json") > 0) {
                const filePath = path.join(this._rootDir, assets[key]);
                if (fs.existsSync(filePath)) {
                    // console.log("image:" + filePath);
                    assets[key] = assets[key].replace(".imgdesc.json", ".image.json");
                    fs.renameSync(filePath, filePath.replace(".imgdesc.json", ".image.json"))
                    // console.log("--------------");
                }
            }
        }
        const comps = config.components;
        for (const key in comps) {
            if (comps[key]._animations) {
                for (const ani of comps[key]._animations) {
                    const asset = assets[ani.asset];
                    // console.log("ani:" + asset);
                    const filePath = path.join(this._rootDir, asset);
                    assets[ani.asset] = asset.replace(".gltf.bin", ".ani.bin");
                    if (fs.existsSync(filePath)) {
                        // console.log("ani:" + filePath);
                        fs.renameSync(filePath, filePath.replace(".gltf.bin", ".ani.bin"))
                        // console.log("--------------");
                    }
                }
            }

            if (comps[key]._materials) {
                const mats = comps[key]._materials;
                for (const matName in mats) {
                    if (mats[matName]._glTFAsset) {
                        mats[matName] = { asset: mats[matName]._glTFAsset.asset } as any;
                    }
                }
            }

            // console.log("_mesh:");
            if (comps[key]._mesh) {
                let index = comps[key]._mesh.asset;
                if (index === undefined || index === null) {
                    index = (comps[key]._mesh as any)["_glTFAsset"]["asset"];
                    comps[key]._mesh = { asset: index };
                }
                const asset = assets[index];
                const filePath = path.join(this._rootDir, asset);
                assets[index] = asset.replace(".gltf.bin", ".mesh.bin");
                if (fs.existsSync(filePath)) {
                    // console.log("mesh:" + filePath);
                    const newPath = filePath.replace(".gltf.bin", ".mesh.bin");
                    fs.renameSync(filePath, newPath)
                    // console.log("mesh:" + asset + "->:" + newPath);
                }
            }

        }
        return config as any;
    }


    onFinish() {

    }
}


let uuidCount = 0;

function createUUID() {
    uuidCount++;
    return uuidCount.toString();
}

export const sceneConverters: Converters = {

    matcher: "**/**/*.{prefab.json,scene.json}",

    converters: [new SceneConverter, new Scene2To3Converter, new Scene3To4Converter]
}