# Source: SkinToneAnalyzer.js

    import Globals from "./Globals";
    import { SKINTONE_TARGET_QUALITY } from "./MakeupSDK";
    import { MountFileWithProgress } from "./Utility/MountFileWithProgress";

    let resource_loading = null;

    /**
     * Provides skin tone estimation functionality over multiple frames.
     * To enable this module pass the appropriate license to the SDKProvider.
     */
    export class SkinToneAnalyzer {
        static async Create(modulesManager, skinToneTargetQuality, onProgress) {
            const bsc = modulesManager.modules.visage.module;
            const quality =
                skinToneTargetQuality == SKINTONE_TARGET_QUALITY.MORE_ACCURACY
                    ? bsc.SkinToneTargetQuality.MORE_ACCURACY
                    : bsc.SkinToneTargetQuality.MORE_SPEED;
            const bscAnalyzer = bsc.GetSkinToneAnalysis(quality);

            // file loads wrapped so they load lazily, but once-only
            if (!resource_loading) {
                // NOTE(Straza) byteLen added because if the server uses compression (gzip),
                // content-length will not be correct
                // there is no standard header for uncompressed size
                let resources = [];

                if (
                    quality?.value == bsc.SkinToneTargetQuality.MORE_ACCURACY?.value
                ) {
                    resources = [
                        {
                            name: "d_st_c.bin",
                            path:
                                Globals.pathToAssets + "Assets/Plugins/d_st_c.bin",
                            byteLen: 1236,
                        },
                        {
                            name: "d_st_m.js.json",
                            path:
                                Globals.pathToAssets +
                                "Assets/Plugins/d_st_m.js.json",
                            byteLen: 320112,
                        },
                        {
                            name: "d_st_m.js.bin",
                            path:
                                Globals.pathToAssets +
                                "Assets/Plugins/d_st_m.js.bin",
                            byteLen: 9867712,
                        },
                    ];
                } else {
                    resources = [
                        {
                            name: "st_c.bin",
                            path: Globals.pathToAssets + "Assets/Plugins/st_c.bin",
                            byteLen: 1236,
                        },
                        {
                            name: "st_m.js.json",
                            path:
                                Globals.pathToAssets +
                                "Assets/Plugins/st_m.js.json",
                            byteLen: 39024,
                        },
                        {
                            name: "st_m.js.bin",
                            path:
                                Globals.pathToAssets + "Assets/Plugins/st_m.js.bin",
                            byteLen: 22344672,
                        },
                    ];
                }

                let curentprogresses = resources.reduce((acc, x) => {
                    acc[x.name] = {
                        contentLength: x.byteLen,
                        receivedLength: 0,
                        progress: 0,
                        done: false,
                    };
                    return acc;
                }, {});

                let total = resources
                    .map((k) => k.byteLen)
                    .reduce((partialSum, a) => partialSum + a, 0);

                const start = (x) => {};

                const progress = (name) => (x) => {
                    if (!onProgress) return;

                    curentprogresses[name] = x;

                    const keys = Object.keys(curentprogresses);
                    const done = keys.every((k) => curentprogresses[k].done);

                    let received = 0;

                    if (done) {
                        received = total;
                    } else {
                        received = keys
                            .map((k) => curentprogresses[k].receivedLength)
                            .reduce((partialSum, a) => partialSum + a, 0);
                    }

                    onProgress({
                        contentLength: total,
                        receivedLength: received,
                        progress: received / total,
                        done,
                    });
                };

                const resource_promises = resources.map((r) =>
                    MountFileWithProgress(
                        bsc.FS,
                        r.name,
                        r.path,
                        progress(r.name),
                        start,
                        r.byteLen,
                    ),
                );

                resource_loading = Promise.all(resource_promises).catch(
                    () => (resource_loading = null),
                );
            }
            await resource_loading;

            return new SkinToneAnalyzer(bscAnalyzer, bsc);
        }

        constructor(bscAnalyzer, bsc) {
            this.bscAnalyzer = bscAnalyzer;
            this.bsc = bsc;
        }

        /**
         *@callback SkinToneCallback
         *@param {Object} SkinToneAnalyzerResult - Analyzed skin tone
         *@param {"MONK_1"|"MONK_2"|"MONK_3"|"MONK_4"|"MONK_5"|"MONK_6"|"MONK_7"|"MONK_8"|"MONK_9"|"MONK_10"|"UNDEFINED"} SkinToneAnalyzerResult.monk - Estimated Monk skin tone
         * The monk skin tone scale is a scale describing human skin color.
         * The scale consist of 10 categories, where a lower number maps to lighter while a higher to darker skin tones.
         *@param {number} SkinToneAnalyzerResult.decimalMonk - Numerical value of predicted monk skin tone.
         *@param {Array<number>} SkinToneAnalyzerResult.RGB - This property now represents apparent RGB instead of the original Google RGB representation, of various monk skin tones.
         *@param {Array<number>} SkinToneAnalyzerResult.renderRGB - Deprecated: RGB of various monk skin tones that will give more realistic rendering results
         *@deprecated SkinToneAnalyzerResult.renderRGB - will be removed in future versions, SkinToneAnalyzerResult.RGB - It will be used as apparent RGB representation of various monk skin tones
         *@param {Array<number>} SkinToneAnalyzerResult.HSL - Apparent hue, saturation and light of skin
         *@param {number} SkinToneAnalyzerResult.frameIndex -  Index of the frame that has been analyzed.
         *@param { "OFF"|"OK"|"RECOVERING"|"INIT" } SkinToneAnalyzerResult.status - Indicates tracking status.
         */

        /**
         * Initiates the skin tone analysis. When a frame has been analyzed a call to the {@link SkinToneCallback}
         * function is issued, with the analysis results passed as the argument.
         * <br/>
         * @param {SkinToneCallback} SkinToneCallback - callback which will be called every time a new frame is analyzed
         */
        StartAnalysis(SkinToneCallback) {
            const convert = (result) => {
                const monk_mapping = new Map([
                    [this.bsc.MonkScale.Monk_1, "MONK_1"],
                    [this.bsc.MonkScale.Monk_2, "MONK_2"],
                    [this.bsc.MonkScale.Monk_3, "MONK_3"],
                    [this.bsc.MonkScale.Monk_4, "MONK_4"],
                    [this.bsc.MonkScale.Monk_5, "MONK_5"],
                    [this.bsc.MonkScale.Monk_6, "MONK_6"],
                    [this.bsc.MonkScale.Monk_7, "MONK_7"],
                    [this.bsc.MonkScale.Monk_8, "MONK_8"],
                    [this.bsc.MonkScale.Monk_9, "MONK_9"],
                    [this.bsc.MonkScale.Monk_10, "MONK_10"],
                    [this.bsc.MonkScale.UNDEFINED, "UNDEFINED"],
                ]);

                const status_mapping = new Map([
                    [this.bsc.TrackStatus.off, "OFF"],
                    [this.bsc.TrackStatus.init, "INIT"],
                    [this.bsc.TrackStatus.recovering, "RECOVERING"],
                    [this.bsc.TrackStatus.ok, "OK"],
                ]);

                let res = {
                    frameIndex: result.frameIndex,
                    monk: monk_mapping.get(result.monk),
                    decimalMonk: result.decimalMonk,
                    RGB: result.RGB,
                    renderRGB: result.renderRGB,
                    HSL: result.HSL,
                    status: status_mapping.get(result.status),
                };
                result.delete();

                return res;
            };

            const convertThenCall = (result) => {
                SkinToneCallback(convert(result));
            };

            this.bscAnalyzer.SetCallback(convertThenCall);
            this.bscAnalyzer.StartAnalysis();
        }

        /**
         * Stops the skin tone analysis and stops issuing calls to the callback given as a argument to the
         * {@link StartAnalysis} function.
         * <br/>
         */
        StopAnalysis() {
            this.bscAnalyzer.StopAnalysis();
        }

        /**
         * Resets the internal state of the skin tone analyzer. This is useful when switching between frames of different people.
         */
        Reset() {
            this.bscAnalyzer.Reset();
        }
    }