#pragma once

#include "utils/utils.hpp"
#include <filesystem>
#include <vector>
#include <algorithm>

#include "logger/logger.hpp"

namespace rrr
{
  // NONE - просто выводится текст. когда пупка пуста и нет контента вообще
  // FILE - файл
  // DIR - дирректория
  enum class file_type
  {
    NONE, FILE, DIR
  };

  // класс-тип контента
  // т.е. контентом является все, что может поместиться в терминал
  // и это все называется файлом. ддиректория - это файл, файл - это файл и т.п.
  // просто каждая сущность имеет свой тип см. выше
  class file : public hack::utils::counter<int>
  {
    public: 
      file() = default;
      file(std::filesystem::path, file_type, bool, bool);
      file(file&&);
      file(const file&);

    public:
      file& operator=(const file&);
      bool operator<(const file&) const;
      bool operator==(const file&) const;
      friend std::ostream& operator<<(std::ostream&, const file&);

    public:
      int id;
      std::filesystem::path path;
      file_type type;
      bool is_link = false;
      bool is_hidden = false;
      bool is_mark = false;
      bool is_single_buffer = false;
  };
}

namespace rrr::file_utils
{
  using files = std::vector<file>;

  inline bool is_hidden(const std::filesystem::path &p)
  {
    std::filesystem::path::string_type name = p.filename();
    if(name != ".." && name != "." && name[0] == '.')
       return true;
    return false;
  }

  struct filesystem_convert
  {
    file operator()(const std::filesystem::directory_entry& entry) const
    {
      file_type type = file_type::FILE;

      if (std::filesystem::is_directory(entry))
        type = file_type::DIR;

      auto is_link = std::filesystem::is_symlink(entry);
      auto is_hidden = file_utils::is_hidden(entry);

      return { entry.path(), type, is_link, is_hidden };
    }
  };

  inline files get_files_struct(const std::filesystem::path& path)
  {
    files f;

    try 
    {
      std::filesystem::directory_iterator start(path);
      std::filesystem::directory_iterator end;
      std::transform(start, end, std::back_inserter(f), filesystem_convert());
    }
    catch(...)
    {
      f.push_back({ path / "no permission", file_type::NONE, false, false });
    }

    return f;
  }

  // не нужно тут передавать pwd, как ссылку
  // т.к. один фиг копирование в content::fill произойдет из-за
  // parent_path() метода, которым мы пользуемся при передачи сюда параметра
  // НО пока эта информация уже устарела, т.к. много в коде изменилось
  // по этому потом поэкспериментируй с запусками эстов конечно же
  // т.е. передай по ссылке и посмотри на поведение
  inline files fill(std::filesystem::path pwd)
  {
    files current_files = get_files_struct(pwd);

    if (current_files.size() == 1 && current_files.at(0).path == pwd / "no permission")
    {
      return current_files;
    }

    files tmp;
    tmp.reserve(current_files.size());
    std::sort(current_files.begin(), current_files.end());

    files tmp_files;
    tmp_files.reserve(current_files.size());

    std::copy_if(current_files.begin(), current_files.end(), std::back_inserter(tmp), [](const file& f) -> bool { 
      if (f.type == file_type::DIR) return true;
      return false;
    });

    std::copy_if(current_files.begin(), current_files.end(), std::back_inserter(tmp_files), [](const file& f) -> bool { 
      if (f.type != file_type::DIR) return true;
      return false;
    });

    tmp.insert(tmp.end(), tmp_files.begin(), tmp_files.end());

    return tmp;
  }
}
