// MassBuilderSaveTool
// Copyright (C) 2021-2024 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <imgui_internal.h>

#include "../FontAwesome/IconsFontAwesome5.h"
#include "../GameData/ArmourSets.h"
#include "../GameData/StyleNames.h"

#include "Application.h"

namespace mbst {

void
Application::drawArmour() {
    if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) {
        return;
    }

    constexpr static Containers::StringView slot_labels[] = {
        #define c(enumerator, strenum, name) name,
        #include "../Maps/ArmourSlots.hpp"
        #undef c
    };

    auto labels_view = arrayView(slot_labels);

    const static float footer_height_to_reserve = ImGui::GetFrameHeightWithSpacing();

    ImGui::BeginGroup();

    if(ImGui::BeginTable("##SlotsTable", 1,
                         ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH,
                         {ImGui::GetContentRegionAvail().x * 0.15f, -footer_height_to_reserve}))
    {
        ImGui::TableSetupColumn("##Slots", ImGuiTableColumnFlags_WidthStretch);

        for(std::size_t i = 0; i < labels_view.size(); i++) {
            ImGui::TableNextRow();
            ImGui::TableNextColumn();
            if(ImGui::Selectable(labels_view[i].data(), _selectedArmourSlot && (*_selectedArmourSlot) == i,
                                 ImGuiSelectableFlags_SpanAvailWidth))
            {
                _selectedArmourSlot = i;
            }
        }

        ImGui::EndTable();
    }

    if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) {
        _currentMass->getArmourParts();
    }

    ImGui::EndGroup();

    ImGui::SameLine();

    if(!_selectedArmourSlot) {
        ImGui::TextUnformatted("No selected armour slot.");
        return;
    }

    auto& part = _currentMass->armourParts()[*_selectedArmourSlot];

    ImGui::BeginGroup();

    if(ImGui::BeginChild("##ArmourEditor", {0.0f, -footer_height_to_reserve})) {

        ImGui::SeparatorText("Part");

        if(GameData::armour_sets.find(part.id) != GameData::armour_sets.cend()) {
            ImGui::Text("Set name: %s", GameData::armour_sets.at(part.id).name.data());
        }
        else {
            ImGui::Text("Set ID: %d", part.id);
        }

        ImGui::SameLine();

        if(ImGui::SmallButton("Change")) {
            ImGui::OpenPopup("##ArmourPartPopup");
        }
        if(ImGui::BeginPopup("##ArmourPartPopup")) {
            if(ImGui::BeginListBox("##ChangePart")) {
                for(const auto& [id, set] : GameData::armour_sets) {
                    if((part.slot == GameObjects::ArmourPart::Slot::Neck && !set.neck_compatible) ||
                       (id == -2 &&
                           !(part.slot == GameObjects::ArmourPart::Slot::LeftFrontSkirt ||
                             part.slot == GameObjects::ArmourPart::Slot::RightFrontSkirt ||
                             part.slot == GameObjects::ArmourPart::Slot::LeftSideSkirt ||
                             part.slot == GameObjects::ArmourPart::Slot::RightSideSkirt ||
                             part.slot == GameObjects::ArmourPart::Slot::LeftBackSkirt ||
                             part.slot == GameObjects::ArmourPart::Slot::RightBackSkirt ||
                             part.slot == GameObjects::ArmourPart::Slot::LeftAnkle ||
                             part.slot == GameObjects::ArmourPart::Slot::RightAnkle)
                       )
                      )
                    {
                        continue;
                    }

                    if(ImGui::Selectable(set.name.data(), id == part.id,
                                         ImGuiSelectableFlags_SpanAvailWidth))
                    {
                        part.id = id;
                    }
                }
                ImGui::EndListBox();
            }

            ImGui::EndPopup();
        }

        ImGui::SeparatorText("Styles");

        for(std::int32_t i = 0; i < 4; i++) {
            drawAlignedText("Slot %d:", i + 1);

            ImGui::SameLine();

            ImGui::PushID(i);
            if(ImGui::BeginCombo("##Style",
                                 getStyleName(part.styles[i], _currentMass->armourCustomStyles()).data()))
            {
                for(const auto& style : GameData::builtin_style_names) {
                    if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()).data(),
                                         part.styles[i] == style.first))
                    {
                        part.styles[i] = style.first;
                    }
                }

                ImGui::EndCombo();
            }
            ImGui::PopID();
        }

        ImGui::SeparatorText("Decals");

        constexpr static float selectable_width = 25.0f;

        drawAlignedText("Showing/editing decal:");
        ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f});
        for(std::uint32_t i = 0; i < part.decals.size(); i++) {
            ImGui::SameLine();
            if(ImGui::Selectable(std::to_string(i + 1).c_str(), _selectedArmourDecals[*_selectedArmourSlot] == int(i),
                                 ImGuiSelectableFlags_None, {selectable_width, 0.0f}))
            {
                _selectedArmourDecals[*_selectedArmourSlot] = int(i);
            }
            if(i != part.decals.size() - 1) {
                ImGui::SameLine();
                ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
            }
        }
        ImGui::PopStyleVar();
        drawDecalEditor(part.decals[_selectedArmourDecals[*_selectedArmourSlot]]);

        if(!part.accessories.isEmpty()) {
            ImGui::SeparatorText("Accessories");

            drawAlignedText("Showing/editing accessory:");
            ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f});
            for(std::uint32_t i = 0; i < part.accessories.size(); i++) {
                ImGui::SameLine();
                if(ImGui::Selectable((std::string{} + char(i + 65)).c_str(),
                                     _selectedArmourAccessories[*_selectedArmourSlot] == int(i),
                                     ImGuiSelectableFlags_None, {selectable_width, 0.0f}))
                {
                    _selectedArmourAccessories[*_selectedArmourSlot] = int(i);
                }
                if(i != part.accessories.size() - 1) {
                    ImGui::SameLine();
                    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
                }
            }
            ImGui::PopStyleVar();
            drawAccessoryEditor(part.accessories[_selectedArmourAccessories[*_selectedArmourSlot]],
                                _currentMass->armourCustomStyles());
        }
    }

    ImGui::EndChild();

    if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
        _modifiedBySaveTool = true;
        if(!_currentMass->writeArmourPart(part.slot)) {
            _modifiedBySaveTool = false;
            _queue.addToast(Toast::Type::Error, _currentMass->lastError());
        }
    }

    ImGui::EndGroup();
}

void
Application::drawBLAttachment() {
    if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) {
        return;
    }

    drawAlignedText("Attachment style:"_s);
    ImGui::SameLine();
    if(ImGui::RadioButton("Active one",
                          _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOne))
    {
        _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOne;
    }
    ImGui::SameLine();
    if(ImGui::RadioButton("Active one per slot",
                          _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot))
    {
        _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot;
    }
    ImGui::SameLine();
    if(ImGui::RadioButton("All equipped",
                          _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::AllEquipped))
    {
        _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot;
    }

    ImGui::Separator();

    constexpr static float selectable_width = 25.0f;
    drawAlignedText("Launcher slot:");
    ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f});
    for(auto i = 0u; i < _currentMass->bulletLauncherAttachments().size(); i++) {
        ImGui::SameLine();
        if(ImGui::Selectable(std::to_string(i).c_str(), _selectedBLPlacement == i, ImGuiSelectableFlags_None,
                             {selectable_width, 0.0f}))
        {
            _selectedBLPlacement = i;
        }
        if(i != _currentMass->bulletLauncherAttachments().size() - 1) {
            ImGui::SameLine();
            ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
        }
    }
    ImGui::PopStyleVar();

    auto& placement = _currentMass->bulletLauncherAttachments()[_selectedBLPlacement];

    static const Containers::StringView socket_labels[] = {
            #define c(enumerator, enumstr, name) name,
            #include "../Maps/BulletLauncherSockets.hpp"
            #undef c
    };

    drawAlignedText("Socket:");
    ImGui::SameLine();
    if(ImGui::BeginCombo("##Socket", socket_labels[std::uint32_t(placement.socket)].data())) {
        for(std::uint32_t i = 0; i < (sizeof(socket_labels) / sizeof(socket_labels[0])); i++) {
            if(ImGui::Selectable(socket_labels[i].data(), i == std::uint32_t(placement.socket), ImGuiSelectableFlags_SpanAvailWidth)) {
                placement.socket = static_cast<GameObjects::BulletLauncherAttachment::Socket>(i);
            }
        }
        ImGui::EndCombo();
    }

    if(placement.socket != GameObjects::BulletLauncherAttachment::Socket::Auto) {
        ImGui::BeginGroup();
        drawAlignedText("Relative position:");
        drawAlignedText("Offset position:");
        drawAlignedText("Relative rotation:");
        drawAlignedText("Offset rotation:");
        drawAlignedText("Scale:");
        ImGui::EndGroup();

        ImGui::SameLine();

        ImGui::BeginGroup();
        std::visit(
            [this](auto& arg){
                using T = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<T, Vector3>) {
                    drawVector3Drag("##RelativePosition", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, "X: %.3f",
                                    "Y: %.3f", "Z: %.3f");
                }
                else if constexpr (std::is_same_v<T, Vector3d>) {
                    drawVector3dDrag("##RelativePosition", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX},
                                     "X: %.3f", "Y: %.3f", "Z: %.3f");
                }
            }, placement.relativeLocation
        );

        std::visit(
            [this](auto& arg){
                using T = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<T, Vector3>) {
                    drawVector3Slider("##OffsetPosition", arg, Vector3{-500.0f}, Vector3{500.0f}, "X: %.3f", "Y: %.3f",
                                      "Z: %.3f");
                }
                else if constexpr (std::is_same_v<T, Vector3d>) {
                    drawVector3dSlider("##OffsetPosition", arg, Vector3d{-500.0}, Vector3d{500.0}, "X: %.3f",
                                       "Y: %.3f", "Z: %.3f");
                }
            }, placement.offsetLocation
        );
        ImGui::SameLine();
        drawHelpMarker("+/-500.0 = +/-250 in-game");

        std::visit(
            [this](auto& arg){
                using T = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<T, Vector3>) {
                    drawVector3Drag("##RelativeRotation", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX},
                                    "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f");
                }
                else if constexpr (std::is_same_v<T, Vector3d>) {
                    drawVector3dDrag("##RelativeRotation", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX},
                                     "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f");
                }
            }, placement.relativeRotation
        );

        std::visit(
            [this](auto& arg){
                using T = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<T, Vector3>) {
                    drawVector3Slider("##OffsetRotation", arg, Vector3{-30.0f, -30.0f, -180.0f},
                                      Vector3{30.0f, 30.0f, 180.0f}, "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f");
                }
                else if constexpr (std::is_same_v<T, Vector3d>) {
                    drawVector3dSlider("##OffsetRotation", arg, Vector3d{-30.0, -30.0, -180.0},
                                       Vector3d{30.0, 30.0, 180.0}, "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f");
                }
            }, placement.offsetRotation
        );

        std::visit(
            [this](auto& arg){
                using T = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<T, Vector3>) {
                    drawVector3Slider("##Scale", arg, Vector3{0.5f}, Vector3{1.5f}, "X: %.3f", "Y: %.3f", "Z: %.3f");
                }
                else if constexpr (std::is_same_v<T, Vector3d>) {
                    drawVector3dSlider("##Scale", arg, Vector3d{0.5}, Vector3d{1.5}, "X: %.3f", "Y: %.3f", "Z: %.3f");
                }
            }, placement.relativeScale
        );
        ImGui::SameLine();
        drawHelpMarker("0.5 = 50 in-game\n1.5 = 150 in-game");
        ImGui::EndGroup();
    }

    _modifiedBySaveTool = true;
    if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
        _modifiedBySaveTool = true;
        if(!_currentMass->writeBulletLauncherAttachments()) {
            _modifiedBySaveTool = false;
            _queue.addToast(Toast::Type::Error, _currentMass->lastError());
        }
    }
}

void
Application::drawCustomArmourStyles() {
    if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) {
        return;
    }

    if(!ImGui::BeginChild("##ArmourStyles")) {
        ImGui::EndChild();
        return;
    }

    ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.");

    for(std::uint32_t i = 0; i < _currentMass->armourCustomStyles().size(); i++) {
        ImGui::PushID(int(i));
        auto result = drawCustomStyle(_currentMass->armourCustomStyles()[i]);
        switch(result) {
            case DCS_ResetStyle:
                _currentMass->getArmourCustomStyles();
                break;
            case DCS_Save:
                _modifiedBySaveTool = true;
                if(!_currentMass->writeArmourCustomStyle(i)) {
                    _modifiedBySaveTool = false;
                    _queue.addToast(Toast::Type::Error, _currentMass->lastError());
                }
                break;
            default:
                break;
        }
        ImGui::PopID();
    }

    ImGui::EndChild();
}

}