#include "content.hpp"

#include <filesystem>
#include <oneapi/tbb/parallel_for.h>
#include "logger/logger.hpp"

#include "buffer/buffer.hpp"

namespace rrr 
{
  void content::set_pwd(std::filesystem::path p)
  {
    PWD = p;
  }

  void content::fill()
  {
    nav.fill(PWD);
    his.fill(PWD);
    prev.fill(nav.data.at(navigation_cursor_position).path);
  }

  files* content::get(TYPE_WIN type)
  {
    switch (type) 
    {
      case TYPE_WIN::HISTORY:
        return &his.data;
        break;
      case TYPE_WIN::NAVIGATION:
        return &nav.data;
        break;
      case TYPE_WIN::PREVIEW:
        return &prev.data;
        break;
      default:
        std::terminate();
        break;
    }
  }

  file content::get_selected_file(TYPE_WIN type)
  {
    file f;
    switch (type) 
    {
      case TYPE_WIN::HISTORY:
        try
        {
          set_history_cursor_position();
          f = his.data.at(history_cursor_position);
        }
        catch(...) { hack::error()("Dont set history"); }
        break;
      case TYPE_WIN::NAVIGATION:
        try
        {
          f = nav.data.at(navigation_cursor_position);
        }
        catch(...) { hack::error()("Dont set navigation"); }
        break;
      case TYPE_WIN::PREVIEW:
        try
        {
          set_preview_cursor_position();
          f = prev.data.at(preview_cursor_position);
        }
        catch(...) { hack::error()("Dont set preview"); }
        break;
    }

    return f;
  }

  void content::set_history_cursor_position()
  {
    history_cursor_position = 0;
    tbb::parallel_for(tbb::blocked_range<int>(0, his.data.size()), [&](tbb::blocked_range<int> r)
    {
      for (int i = r.begin(); i < r.end(); ++i)
        if (his.data.at(i).path == PWD)
          history_cursor_position = i;
    });
  }

  void content::set_preview_cursor_position()
  {
    if (std::filesystem::is_empty(PWD))
      return;

    auto pwd = nav.data.at(navigation_cursor_position).path;
    preview_cursor_position = 0;

    if (buffers::get_instance().path_buffer.contains(pwd))
    {
      tbb::parallel_for(tbb::blocked_range<int>(0, prev.data.size()), [&](tbb::blocked_range<int> r)
      {
        for (int i = r.begin(); i < r.end(); ++i)
          if (prev.data.at(i).path == buffers::get_instance().path_buffer[pwd].path)
            preview_cursor_position = i;
      });
    }
  }

  void content::increment_position(int step)
  {
    navigation_cursor_position += step;
    check_cursor_position();
    
    // в зависимости от того пустаая ли директория
    // например после удаления последнего файла
    if (std::filesystem::is_empty(PWD))
      prev.data.clear();
    else
      prev.fill(nav.data.at(navigation_cursor_position).path);
  }

  void content::check_cursor_position()
  {
    if (navigation_cursor_position >= (int)nav.data.size()) navigation_cursor_position = (int)nav.data.size() - 1;
    else if (navigation_cursor_position < 0) navigation_cursor_position = 0;
  }

  void content::navigation_right()
  {
    if (std::filesystem::is_empty(PWD)) return;

    // ставим новый pwd и заполняем навигацию и историю
    PWD = PWD / nav.data.at(navigation_cursor_position).path.filename();

    nav.fill(PWD);
    his.fill(PWD);

    // смотрим есть ли в этом pwd какой-то файл в буфере
    // проще говоря, были ли мы тут
    // и если были, то устанавливаем курсор
    if (buffers::get_instance().path_buffer.contains(PWD))
    {
      auto f = buffers::get_instance().path_buffer[PWD];
      tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
      {
        for (int i = r.begin(); i < r.end(); ++i)
          if (nav.data.at(i).path == f.path)
            navigation_cursor_position = i;
      });
    }
    else
    {
      navigation_cursor_position = 0;
    }

    // и исходя из позиции курсора заполняем окно предварительного просмотра
    // в зависимости от того пустаая ли она или неt
    if (std::filesystem::is_empty(PWD))
      prev.data.clear();
    else
      prev.fill(nav.data.at(navigation_cursor_position).path);
  }

  void content::navigation_left()
  {
    // буфер заполняется только когда отсюда уходишь
    // типа я тут был и если тут что-то есть
    if (!std::filesystem::is_empty(PWD))
      buffers::get_instance().path_buffer[PWD] = nav.data.at(navigation_cursor_position);

    auto from = PWD;

    nav.fill(PWD.parent_path());
    his.fill(PWD.parent_path());
    prev.fill(PWD);

    // ставим новый pwd и заполняем навигацию и историю
    PWD = PWD.parent_path();

    // тут всегда есть место откуда ты пришел
    // т.е. нет возмолжности придти неоткуда
    // соответственно находим это место
    tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
    {
      for (int i = r.begin(); i < r.end(); ++i)
        if (nav.data.at(i).path == from)
          navigation_cursor_position = i;
    });
  }

  void content::create_file(std::string filename)
  {
    std::string cmd;

    if (filename.find("/") != std::string::npos)
    {
      if (filename.at(filename.size() - 1) == '/') cmd = "mkdir -p " + std::string(PWD / filename);
      else cmd = "mkdir -p " + std::string(PWD / std::filesystem::path(filename).parent_path()) 
        + " && touch " + std::string(PWD / filename);
    }
    else
    {
      cmd = "touch " + std::string(PWD / filename);
    }

    hack::utils::unix_cmd(cmd);
    nav.fill(PWD);
    check_cursor_position();
  }

  void content::rename_file(std::filesystem::path old_name, std::filesystem::path new_name)
  {
    if (std::filesystem::exists(new_name)) 
    {
      new_name = new_name.filename().string() + "_" + 
        std::to_string(
          std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()
        );
      new_name = old_name.parent_path() / std::filesystem::path(new_name);
    }

    hack::utils::unix_cmd("mv " + old_name.string() + " " + new_name.string());
    nav.fill(PWD);

    tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
    {
      for (int i = r.begin(); i < r.end(); ++i)
        if (nav.data.at(i).path == new_name)
          navigation_cursor_position = i;
    });

    check_cursor_position();
  }

  void content::paste(std::filesystem::path f)
  {
    std::filesystem::path target;
    if (std::filesystem::exists(PWD / f.filename())) 
    {
      target = f.filename().string() + "_" + 
        std::to_string(
          std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()
        );
      target = f.parent_path() / std::filesystem::path(target);
    }
    else
    {
      target = PWD / f.filename();
    }

    std::string cmd = std::filesystem::is_directory(f) ? "cp -R " : "cp ";
    hack::utils::unix_cmd(cmd + f.string() + " " + target.string());
    nav.fill(PWD);

    check_cursor_position();
  }

  void content::delete_file(file f)
  {
    std::string cmd = "delete " + f.path.string();
    hack::utils::unix_cmd(cmd);
    nav.fill(PWD);
    increment_position(-1);
  }

  void content::cut(std::filesystem::path f)
  {
    std::filesystem::path target;
    if (std::filesystem::exists(PWD / f.filename())) 
    {
      target = f.filename().string() + "_" + 
        std::to_string(
          std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()
        );
      target = f.parent_path() / std::filesystem::path(target);
    }
    else
    {
      target = PWD / f.filename();
    }

    std::string cmd = "mv ";
    hack::utils::unix_cmd(cmd + f.string() + " " + target.string());
    nav.fill(PWD);

    tbb::parallel_for(tbb::blocked_range<int>(0, nav.data.size()), [&](tbb::blocked_range<int> r)
    {
      for (int i = r.begin(); i < r.end(); ++i)
        if (nav.data.at(i).path == target)
        {
          navigation_cursor_position = i;
        }
    });

    check_cursor_position();
  }
}
