export class DisplayManager {
    constructor() {
        this.looks = [];
        this.makeupEngine = null;
        this.busy = false;
        this.options = [];
    }

    async push(key, look) {
        const effects = [];

        for (const effect of look) {
            const ID = await this.makeupEngine.CreateMakeup(effect.feature, effect.finish, [0.0, 0.0, 1.0, 1.0]);
            this.makeupEngine.UpdateMakeup(ID); // ?????????????????????????
            effects.push({ effect, ID });
        }

        const engineLook = { key, effects };
        this.looks.push(engineLook);

        return engineLook;
    }

    getActiveLooks() {
        return this.looks.filter((x) => x.active);
    }
    getLookByKey(key) {
        return this.looks.find((x) => x.key === key);
    }

    _hideLook(look) {
        return look?.effects.map((effect) => this.makeupEngine.HideMakeup(effect.ID)) ?? [];
    }
    _displayLook(look) {
        return look?.effects.map((effect) => this.makeupEngine.DisplayMakeup(effect.ID)) ?? [];
    }

    async activateLook(key) {
        const fresh = this.getLookByKey(key);
        if (fresh && !fresh.active) {
            this.busy = true;
            fresh.active = true;
            await Promise.all(this._displayLook(fresh)).finally(() => (this.busy = false));
        }
    }

    async deactivateLook(key) {
        const old = this.getLookByKey(key);
        if (old && old.active) {
            this.busy = true;
            old.active = false;
            await Promise.all(this._hideLook(old)).finally(() => (this.busy = false));
        }
    }

    async clearLooks() {
        this.busy = true;
        await Promise.all(this.getActiveLooks().map((look) => this.deactivateLook(look.key))).finally(
            () => (this.busy = false)
        );
    }

    async updateMakeup(lookCallback) {
        for (const look of this.looks) {
            lookCallback(look);
            for (const effect of look.effects) await this.makeupEngine.UpdateMakeup(effect.ID);
        }
    }

    static async Create(makeupEngine) {
        const manager = new DisplayManager();
        manager.makeupEngine = makeupEngine;

        let features = makeupEngine.GetFeatures();
        let finishes = makeupEngine.GetFinishes();
        const [matte, metallic, glossy, newGlossy, satin] = ["Matte", "Metallic", "Glossy", "Lipgloss", "Satin"].map(
            (name) => finishes.find((fin) => fin.name === name)
        );

        const find_feature = (name) => features.find((x) => x.name === name);

        //softener
        const faceSoftener = find_feature("FaceSoftener");
        faceSoftener.elements[0].color.a = 0.5;
        let id = await makeupEngine.CreateMakeup(faceSoftener, null);
        makeupEngine.DisplayMakeup(id);

        //perfect coverup foundation
        const foundation = find_feature("FoundationFull");
        foundation.elements[1].color = { r: 201.0 / 255.0, g: 167.0 / 255.0, b: 147.0 / 255.0, a: 0.1 };

        //contour heart
        const contourHeart = find_feature("ContourHeart");
        contourHeart.elements[0].color = { r: 119.0 / 255.0, g: 77.0 / 255.0, b: 65.0 / 255.0, a: 0.4 };

        //contour oval
        const contourOval = find_feature("ContourOval");
        contourOval.elements[0].color = { r: 119.0 / 255.0, g: 77.0 / 255.0, b: 65.0 / 255.0, a: 0.4 };

        //contour round
        const contourRound = find_feature("ContourRound");
        contourRound.elements[0].color = { r: 119.0 / 255.0, g: 77.0 / 255.0, b: 65.0 / 255.0, a: 0.4 };

        //contour square
        const contourSquare = find_feature("ContourSquare");
        contourSquare.elements[0].color = { r: 119.0 / 255.0, g: 77.0 / 255.0, b: 65.0 / 255.0, a: 0.4 };

        //blush square
        const blushSquare = find_feature("BlushApple");
        blushSquare.elements[0].color = { r: 225.0 / 255.0, g: 133.0 / 255.0, b: 122.0 / 255.0, a: 0.22 };

        //blush heart
        const blushHeart = find_feature("BlushLong");
        blushHeart.elements[0].color = { r: 225.0 / 255.0, g: 133.0 / 255.0, b: 122.0 / 255.0, a: 0.22 };

        //blush oval
        const blushOval = find_feature("BlushOval");
        blushOval.elements[0].color = { r: 225.0 / 255.0, g: 133.0 / 255.0, b: 122.0 / 255.0, a: 0.22 };

        //blush round
        const blushRound = find_feature("BlushRound");
        blushRound.elements[0].color = { r: 225.0 / 255.0, g: 133.0 / 255.0, b: 122.0 / 255.0, a: 0.22 };

        //highlighter
        const highlighter = find_feature("Highlighter");
        highlighter.elements[0].color = { r: 255.0 / 255.0, g: 255.0 / 255.0, b: 255.0 / 255.0, a: 0.1 };
        highlighter.transparency = 0.1;

        //eyebrows
        const thickBrows = find_feature("EyebrowsThick");
        thickBrows.elements[0].color = { r: 48.0 / 255.0, g: 27.0 / 255.0, b: 15.0 / 255.0, a: 150.0 / 255.0 };

        //lipstick matte
        const lipstickMatte = find_feature("LipstickOneTone");
        lipstickMatte.elements[0].color = { r: 110.0 / 255.0, g: 5.0 / 255.0, b: 2.0 / 255.0, a: 180.0 / 255.0 };

        //lipstick satin
        const lipstickSatin = find_feature("LipstickOneTone");
        lipstickSatin.elements[0].color = { r: 168 / 255.0, g: 72 / 255.0, b: 96 / 255.0, a: 200 / 255.0 };

        //lipstick glossy
        const lipstickGlossy = find_feature("LipstickOneTone");
        lipstickGlossy.elements[0].color = { r: 188 / 255.0, g: 100 / 255.0, b: 79 / 255.0, a: 130 / 255.0 };
        glossy.transparency = 0.5;

        //lipstick
        const lipstick = find_feature("LipstickOneTone");
        lipstick.elements[0].color = { r: 178 / 255.0, g: 31 / 255.0, b: 50 / 255.0, a: 120 / 255.0 };

        //lipliner
        const lipliner = find_feature("Lipliner");
        lipliner.elements[0].color = { r: 188 / 255.0, g: 100 / 255.0, b: 79 / 255.0, a: 230 / 255.0 };

        //lipstick ombre out
        const lipstickOmbreOut = find_feature("LipstickOmbreOuter");
        lipstickOmbreOut.elements[0].color = { r: 110.0 / 255.0, g: 5.0 / 255.0, b: 2.0 / 255.0, a: 120.0 / 255.0 };

        //lipsticck ombre in
        const lipstickOmbreIn = find_feature("LipstickOmbreInner");
        lipstickOmbreIn.elements[0].color = { r: 255 / 255.0, g: 0 / 255.0, b: 0 / 255.0, a: 130.0 / 255.0 };

        const generateOptions = async (optionsName, options) => {
            const keyedOptions = options.map((option) => ({ ...option, key: `${optionsName}_${option.name}` }));
            for (const option of keyedOptions) {
                await manager.push(option.key, option.effects);
            }
            manager.options.push([optionsName, keyedOptions]);
        };

        await generateOptions("face-heart", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourHeart, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushHeart, finish: satin }] },
        ]);
        await generateOptions("face-round", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourRound, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushRound, finish: satin }] },
        ]);
        await generateOptions("face-oval", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourOval, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushOval, finish: satin }] },
        ]);
        await generateOptions("face-square", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourSquare, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushSquare, finish: satin }] },
        ]);

        await generateOptions("lips-wide", [
            {
                name: "Lipstick ombre",
                effects: [
                    { feature: lipstickOmbreOut, finish: matte },
                    { feature: lipstickOmbreIn, finish: matte },
                ],
            },
        ]);
        await generateOptions("lips-thin", [
            { name: "Lipstick glossy", effects: [{ feature: lipstick, finish: glossy }] },
        ]);
        await generateOptions("lips-medium", [
            { name: "Lipstick satin", effects: [{ feature: lipstick, finish: satin }] },
        ]);
        await generateOptions("lips-full", [
            { name: "Lipstick matte", effects: [{ feature: lipstick, finish: matte }] },
        ]);
        
        await generateOptions("eye-almond", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourHeart, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushHeart, finish: satin }] },
        ]);

        await generateOptions("eye-hooded", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourHeart, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushHeart, finish: satin }] },
        ]);

        await generateOptions("eye-monolid", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourHeart, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushHeart, finish: satin }] },
        ]);

        await generateOptions("eye-round", [
            { name: "Foundation", effects: [{ feature: foundation, finish: satin }] },
            { name: "Contour", effects: [{ feature: contourHeart, finish: satin }] },
            { name: "Blush", effects: [{ feature: blushHeart, finish: satin }] },
        ]);

        return manager;
    }
}
