#include "tools.hpp"

#include "../../util/imgui.hpp"
#include "../../util/imgui/widget.hpp"

#include <cfloat>

using namespace game::util;
using namespace game::util::imgui;
using namespace game::resource::xml;

namespace game::state::play
{
  void Tools::update(Entity& character, Entity& cursor, World& world, World::Focus focus, Canvas& canvas,
                     bool isDisabled, float fadeAlpha)
  {
    static constexpr auto WIDTH_MULTIPLIER = 0.05f;
    static constexpr auto ALPHA_MIN = 0.0f;
    static constexpr auto ALPHA_MAX = 1.0f;
    static constexpr auto OUTER_STYLE_VAR_COUNT = 3;
    static constexpr auto BAR_STYLE_VAR_COUNT = 2;
    static constexpr auto PADDING_SIZE_MULTIPLIER = 2.0f;

    auto style = ImGui::GetStyle();
    auto& io = ImGui::GetIO();
    auto& schema = character.data.menuSchema;
    auto& strings = character.data.strings;

    if (isDisabled) isOpen = false;
    slide.update(isOpen, io.DeltaTime);
    if (fadeAlpha <= ALPHA_MIN) return;

    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
    ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
    ImGui::PushStyleVar(ImGuiStyleVar_Alpha, style.Alpha * fadeAlpha);

    auto windowSize = imgui::to_ivec2(ImGui::GetMainViewport()->Size);

    auto size =
        ImVec2(windowSize.x * WIDTH_MULTIPLIER, windowSize.y - style.WindowPadding.y * PADDING_SIZE_MULTIPLIER);
    auto targetX = 0;
    auto t = slide.value_get();
    auto eased = slide.eased_get();
    auto closedX = -size.x;
    auto posX = closedX + (targetX - closedX) * eased;
    auto pos = ImVec2(posX, style.WindowPadding.y);
    auto barSize =
        ImVec2(imgui::side_bar_width_get(), windowSize.y - style.WindowPadding.y * PADDING_SIZE_MULTIPLIER);
    auto barPos = ImVec2(pos.x + size.x, style.WindowPadding.y);
    auto flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
                 ImGuiWindowFlags_NoMove;
    if (isDisabled || fadeAlpha < ALPHA_MAX) flags |= ImGuiWindowFlags_NoInputs;

    if (slide.is_visible())
    {
      ImGui::SetNextWindowSize(size);
      ImGui::SetNextWindowPos(pos);

      if (ImGui::Begin("##Tools", nullptr, flags))
      {
        ImGui::BeginDisabled(isDisabled);
        auto buttonSize = imgui::to_imvec2(vec2(ImGui::GetContentRegionAvail().x));
        auto* iconsFont = schema.iconsFont.is_valid() ? schema.iconsFont.get() : nullptr;

        auto tool_button = [&](const char* id, const std::string& label, const std::string& iconGlyph,
                               const std::string& tooltip, bool isActive = false)
        {
          auto useIcon = iconsFont && !iconGlyph.empty();
          auto result = false;

          ImGui::PushID(id);
          if (isActive) ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered));

          result = WIDGET_FX(ImGui::Button(useIcon ? "##ToolIcon" : label.c_str(), buttonSize));

          if (isActive) ImGui::PopStyleColor();

          if (useIcon)
          {
            auto min = ImGui::GetItemRectMin();
            auto size = ImGui::GetItemRectSize();
            auto fontSize = size.y;
            auto textSize = iconsFont->CalcTextSizeA(fontSize, FLT_MAX, 0.0f, iconGlyph.c_str());
            auto pos = ImVec2(min.x + (size.x - textSize.x) * 0.5f, min.y + (size.y - textSize.y) * 0.5f);
            ImGui::GetWindowDrawList()->AddText(iconsFont, fontSize, pos, ImGui::GetColorU32(ImGuiCol_Text),
                                                iconGlyph.c_str());
          }

          if (!tooltip.empty()) ImGui::SetItemTooltip("%s", tooltip.c_str());
          ImGui::PopID();
          return result;
        };

        auto cursor_mode_button = [&](const std::string& name, int interactTypeID)
        {
          std::string iconGlyph{};
          for (auto* area : character.data.interact_area_types_get())
          {
            if (!area || area->typeString != name || area->iconGlyph.empty()) continue;
            iconGlyph = area->iconGlyph;
            break;
          }

          if (tool_button(name.c_str(), name, iconGlyph, iconsFont && !iconGlyph.empty() ? name : "",
                          cursor.interactTypeID == interactTypeID))
            cursor.interactTypeID = interactTypeID;
        };

        auto* root = schema.root();
        auto* home = root ? schema.child_get(*root, Schema::Element::HOME) : nullptr;
        auto homeIconGlyph = home ? home->iconGlyph : std::string{};
        if (tool_button("Home", strings.get(Strings::ToolsHomeButton), homeIconGlyph,
                        strings.get(Strings::ToolsHomeTooltip)))
          world.character_focus(character, canvas, focus);

        auto interactTypeNames = character.data.interact_type_names_get();
        for (int i = 0; i < (int)interactTypeNames.size(); i++)
          cursor_mode_button(interactTypeNames[i], i);
        ImGui::EndDisabled();
      }
      ImGui::End();
    }

    ImGui::SetNextWindowSize(barSize);
    ImGui::SetNextWindowPos(barPos);

    ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
    if (ImGui::Begin("##Tools Open Bar", nullptr, flags))
    {
      auto buttonSize = ImGui::GetContentRegionAvail();
      auto cursorPos = ImGui::GetCursorScreenPos();

      ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
      ImGui::BeginDisabled(isDisabled);
      auto result = WIDGET_FX(ImGui::Button("##ToolsToggle", buttonSize));

      if (!isDisabled && (t <= 0.0f || t >= 1.0f))
      {
        ImGui::SetItemTooltip("%s",
                              strings.get(isOpen ? Strings::ToolsCloseTooltip : Strings::ToolsOpenTooltip).c_str());
        if (result)
        {
          isOpen = !isOpen;
          if (isOpen)
            schema.root()->soundOpen.play();
          else
            schema.root()->soundClose.play();
        }
        if (!isOpen && t <= 0.0f && ImGui::IsItemHovered())
        {
          isOpen = true;
          schema.root()->soundOpen.play();
        }
      }
      ImGui::EndDisabled();
      ImGui::PopStyleVar();

      auto color = ImGui::GetStyleColorVec4(ImGuiCol_Text);
      if (isDisabled) color.w *= style.DisabledAlpha;
      auto direction = isOpen ? imgui::TriangleDirection::LEFT : imgui::TriangleDirection::RIGHT;
      imgui::triangle_draw(*ImGui::GetWindowDrawList(), cursorPos, buttonSize, direction, ImGui::GetColorU32(color));
    }
    ImGui::End();
    ImGui::PopStyleVar(BAR_STYLE_VAR_COUNT);

    ImGui::PopStyleVar(OUTER_STYLE_VAR_COUNT);
  }
}
