#include "navigation.hpp"

#include <algorithm>

#include "try_engine/event/event_classificator.hpp"
#include "utils/types.hpp"
#include "buffer/buffer.hpp"

#include "logger/logger.hpp"

namespace rrr::layers::gui
{
  void navigation::on_attach() 
  {
    BASE_WINDOW_FLAGS();

    data = cnt->get(TYPE_WIN::NAVIGATION);
    selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
  }

  void navigation::on_detach()
  {

  }

  void navigation::gui_render()
  {
    if (!show) return;

    ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y));
    ImGui::SetNextWindowSize(ImVec2(width, height));

    ImGuiStyle& st = ImGui::GetStyle();
    st.Colors[ImGuiCol_WindowBg] = func::get_IMGUI_color<ImVec4>(33.f, 36.f, 46.f);

    BEGIN_IMGUI_WIN();

    auto pos = ImGui::GetCursorPos();
    pos.x += 17.f;
    pos.y += 9.f; // небольшой отступ сверху
    ImGui::SetCursorPosY(pos.y);

    auto begin = data->begin() + delta;
    auto end = data->end();

    for (files::iterator it = begin; it != end; ++it)
    {
      auto item = *it;
      ImGui::PushID(item.id);

      push_style(item);

      if (selected_file.path == item.path)
      {
        if (selected_file.is_hidden)
          TR_PUSH_FONT(SEMI_BOLD_ITALIC, 18);
        else
          TR_PUSH_FONT(SEMI_BOLD, 18);

        ImGui::TextUnformatted(">");
        ImGui::SameLine(22.f);
        // HERE 
        // нужно использовать std::distance
        current_position = it - data->begin();
      }
      else
      {
        ImGui::SetCursorPosX(pos.x);
        if (item.is_hidden)
          TR_PUSH_FONT(MEDIUM_ITALIC, 18);
        else
          TR_PUSH_FONT(MEDIUM, 18);
      }

      ImGui::TextUnformatted(get_file_content(item).data());

      pop_style(item);
    }

    END_IMGUI_WIN();
  }

  void navigation::push_style(file& f)
  {
    switch (f.type) 
    {
      case file_type::DIR:
        ImGui::PushStyleColor(ImGuiCol_Text, dir_color);
        break;
      default: // когда просто файл
        ImGui::PushStyleColor(ImGuiCol_Text, file_color);
        break;
    }

    if (f.is_link)
      ImGui::PushStyleColor(ImGuiCol_Text, link_color);
  }

  void navigation::pop_style(file& f)
  {
    TR_POP_FONT();
    ImGui::PopStyleColor();
    ImGui::PopID();

    if (f.is_link)
      ImGui::PopStyleColor();
  }

  std::string navigation::get_file_content(file& f)
  {
    auto file_content = f.path.filename().string();

    if (!buffers::get_instance().single_copy_buffer.empty())
      if (f.path == buffers::get_instance().single_copy_buffer[1].path)
        file_content = "* " + file_content;

    if (!buffers::get_instance().single_cut_buffer.empty())
      if (f.path == buffers::get_instance().single_cut_buffer[1].path)
        file_content = "** " + file_content;

    if (f.is_link)
      file_content += " ->";

    return file_content;
  }

  void navigation::on_event(system_event& e)
  {
    if (e.get_name() == try_engine::system_event::classificator::WINDOW_RESIZE())
      resize();

    if (e.get_name() == try_engine::system_event::classificator::KEY_PRESSED())
      pressed(e);

    if (e.get_name() == try_engine::system_event::classificator::KEY_RELEASED())
      released(e);
  }

  void navigation::on_event(std::any event_type, std::any value)
  {
    auto et = std::any_cast<types::event_type>(event_type);

    switch (et)
    {
      case types::event_type::FREEZE_BROWSER_ACTION:
        freeze = true;
        break;
      case types::event_type::UNFREEZE_BROWSER_ACTION:
        freeze = false;
        break;
      case types::event_type::DELETE_CURRENT_FILE:
        {
          freeze = false;
          cnt->delete_file(selected_file);
          selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
        }
        break;
      case types::event_type::CREATE_FILE:
        {
          freeze = false;
          auto filename = std::any_cast<std::string>(value);
          cnt->create_file(filename);
          selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
        }
        break;
      case types::event_type::RENAME_FILE:
        {
          freeze = false;
          auto filename = std::any_cast<std::filesystem::path>(value);
          cnt->rename_file(selected_file.path, filename);
          selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
        }
        break;
      default:
        break;
    }
  }

  void navigation::on_update(time t)
  {

  }

  void navigation::resize()
  {
    height = try_engine::application::get()->get_window()->height();
    width = try_engine::application::get()->get_window()->width() / 3.f - padding_between_window;
    pos_x = try_engine::application::get()->get_window()->width() / 3.f + padding_between_window / 2.f;
  }

  void navigation::pressed(system_event& e)
  {
    if (freeze) return;

    auto key = static_cast<try_engine::system_event::key_pressed_event&>(e);

    if (key.get_keycode() == try_engine::key::LEFTSHIFT || key.get_keycode() == try_engine::key::RIGHTSHIFT)
      shift = true;

    if (shift)
    {
      // удаление
      if (shift && key.get_keycode() == try_engine::key::D)
      {
        em->execute(types::event_type::SHOW_DELETE_ONE_FILE_DIALOG, selected_file);
        freeze = true;
      }

      // создание файла/директории
      if (shift && key.get_keycode() == try_engine::key::A)
      {
        em->execute(types::event_type::SHOW_CREATE_FILE_DIALOG, nullptr);
        freeze = true;
      }

      // переименование файла/директории
      if (shift && key.get_keycode() == try_engine::key::R)
      {
        em->execute(types::event_type::SHOW_RENAME_FILE_DIALOG, selected_file);
        freeze = true;
      }

      // перемещение в конец списка
      if (shift && key.get_keycode() == try_engine::key::G)
      {
        cnt->increment_position(data->size());
        selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
        em->execute(types::event_type::NAVIGATION_DOWN, nullptr);
        set_delta(MOVE_DIRECTION::DOWN);
        set_scroll();
      }

      // вставка из single_buffer
      if (shift && key.get_keycode() == try_engine::key::P)
      {
        paste_from_copy();
        paste_from_cut();
      }

      return;
    }

    if (key.get_keycode() == try_engine::key::J)
    {
      cnt->increment_position(STEP_DOWN);
      selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
      em->execute(types::event_type::NAVIGATION_DOWN, nullptr);
      set_delta(MOVE_DIRECTION::DOWN);
    }

    if (key.get_keycode() == try_engine::key::K)
    {
      cnt->increment_position(STEP_UP);
      selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
      em->execute(types::event_type::NAVIGATION_UP, nullptr);
      set_delta(MOVE_DIRECTION::UP);
    }

    if (key.get_keycode() == try_engine::key::H)
    {
      cnt->navigation_left();
      data = cnt->get(TYPE_WIN::NAVIGATION);
      selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
      em->execute(types::event_type::NAVIGATION_LEFT, nullptr);
      set_scroll();
    }

    if (key.get_keycode() == try_engine::key::L)
    {
      cnt->navigation_right();
      data = cnt->get(TYPE_WIN::NAVIGATION);
      selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
      em->execute(types::event_type::NAVIGATION_RIGHT, nullptr);
      set_scroll();
    }

    // двойное нажатие перемещение в начало списка
    if (key.get_keycode() == try_engine::key::G)
    {
      ++g_coutn;
      if (g_coutn != 2) return;
      cnt->increment_position(-data->size());
      selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
      em->execute(types::event_type::NAVIGATION_UP, nullptr);
      set_delta(MOVE_DIRECTION::UP);
      set_scroll();
      g_coutn = 0;
    }

    // копирование/добавление в буфер одного файла вместо другого
    // когда в буфере что-то есть и мы на томже файле жмем кнопку еще раз
    // то происходит очищение буфера
    if (key.get_keycode() == try_engine::key::C)
    {
      // проверяем а может это файл отмечен на вырезание
      if (!buffers::get_instance().single_cut_buffer.empty())
        buffers::get_instance().single_cut_buffer.clear();

      if (buffers::get_instance().single_copy_buffer.empty())
        buffers::get_instance().single_copy_buffer[1] = selected_file;
      else
        if (selected_file.path == buffers::get_instance().single_copy_buffer[1].path)
          buffers::get_instance().single_copy_buffer.clear();
        else
          buffers::get_instance().single_copy_buffer[1] = selected_file;
    }

    if (key.get_keycode() == try_engine::key::X)
    {
      // проверяем а может это файл отмечен на копирование
      if (!buffers::get_instance().single_copy_buffer.empty())
        buffers::get_instance().single_copy_buffer.clear();

      if (buffers::get_instance().single_cut_buffer.empty())
        buffers::get_instance().single_cut_buffer[1] = selected_file;
      else
        if (selected_file.path == buffers::get_instance().single_cut_buffer[1].path)
          buffers::get_instance().single_cut_buffer.clear();
        else
          buffers::get_instance().single_cut_buffer[1] = selected_file;
    }

    // очистка
    if (key.get_keycode() == try_engine::key::ESCAPE)
    {
      buffers::get_instance().single_copy_buffer.clear();
      buffers::get_instance().single_cut_buffer.clear();
    }

    // помощь
    if (key.get_keycode() == try_engine::key::F1)
    {
      em->execute(types::event_type::SHOW_HELP_DIALOG, nullptr);
      freeze = true;
    }

    detect_img();
  }
  
  void navigation::released(system_event& e)
  {
    auto key = static_cast<try_engine::system_event::key_pressed_event&>(e);

    if (key.get_keycode() == try_engine::key::LEFTSHIFT || key.get_keycode() == try_engine::key::RIGHTSHIFT)
      shift = false;
  }

  void navigation::set_delta(MOVE_DIRECTION mvd)
  {
    auto row_size = 26.3f; // высота вы пикселях одной строки списка
    int h = height / row_size;  // высота в строках списка всего столбца экрана
    auto big_content = height < data->size() * row_size;

    int size = data->size();

    if (big_content)
    {
      if (mvd == MOVE_DIRECTION::DOWN)
      {
        ++cursor_position;

        // на самом деле получаем его выше, но делаем такой трюк 
        // т.к. происходит задержка на один ход нажатия клавиши
        ++current_position; 
        if (current_position >= size) current_position = size - 1;

        bool is_end = size - current_position <= 10;
        if (cursor_position >= h - 10 &&  !is_end)
        {
          ++delta;
          cursor_position = h - 10;
        }

        if (cursor_position >= h) cursor_position = h;
      }

      if (mvd == MOVE_DIRECTION::UP)
      {
        --cursor_position;
        --current_position;

        if (cursor_position <= 10) 
          --delta;

        if (cursor_position < 10 && current_position > 12)
          cursor_position = 10;
        
        if (delta < 0) 
        {
          delta = 0;
          cursor_position = 0;
        }
      }
    }
  }

  void navigation::set_scroll()
  {
    delta = 0;
    current_position = 0;
    cursor_position = 0;

    auto row_size = 26.3f; // высота вы пикселях одной строки списка
    auto big_content = height < data->size() * row_size;

    if (big_content)
    {
      // как бы прокручиваем с начала списка
      for (const auto& f : *data)
      {
        if (f.path != selected_file.path)
        {
          set_delta(MOVE_DIRECTION::DOWN);
        }
        else
        {
          set_delta(MOVE_DIRECTION::DOWN);
          break;
        }
      }
    }
  }

  void navigation::paste_from_copy()
  {
    if (buffers::get_instance().single_copy_buffer.empty()) return;
    cnt->paste(buffers::get_instance().single_copy_buffer[1].path);
    buffers::get_instance().single_copy_buffer.clear();
  }

  void navigation::paste_from_cut()
  {
    if (buffers::get_instance().single_cut_buffer.empty()) return;
    cnt->cut(buffers::get_instance().single_cut_buffer[1].path);
    buffers::get_instance().single_cut_buffer.clear();
    selected_file = cnt->get_selected_file(TYPE_WIN::NAVIGATION);
  }

  void navigation::detect_img()
  {
    if (selected_file.path.extension() == ".jpg" ||
        selected_file.path.extension() == ".jpeg" ||
        selected_file.path.extension() == ".png")
      em->execute(types::event_type::SHOW_IMG, selected_file.path);
  }
}
