#include <FL/Fl.H>
#include <FL/Fl_PNG_Image.H>
#include <FL/Fl_JPEG_Image.H>

#include <algorithm>
#include <cctype>
#include <filesystem>
#include <iostream>
#include <mutex>

#include "thumbnail.hh"
#include "clickablebox.hh"
#include "misc.hh"
#include "widget.hh"

namespace fe {
  Thumbnail::Thumbnail() : loadingDialog(nullptr) {}

  Thumbnail::~Thumbnail() {
    for (auto img : thumbs) {
      delete img;
    }

    if (loadingDialog) {
      delete loadingDialog;
    }
  }

  void Thumbnail::createLoadingDialog() {
    Fl_Window *mainWindow = Widget::getScroll()->window();
    if (!mainWindow) return;

    int w = 200;
    int h = 100;
    int sw = mainWindow->w();
    int sh = mainWindow->h();

    int sx = mainWindow->x();
    int sy = mainWindow->y();
    int x = sx + (sw - w) / 2;
    int y = sy + (sh - h) / 2;

    loadingDialog = new Fl_Window(x, y, w, h, "読み込み中");
    loadingDialog->set_modal();

    Fl_Box *box =
      new Fl_Box(0, 0, w, h, "画像を読み込んでいます。\nお待ち下さい。");
    box->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE);

    loadingDialog->end();
    loadingDialog->show();
    loadingDialog->take_focus();
  }

  void Thumbnail::init() {
    const char *home = getenv("HOME");
    std::string homedir = "";
    if (home) homedir = std::string(home) + "/.local/share/wallpapers";
    std::string localdir = "/usr/local/share/wallpapers";
    std::string sysdir = "/usr/share/wallpapers";

    if (Misc::isExist(homedir)) {
      std::vector<std::string> img = listImages(homedir);
      imgfiles.insert(imgfiles.end(), img.begin(), img.end());
    }
    if (Misc::isExist(localdir)) {
      std::vector<std::string> img = listImages(localdir);
      imgfiles.insert(imgfiles.end(), img.begin(), img.end());
    }
    if (Misc::isExist(sysdir)) {
      std::vector<std::string> img = listImages(sysdir);
      imgfiles.insert(imgfiles.end(), img.begin(), img.end());
    }

    Widget::getScroll()->deactivate();

    for (const auto &file : imgfiles) {
      pending.push(file);
    }

    createLoadingDialog();
    Fl::add_timeout(0.0, loadImagesCb, this);
  }

  void Thumbnail::loadImagesCb(void *v) {
    Thumbnail *thumb = static_cast<Thumbnail *>(v);
    thumb->loadNextImage();
  }

  void Thumbnail::loadNextImage() {
    std::lock_guard<std::mutex> lock(mutex);

    if (pending.empty()) {
      if (loadingDialog) {
        loadingDialog->hide();
        delete loadingDialog;
        loadingDialog = nullptr;
      }
      Widget::getScroll()->activate();
      Widget::getScroll()->redraw();
      return;
    }

    std::string filepath = pending.front();
    pending.pop();

    Fl_Image *img = nullptr;
    std::string ext = filepath.substr(filepath.size() - 4);

    if (ext == ".png") {
      img = new Fl_PNG_Image(filepath.c_str());
    } else if (ext == ".jpg") {
      img = new Fl_JPEG_Image(filepath.c_str());
    }

    if (!img || img->w() <= 0 || img->h() <= 0) {
      if (img) delete img;
      if (!pending.empty()) Fl::add_timeout(0.0, loadImagesCb, this);
      else {
        if (loadingDialog) {
          loadingDialog->hide();
          delete loadingDialog;
          loadingDialog = nullptr;
        }
        Widget::getScroll()->activate();
        Widget::getScroll()->redraw();
      }
      return;
    }

    Fl_Image *thumb = img->copy(100, 100);
    thumbs.push_back(thumb);
    delete img;

    ClickableBox *box = new ClickableBox(curX, curY, 100, 100);
    box->image(thumb);
    box->box(FL_FLAT_BOX);
    box->setImagePath(filepath.c_str());

    Widget::getScroll()->add(box);
    Widget::getScroll()->redraw();

    curX += 110;
    if (curX + 110 > Widget::getScroll()->w()) {
        curX = 10;
        curY += 110;
    }

    if (!pending.empty()) Fl::add_timeout(0.01, loadImagesCb, this);
    else {
      if (loadingDialog) {
        loadingDialog->hide();
        delete loadingDialog;
        loadingDialog = nullptr;
      }
      Widget::getScroll()->activate();
      Widget::getScroll()->redraw();
    }
  }

  std::vector<std::string> Thumbnail::listImages(const std::string &directory) {
    std::vector<std::string> images;
    std::filesystem::path dir(directory);

    if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
      std::cerr << "ディレクトリを開くに失敗： " << directory << std::endl;
      return images;
    }

    for (const auto &entry : std::filesystem::directory_iterator(dir)) {
      if (!entry.is_regular_file()) continue;

      std::string filename = entry.path().filename().string();
      if (filename.size() <= 4) continue;

      std::string ext = entry.path().extension().string();
      std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);

      if (ext == ".png" || ext == ".jpg") {
        images.push_back(entry.path().string());
      }
    }

    return images;
  }
}
