#include "documents.hpp"

#include <format>
#include <vector>

#include "log.hpp"
#include "path.hpp"
#include "strings.hpp"
#include "time.hpp"
#include "toast.hpp"
#include "util/imgui/layout.hpp"
#include "util/imgui/shortcut.hpp"

using namespace anm2ed::resource;
using namespace anm2ed::types;
using namespace anm2ed::util;

namespace anm2ed::imgui
{
  Options save_options_get(const Settings& settings)
  {
    Flags flags{};
    if (settings.fileIsSerializeGroups) flags |= SERIALIZE_GROUPS;
    if (settings.fileIsSerializeRegions) flags |= SERIALIZE_REGIONS;
    if (settings.fileIsSerializeSounds) flags |= SERIALIZE_SOUNDS;
    if (settings.fileIsKeepRedundantFrameRegionValues) flags |= SERIALIZE_REDUNDANT_FRAME_REGION_VALUES;
    if (settings.fileIsBakeSpecialInterpolatedFrames) flags |= SERIALIZE_BAKE_SPECIAL_INTERPOLATED_FRAMES;
    return {.flags = flags};
  }

  void Documents::update(Taskbar& taskbar, Manager& manager, Settings& settings, Resources& resources, bool& isQuitting)
  {
    auto viewport = ImGui::GetMainViewport();
    auto windowHeight = ImGui::GetFrameHeightWithSpacing();
    bool isLightTheme = settings.theme == theme::LIGHT;
    bool pushedStyle = false;

    ImGui::SetNextWindowViewport(viewport->ID);
    ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + taskbar.height));
    ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, windowHeight));
    if (isLightTheme)
    {
      ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_TitleBgActive));
      pushedStyle = true;
    }

    for (auto i = 0; i < (int)manager.documents.size(); ++i)
    {
      auto& document = manager.documents[i];
      auto isDirty = (document.is_dirty() || document.isForceDirty) && document.is_autosave_dirty();
      if (isDirty)
      {
        document.lastAutosaveTime += ImGui::GetIO().DeltaTime;
        if (document.lastAutosaveTime > time::SECOND_M)
        {
          auto options = save_options_get(settings);
          manager.command_push({i,
                                [options](Manager& manager, Document& document)
                                { manager.autosave(document, options); }});
        }
      }
    }

    if (ImGui::Begin("##Documents", nullptr,
                     ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
                         ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus |
                         ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoScrollbar |
                         ImGuiWindowFlags_NoScrollWithMouse))
    {
      height = ImGui::GetWindowSize().y;

      if (ImGui::BeginTabBar("##Documents Bar", ImGuiTabBarFlags_Reorderable))
      {
        auto documentsCount = (int)manager.documents.size();
        bool isCloseShortcut = shortcut(manager.chords[SHORTCUT_CLOSE], shortcut::GLOBAL) && !closePopup.is_open();
        int closeShortcutIndex =
            isCloseShortcut && manager.selected >= 0 && manager.selected < documentsCount ? manager.selected : -1;

        std::vector<int> closeIndices{};
        closeIndices.reserve(documentsCount);

        for (int i = 0; i < documentsCount; ++i)
        {
          auto& document = manager.documents[i];
          auto isDocumentDirty = document.is_dirty() || document.isForceDirty;
          auto isSpritesheetDirty = document.spritesheet_any_dirty();
          auto isDirty = isDocumentDirty || isSpritesheetDirty;

          if (!closePopup.is_open())
          {
            if (isQuitting)
              document.isOpen = false;
            else if (i == closeShortcutIndex)
              document.isOpen = false;
          }

          if (!closePopup.is_open() && !document.isOpen)
          {
            if (isDirty)
            {
              closePopup.open();
              closeDocumentIndex = i;
              document.isOpen = true;
            }
            else
            {
              closeIndices.push_back(i);
              continue;
            }
          }

          auto isRequested = i == manager.pendingSelected;
          auto font = isDocumentDirty ? font::ITALICS : font::REGULAR;
          auto filename = path::to_utf8(document.filename_get());
          auto string = isDocumentDirty ? std::vformat(localize.get(FORMAT_NOT_SAVED), std::make_format_args(filename))
                                        : filename;
          auto label = std::format("{}###Document{}", string, i);

          auto flags = isDocumentDirty ? ImGuiTabItemFlags_UnsavedDocument : 0;
          if (isRequested) flags |= ImGuiTabItemFlags_SetSelected;

          ImGui::PushFont(resources.fonts[font].get(), font::SIZE);
          if (ImGui::BeginTabItem(label.c_str(), &document.isOpen, flags))
          {
            if (manager.selected != i) manager.set(i);

            if (isRequested) manager.pendingSelected = -1;

            ImGui::EndTabItem();
          }
          auto pathUtf8 = path::to_utf8(document.path);
          ImGui::SetItemTooltip("%s", pathUtf8.c_str());

          ImGui::PopFont();
        }

        for (auto it = closeIndices.rbegin(); it != closeIndices.rend(); ++it)
        {
          if (closePopup.is_open() && closeDocumentIndex > *it) --closeDocumentIndex;
          auto index = *it;
          manager.command_push({.runManager = [index](Manager& manager) { manager.close(index); }});
        }

        ImGui::EndTabBar();
      }

      closePopup.trigger();

      if (ImGui::BeginPopupModal(closePopup.label(), &closePopup.isOpen, ImGuiWindowFlags_NoResize))
      {
        if (closeDocumentIndex >= 0 && closeDocumentIndex < (int)manager.documents.size())
        {
          auto& closeDocument = manager.documents[closeDocumentIndex];

          auto filename = path::to_utf8(closeDocument.filename_get());
          auto isDocumentDirty = closeDocument.is_dirty() || closeDocument.isForceDirty;
          auto isSpritesheetDirty = closeDocument.spritesheet_any_dirty();
          auto promptLabel =
              isDocumentDirty && isSpritesheetDirty
                  ? LABEL_DOCUMENT_AND_SPRITESHEETS_MODIFIED_PROMPT
                  : (isDocumentDirty ? LABEL_DOCUMENT_MODIFIED_PROMPT : LABEL_SPRITESHEETS_MODIFIED_PROMPT);
          auto prompt = std::vformat(localize.get(promptLabel), std::make_format_args(filename));
          ImGui::TextUnformatted(prompt.c_str());

          auto widgetSize = imgui::widget_size_with_row_get(3);

          auto close = [&]()
          {
            closeDocumentIndex = -1;
            closePopup.close();
          };

          shortcut(manager.chords[SHORTCUT_CONFIRM]);
          if (ImGui::Button(localize.get(BASIC_YES), widgetSize))
          {
            bool isSaved = true;
            if (isDocumentDirty) isSaved = taskbar.save_manual(manager, settings, closeDocumentIndex);

            if (!isSaved)
            {
              ImGui::EndPopup();
              return;
            }

            if (isSpritesheetDirty)
            {
              auto spritesheets = closeDocument.anm2.element_get(ElementType::SPRITESHEETS);
              if (spritesheets)
              {
                for (auto& spritesheet : spritesheets->children)
                {
                  if (spritesheet.type != ElementType::SPRITESHEET) continue;
                  auto id = spritesheet.id;
                  auto texture = closeDocument.texture_get(id);
                  if (!texture || !closeDocument.spritesheet_is_dirty(id)) continue;
                  auto pathString = path::to_utf8(spritesheet.path);
                  auto savePath = closeDocument.directory_get() / spritesheet.path;
                  path::ensure_directory(savePath.parent_path());
                  if (texture->write_png(savePath))
                  {
                    closeDocument.spritesheet_hash_set_saved(id);
                    toasts.push(
                        std::vformat(localize.get(TOAST_SAVE_SPRITESHEET), std::make_format_args(id, pathString)));
                    logger.info(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET, anm2ed::ENGLISH),
                                             std::make_format_args(id, pathString)));
                  }
                  else
                  {
                    toasts.push(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET_FAILED),
                                             std::make_format_args(id, pathString)));
                    logger.error(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET_FAILED, anm2ed::ENGLISH),
                                              std::make_format_args(id, pathString)));
                  }
                }
              }
            }
            auto index = closeDocumentIndex;
            manager.command_push({.runManager = [index](Manager& manager) { manager.close(index); }});
            close();
          }

          ImGui::SameLine();

          if (ImGui::Button(localize.get(BASIC_NO), widgetSize))
          {
            auto index = closeDocumentIndex;
            manager.command_push({.runManager =
                                      [index](Manager& manager)
                                      {
                                        manager.autosave_file_clear(index);
                                        manager.close(index);
                                      }});
            close();
          }

          ImGui::SameLine();

          shortcut(manager.chords[SHORTCUT_CANCEL]);
          if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize))
          {
            isQuitting = false;
            close();
          }
        }
        else
        {
          closeDocumentIndex = -1;
          closePopup.close();
        }

        ImGui::EndPopup();
      }
    }

    ImGui::End();
    if (pushedStyle) ImGui::PopStyleColor();

    if (manager.isAnm2DragDrop)
    {
      auto drag_drop_reset = [&]()
      {
        manager.isAnm2DragDrop = false;
        manager.anm2DragDropPaths.clear();
        manager.anm2DragDropPopup.close();
      };

      if (manager.anm2DragDropPaths.empty())
        drag_drop_reset();
      else
      {
        if (!manager.anm2DragDropPopup.is_open()) manager.anm2DragDropPopup.open();

        manager.anm2DragDropPopup.trigger();

        if (ImGui::BeginPopupContextWindow(manager.anm2DragDropPopup.label(), ImGuiPopupFlags_None))
        {
          auto document = manager.get();
          if (ImGui::MenuItem(manager.anm2DragDropPaths.size() > 1 ? localize.get(LABEL_DOCUMENTS_OPEN_MANY)
                                                                   : localize.get(LABEL_DOCUMENTS_OPEN_NEW)))
          {
            auto paths = manager.anm2DragDropPaths;
            manager.command_push({.runManager =
                                      [paths](Manager& manager)
                                      {
                                        for (auto& path : paths) manager.open(path);
                                      }});
            drag_drop_reset();
          }

          if (ImGui::MenuItem(localize.get(LABEL_DOCUMENTS_MERGE_INTO_CURRENT), nullptr, false,
                              document && !manager.anm2DragDropPaths.empty()))
          {
            if (document)
            {
              auto paths = manager.anm2DragDropPaths;
              manager.command_push({manager.selected,
                                    [paths](Manager&, Document& document)
                                    {
                                      document.snapshot(localize.get(EDIT_MERGE_ANM2));
                                      for (auto& path : paths) document.file_merge(path);
                                      document.change(Document::ALL);
                                    }});
              drag_drop_reset();
            }
          }

          ImGui::Separator();

          if (ImGui::MenuItem(localize.get(BASIC_CANCEL))) drag_drop_reset();

          manager.anm2DragDropPopup.end();
          ImGui::EndPopup();
        }
        else if (!ImGui::IsPopupOpen(manager.anm2DragDropPopup.label()))
          drag_drop_reset();
      }
    }
  }

}
