 // shitcode will be better in the future
 // ,      \u00dc, \u00fc
 // ,      \u00c4, \u00e4
 // ,      \u00d6, \u00f6
 //         \u00df


 import * as NativeUI from 'NativeUI';
 import * as Data from './data';

 const Menu = NativeUI.Menu;
 const UIMenuItem = NativeUI.UIMenuItem;
 const UIMenuListItem = NativeUI.UIMenuListItem;
 const UIMenuCheckboxItem = NativeUI.UIMenuCheckboxItem;
 const BadgeStyle = NativeUI.BadgeStyle;
 const Point = NativeUI.Point;
 const ItemsCollection = NativeUI.ItemsCollection;
 const Color = NativeUI.Color;

 const creatorCoords = {
     camera: new mp.Vector3(402.8664, -997.5515, -98.5),
     cameraLookAt: new mp.Vector3(402.8664, -996.4108, -98.5)
 };

 const localPlayer = mp.players.local;

export default function charCreator(globalData: GlobalData) {

    var creatorHairMenu;

    function getRandomInt(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function colorForOverlayIdx(index) {
        let color;

        switch (index) {
            case 1:
                color = beardColorItem.Index;
                break;

            case 2:
                color = eyebrowColorItem.Index;
                break;

            case 5:
                color = blushColorItem.Index;
                break;

            case 8:
                color = lipstickColorItem.Index;
                break;

            case 10:
                color = chestHairColorItem.Index;
                break;

            default:
                color = 0;
        }

        return color;
    }

    function updateParents() {
        localPlayer.setHeadBlendData(
            // shape
            Data.mothers[motherItem.Index],
            Data.fathers[fatherItem.Index],
            0,

            // skin
            Data.mothers[motherItem.Index],
            Data.fathers[fatherItem.Index],
            0,

            // mixes
            similarityItem.Index * 0.01,
            skinSimilarityItem.Index * 0.01,
            0.0,

            false
        );
    }

    function updateFaceFeature(index) {
        localPlayer.setFaceFeature(index, parseFloat(featureItems[index].SelectedValue));
    }

    function updateAppearance(index) {
        let overlayID = (appearanceItems[index].Index === 0) ? 255 : appearanceItems[index].Index - 1;
        localPlayer.setHeadOverlay(index, overlayID, appearanceOpacityItems[index].Index * 0.01, colorForOverlayIdx(index), 0);
    }

    function updateHairAndColors() {
        localPlayer.setComponentVariation(2, Data.hairList[currentGender][hairItem.Index].ID, 0, 2);
        localPlayer.setHairColor(hairColorItem.Index, hairHighlightItem.Index);
        localPlayer.setEyeColor(eyeColorItem.Index);
        localPlayer.setHeadOverlayColor(1, 1, beardColorItem.Index, 0);
        localPlayer.setHeadOverlayColor(2, 1, eyebrowColorItem.Index, 0);
        localPlayer.setHeadOverlayColor(5, 2, blushColorItem.Index, 0);
        localPlayer.setHeadOverlayColor(8, 2, lipstickColorItem.Index, 0);
        localPlayer.setHeadOverlayColor(10, 1, chestHairColorItem.Index, 0);
    }

    function applyCreatorOutfit() {
        if (currentGender === 0) {
            localPlayer.setDefaultComponentVariation();
            localPlayer.setComponentVariation(3, 15, 0, 2);
            localPlayer.setComponentVariation(4, 21, 0, 2);
            localPlayer.setComponentVariation(6, 34, 0, 2);
            localPlayer.setComponentVariation(8, 15, 0, 2);
            localPlayer.setComponentVariation(11, 15, 0, 2);
        } else {
            localPlayer.setDefaultComponentVariation();
            localPlayer.setComponentVariation(3, 15, 0, 2);
            localPlayer.setComponentVariation(4, 10, 0, 2);
            localPlayer.setComponentVariation(6, 35, 0, 2);
            localPlayer.setComponentVariation(8, 15, 0, 2);
            localPlayer.setComponentVariation(11, 15, 0, 2);
        }
    }

    function fillHairMenu() {
        hairItem = new UIMenuListItem("Haar", "Deine Haare", new ItemsCollection(Data.hairList[currentGender].map(h => h.Name)));
        creatorHairMenu.AddItem(hairItem);

        hairColorItem = new UIMenuListItem("Haarfarbe", "Deine Haarfarbe", new ItemsCollection(hairColors));
        creatorHairMenu.AddItem(hairColorItem);

        hairHighlightItem = new UIMenuListItem("Haarstr\u00e4hnen", "Farbe deiner Haarstr\u00e4hnen", new ItemsCollection(hairColors));
        creatorHairMenu.AddItem(hairHighlightItem);

        eyebrowColorItem = new UIMenuListItem("Augenbrauen Farbe", "Farbe deiner Augenbrauen", new ItemsCollection(hairColors));
        creatorHairMenu.AddItem(eyebrowColorItem);

        beardColorItem = new UIMenuListItem("Farbe der Gesichtsbehaarung", "Farbe deiner Gesichtsbehaarung", new ItemsCollection(hairColors));
        creatorHairMenu.AddItem(beardColorItem);

        eyeColorItem = new UIMenuListItem("Augenfarbe", "Farbe deiner Augen", new ItemsCollection(Data.eyeColors));
        creatorHairMenu.AddItem(eyeColorItem);

        blushColorItem = new UIMenuListItem("Rouge", "Farbe des Rouges.", new ItemsCollection(blushColors));
        creatorHairMenu.AddItem(blushColorItem);

        lipstickColorItem = new UIMenuListItem("Lippenstift Farbe", "Farbe deines Lippenstifts.", new ItemsCollection(lipstickColors));
        creatorHairMenu.AddItem(lipstickColorItem);

        chestHairColorItem = new UIMenuListItem("Farbe der Brustbehaarung", "Farbe deiner Brustbehaarung", new ItemsCollection(hairColors));
        creatorHairMenu.AddItem(chestHairColorItem);

        creatorHairMenu.AddItem(new UIMenuItem("Zuf\u00e4llig", "~r~Zuf\u00e4lliges Haar & Farben"));
        creatorHairMenu.AddItem(new UIMenuItem("Zur\u00fccksetzen", "~r~Zur\u00fccksetzen von Haar & Farben"));
    }

    function resetParentsMenu(refresh = false) {
        fatherItem.Index = 0;
        motherItem.Index = 0;
        similarityItem.Index = (currentGender === 0) ? 100 : 0;
        skinSimilarityItem.Index = (currentGender === 0) ? 100 : 0;

        updateParents();
        if (refresh) creatorParentsMenu.RefreshIndex();
    }

    function resetFeaturesMenu(refresh = false) {
        for (let i = 0; i < Data.featureNames.length; i++) {
            featureItems[i].Index = 100;
            updateFaceFeature(i);
        }

        if (refresh) creatorFeaturesMenu.RefreshIndex();
    }

    function resetAppearanceMenu(refresh = false) {
        for (let i = 0; i < Data.appearanceNames.length; i++) {
            appearanceItems[i].Index = 0;
            appearanceOpacityItems[i].Index = 100;
            updateAppearance(i);
        }

        if (refresh) creatorAppearanceMenu.RefreshIndex();
    }

    function resetHairAndColorsMenu(refresh = false) {
        hairItem.Index = 0;
        hairColorItem.Index = 0;
        hairHighlightItem.Index = 0;
        eyebrowColorItem.Index = 0;
        beardColorItem.Index = 0;
        eyeColorItem.Index = 0;
        blushColorItem.Index = 0;
        lipstickColorItem.Index = 0;
        chestHairColorItem.Index = 0;
        updateHairAndColors();

        if (refresh) creatorHairMenu.RefreshIndex();
    }

    let currentGender = 0;
    let creatorMenus: NativeUI.Menu[] = [];
    let creatorCamera;

    // color arrays
    let hairColors = [];
    for (let i = 0; i < Data.maxHairColor; i++) hairColors.push(i.toString());

    let blushColors = [];
    for (let i = 0; i < Data.maxBlushColor; i++) blushColors.push(i.toString());

    let lipstickColors = [];
    for (let i = 0; i < Data.maxLipstickColor; i++) lipstickColors.push(i.toString());

    // CREATOR MAIN
    let creatorMainMenu = new Menu("Charaktererstellung", "", new Point(50, 50), null, null);
    let genderItem = new UIMenuListItem("Geschlecht", "~r~Dies setzt deine Einstellungen zur\u00fcck.", new ItemsCollection(["M\u00e4nnlich", "Weiblich"]));
    creatorMainMenu.AddItem(genderItem);
    creatorMainMenu.AddItem(new UIMenuItem("Eltern", "Eltern des Charakters."));
    creatorMainMenu.AddItem(new UIMenuItem("Gesichtsz\u00fcge", "Gesichtsz\u00fcge des Charakters."));
    creatorMainMenu.AddItem(new UIMenuItem("Aussehen", "Aussehen des Charakters."));
    creatorMainMenu.AddItem(new UIMenuItem("Haar & Farben", "Haare & Farben deines Charakters."));

    let angles = [];
    for (let i = -180.0; i <= 180.0; i += 5.0) angles.push(i.toFixed(1));
    let angleItem = new UIMenuListItem("Drehung", "", new ItemsCollection(angles));
    creatorMainMenu.AddItem(angleItem);

    let saveItem = new UIMenuItem("Erstellen", "Erstellt deinen Charakter.");
    saveItem.BackColor = new Color(13, 71, 161);
    saveItem.HighlightedBackColor = new Color(25, 118, 210);
    creatorMainMenu.AddItem(saveItem);

    //let cancelItem = new UIMenuItem("Abbrechen", "Setzt alle \u00c4nderungen zur\u00fcck.");
    //cancelItem.BackColor = new Color(213, 0, 0);
    //cancelItem.HighlightedBackColor = new Color(229, 57, 53);
    //creatorMainMenu.AddItem(cancelItem);

    creatorMainMenu.ListChange.on((item, listIndex) => {
        if (item === genderItem) {
            currentGender = listIndex;
            mp.events.callRemote("creator_GenderChange", listIndex);

            setTimeout(() => {
                localPlayer.clearTasksImmediately();
                applyCreatorOutfit();
                angleItem.Index = 0;
                resetParentsMenu(true);
                resetFeaturesMenu(true);
                resetAppearanceMenu(true);

                creatorHairMenu.Clear();
                fillHairMenu();
                creatorHairMenu.RefreshIndex();
            }, 200);
        } else if (item === angleItem) {
            localPlayer.setHeading(parseFloat(angleItem.SelectedValue));
            localPlayer.clearTasksImmediately();
        }
    });

    creatorMainMenu.ItemSelect.on((item, index) => {
        switch (index) {
            case 1:
                creatorMainMenu.Visible = false;
                creatorParentsMenu.Visible = true;
                break;

            case 2:
                creatorMainMenu.Visible = false;
                creatorFeaturesMenu.Visible = true;
                break;

            case 3:
                creatorMainMenu.Visible = false;
                creatorAppearanceMenu.Visible = true;
                break;

            case 4:
                creatorMainMenu.Visible = false;
                creatorHairMenu.Visible = true;
                break;

            case 6:
                let parentData = {
                    Father: Data.fathers[fatherItem.Index],
                    Mother: Data.mothers[motherItem.Index],
                    Similarity: similarityItem.Index * 0.01,
                    SkinSimilarity: skinSimilarityItem.Index * 0.01
                };

                let featureData = [];
                for (let i = 0; i < featureItems.length; i++) featureData.push(parseFloat(featureItems[i].SelectedValue));

                let appearanceData = [];
                for (let i = 0; i < appearanceItems.length; i++) appearanceData.push({ Value: ((appearanceItems[i].Index === 0) ? 255 : appearanceItems[i].Index - 1), Opacity: appearanceOpacityItems[i].Index * 0.01 });

                let hairAndColors = [
                    Data.hairList[currentGender][hairItem.Index].ID,
                    hairColorItem.Index,
                    hairHighlightItem.Index,
                    eyebrowColorItem.Index,
                    beardColorItem.Index,
                    eyeColorItem.Index,
                    blushColorItem.Index,
                    lipstickColorItem.Index,
                    chestHairColorItem.Index
                ];
                for (let i = 0; i < creatorMenus.length; i++) creatorMenus[i].Visible = false;
                mp.gui.chat.show(true);
                mp.game.ui.displayRadar(true);
                mp.game.ui.displayHud(true);
                localPlayer.freezePosition(false);
                localPlayer.setDefaultComponentVariation();
                localPlayer.setComponentVariation(2, Data.hairList[currentGender][hairItem.Index].ID, 0, 2);
                mp.game.cam.renderScriptCams(false, false, 0, true, false);
                mp.events.callRemote("creatorSave", currentGender, JSON.stringify(parentData), JSON.stringify(featureData), JSON.stringify(appearanceData), JSON.stringify(hairAndColors));
                break;

            case 7:
                mp.events.callRemote("creator_Leave");
                break;
        }
    });

    creatorMainMenu.MenuClose.on(() => {
        mp.events.callRemote("creator_Leave");
        globalData.InMenu = false;
    });

    creatorMainMenu.Visible = false;
    creatorMenus.push(creatorMainMenu);
    // CREATOR MAIN END

    // CREATOR PARENTS
    let similarities = [];
    for (let i = 0; i <= 100; i++) similarities.push(i + "%");

    let creatorParentsMenu = new Menu("Eltern", "", new Point(50, 50), null, null);
    let fatherItem = new UIMenuListItem("Vater", "Dem Charakter sein Vadda.", new ItemsCollection(Data.fatherNames));
    let motherItem = new UIMenuListItem("Mutter", "Dem Charakter seine Mudda.", new ItemsCollection(Data.motherNames));
    let similarityItem = new UIMenuListItem("\u00c4hnlichkeit", "\u00c4hnlichkeit zu den Eltern.\n(niedriger = feminin, h\u00f6her = maskulin)", new ItemsCollection(similarities));
    let skinSimilarityItem = new UIMenuListItem("Hautfarbe", "Hautfarben \u00c4hnlichkeit zu den Eltern.\n(niedriger = Mutter, h\u00f6her = Vater)", new ItemsCollection(similarities));
    creatorParentsMenu.AddItem(fatherItem);
    creatorParentsMenu.AddItem(motherItem);
    creatorParentsMenu.AddItem(similarityItem);
    creatorParentsMenu.AddItem(skinSimilarityItem);
    creatorParentsMenu.AddItem(new UIMenuItem("Zuf\u00e4llig", "~r~Zuf\u00e4llige Eltern."));
    creatorParentsMenu.AddItem(new UIMenuItem("Zur\u00fccksetzen", "~r~Setzt die Eltern zur\u00fcck. :'("));

    creatorParentsMenu.ItemSelect.on((item, index) => {
        switch (item.Text) {
            case "Zuf\u00e4llig":
                fatherItem.Index = getRandomInt(0, Data.fathers.length - 1);
                motherItem.Index = getRandomInt(0, Data.mothers.length - 1);
                similarityItem.Index = getRandomInt(0, 100);
                skinSimilarityItem.Index = getRandomInt(0, 100);
                updateParents();
                break;

            case "Zur\u00fccksetzen":
                resetParentsMenu();
                break;
        }
    });

    creatorParentsMenu.ListChange.on((item, listIndex) => {
        updateParents();
    });

    creatorParentsMenu.ParentMenu = creatorMainMenu;
    creatorParentsMenu.Visible = false;
    creatorMenus.push(creatorParentsMenu);
    // CREATOR PARENTS END

    // CREATOR FEATURES
    let featureItems = [];
    let features = [];
    for (let i = -1.0; i <= 1.01; i += 0.01) features.push(i.toFixed(2));

    let creatorFeaturesMenu = new Menu("Gesichtsz\u00fcge", "", new Point(50, 50), null, null);

    for (let i = 0; i < Data.featureNames.length; i++) {
        let tempFeatureItem = new UIMenuListItem(Data.featureNames[i], "", new ItemsCollection(features));
        tempFeatureItem.Index = 100;
        featureItems.push(tempFeatureItem);
        creatorFeaturesMenu.AddItem(tempFeatureItem);
    }

    creatorFeaturesMenu.AddItem(new UIMenuItem("Zuf\u00e4llig", "~r~Zuf\u00e4llige Gesichtsz\u00fcge."));
    creatorFeaturesMenu.AddItem(new UIMenuItem("Zur\u00fccksetzen", "~r~Setzt Gesichtsz\u00fcge zur\u00fcck."));

    creatorFeaturesMenu.ItemSelect.on((item, index) => {
        switch (item.Text) {
            case "Zuf\u00e4llig":
                for (let i = 0; i < Data.featureNames.length; i++) {
                    featureItems[i].Index = getRandomInt(0, 200);
                    updateFaceFeature(i);
                }
                break;

            case "Zur\u00fccksetzen":
                resetFeaturesMenu();
                break;
        }
    });

    creatorFeaturesMenu.ListChange.on((item, listIndex) => {
        updateFaceFeature(featureItems.indexOf(item));
    });

    creatorFeaturesMenu.ParentMenu = creatorMainMenu;
    creatorFeaturesMenu.Visible = false;
    creatorMenus.push(creatorFeaturesMenu);
    // CREATOR FEATURES END

    // CREATOR APPEARANCE
    let appearanceItems = [];
    let appearanceOpacityItems = [];
    let opacities = [];
    for (let i = 0; i <= 100; i++) opacities.push(i + "%");

    let creatorAppearanceMenu = new Menu("Aussehen", "", new Point(50, 50), null, null);

    for (let i = 0; i < Data.appearanceNames.length; i++) {
        let items = [];
        for (let j = 0, max = mp.game.ped.getNumHeadOverlayValues(i); j <= max; j++) items.push(Data.appearanceItemNames[i][j] === undefined ? j.toString() : Data.appearanceItemNames[i][j]);

        let tempAppearanceItem = new UIMenuListItem(Data.appearanceNames[i], "", new ItemsCollection(items));
        appearanceItems.push(tempAppearanceItem);
        creatorAppearanceMenu.AddItem(tempAppearanceItem);

        let tempAppearanceOpacityItem = new UIMenuListItem(Data.appearanceNames[i] + " Transparenz", "", new ItemsCollection(opacities));
        tempAppearanceOpacityItem.Index = 100;
        appearanceOpacityItems.push(tempAppearanceOpacityItem);
        creatorAppearanceMenu.AddItem(tempAppearanceOpacityItem);
    }

    creatorAppearanceMenu.AddItem(new UIMenuItem("Zuf\u00e4llig", "~r~Zuf\u00e4lliges Aussehen."));
    creatorAppearanceMenu.AddItem(new UIMenuItem("Zur\u00fccksetzen", "~r~Setzt das Aussehen zur\u00fcck."));

    creatorAppearanceMenu.ItemSelect.on((item, index) => {
        switch (item.Text) {
            case "Zuf\u00e4llig":
                for (let i = 0; i < Data.appearanceNames.length; i++) {
                    appearanceItems[i].Index = getRandomInt(0, mp.game.ped.getNumHeadOverlayValues(i) - 1);
                    appearanceOpacityItems[i].Index = getRandomInt(0, 100);
                    updateAppearance(i);
                }
                break;

            case "Zur\u00fccksetzen":
                resetAppearanceMenu();
                break;
        }
    });

    creatorAppearanceMenu.ListChange.on((item, listIndex) => {
        let idx = (creatorAppearanceMenu.CurrentSelection % 2 === 0) ? (creatorAppearanceMenu.CurrentSelection / 2) : Math.floor(creatorAppearanceMenu.CurrentSelection / 2);
        updateAppearance(idx);
    });

    creatorAppearanceMenu.ParentMenu = creatorMainMenu;
    creatorAppearanceMenu.Visible = false;
    creatorMenus.push(creatorAppearanceMenu);
    // CREATOR APPEARANCE END

    // CREATOR HAIR & COLORS
    let hairItem;
    let hairColorItem;
    let hairHighlightItem;
    let eyebrowColorItem;
    let beardColorItem;
    let eyeColorItem;
    let blushColorItem;
    let lipstickColorItem;
    let chestHairColorItem;

    creatorHairMenu = new Menu("Haar & Farben", "", new Point(50, 50), null, null);
    fillHairMenu();

    creatorHairMenu.ItemSelect.on((item, index) => {
        switch (item.Text) {
            case "Zuf\u00e4llig":
                hairItem.Index = getRandomInt(0, Data.hairList[currentGender].length - 1);
                hairColorItem.Index = getRandomInt(0, Data.maxHairColor);
                hairHighlightItem.Index = getRandomInt(0, Data.maxHairColor);
                eyebrowColorItem.Index = getRandomInt(0, Data.maxHairColor);
                beardColorItem.Index = getRandomInt(0, Data.maxHairColor);
                eyeColorItem.Index = getRandomInt(0, Data.maxEyeColor);
                blushColorItem.Index = getRandomInt(0, Data.maxBlushColor);
                lipstickColorItem.Index = getRandomInt(0, Data.maxLipstickColor);
                chestHairColorItem.Index = getRandomInt(0, Data.maxHairColor);
                updateHairAndColors();
                break;

            case "Zur\u00fccksetzen":
                resetHairAndColorsMenu();
                break;
        }
    });

    creatorHairMenu.ListChange.on((item, listIndex) => {
        if (item === hairItem) {
            let hairStyle = Data.hairList[currentGender][listIndex];
            localPlayer.setComponentVariation(2, hairStyle.ID, 0, 2);
        } else {
            switch (creatorHairMenu.CurrentSelection) {
                case 1: // hair color
                    localPlayer.setHairColor(listIndex, hairHighlightItem.Index);
                    break;

                case 2: // hair highlight color
                    localPlayer.setHairColor(hairColorItem.Index, listIndex);
                    break;

                case 3: // eyebrow color
                    localPlayer.setHeadOverlayColor(2, 1, listIndex, 0);
                    break;

                case 4: // facial hair color
                    localPlayer.setHeadOverlayColor(1, 1, listIndex, 0);
                    break;

                case 5: // eye color
                    localPlayer.setEyeColor(listIndex);
                    break;

                case 6: // blush color
                    localPlayer.setHeadOverlayColor(5, 2, listIndex, 0);
                    break;

                case 7: // lipstick color
                    localPlayer.setHeadOverlayColor(8, 2, listIndex, 0);
                    break;

                case 8: // chest hair color
                    localPlayer.setHeadOverlayColor(10, 1, listIndex, 0);
                    break;
            }
        }
    });

    creatorHairMenu.ParentMenu = creatorMainMenu;
    creatorHairMenu.Visible = false;
    creatorMenus.push(creatorHairMenu);
    // CREATOR HAIR & COLORS END

    // EVENTS
    mp.events.add("toggleCreator", () => {
        if (creatorCamera === undefined) {
            creatorCamera = mp.cameras.new("creatorCamera", creatorCoords.camera, new mp.Vector3(0, 0, 0), 45);
            creatorCamera.pointAtCoord(creatorCoords.cameraLookAt);
            creatorCamera.setActive(true);

            globalData.InMenu = true;

            creatorMainMenu.Visible = true;
            mp.gui.chat.show(false);
            mp.game.ui.displayRadar(false);
            mp.game.ui.displayHud(false);
            localPlayer.clearTasksImmediately();
            localPlayer.freezePosition(true);
            mp.game.cam.renderScriptCams(true, false, 0, true, false);
        }
    });

    mp.events.add("sendToServer", (characterData) => {
        mp.events.callRemote("creatorSave", characterData);
    });

    creatorMenus.forEach(menu => {
        menu.Visible = false;
    })
}