#include <string>
#include <numeric>
#include <vector>

#include <imgui.h>
#include <imgui_stdlib.h>

#include "app.hh"
#include "input.hh"

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>


namespace pg {
namespace {

class ImgSeq final : public App, public Input::Data {
 public:
  static inline TypeInfo kType = TypeInfo::Create<ImgSeq>("Input_ImgSeq");

  ImgSeq() noexcept : Data("imgseq") {
  }
  ~ImgSeq() noexcept {
    DropCache();
  }
  void DropCache() noexcept {
    for (auto& frame : frames_) {
      stbi_image_free(const_cast<uint8_t*>(frame.second.rgba));
    }
    frames_.clear();
  }

  void Update() noexcept override {
    const auto id = std::to_string(index())+" Input_ImgSeq | "+
        std::to_string(reinterpret_cast<uintptr_t>(this));

    if (ImGui::Begin(id.c_str())) {
      bool mod = false;
      mod |= ImGui::InputText("dir", &path_);
      mod |= ImGui::DragInt("start", &st_, 1, 0, 1024);
      mod |= ImGui::DragInt("end",   &ed_, 1, 0, 1024);
      if (mod) DropCache();

      if (msg_.size() > 0) {
        ImGui::TextUnformatted(msg_.c_str());
      }
    }
    ImGui::End();
  }

  Frame Fetch(size_t n) noexcept override
  try {
    n += static_cast<size_t>(st_);
    if (n >= static_cast<size_t>(ed_)) {
      throw "frame number out of range";
    }

    auto itr = frames_.find(n);
    if (itr != frames_.end()) return itr->second;

    const auto fname = path_+"/"+std::to_string(n)+".png";

    int w, h, comp;
    uint8_t* buf = stbi_load(fname.c_str(), &w, &h, &comp, 4);
    if (buf == nullptr) {
      throw stbi_failure_reason();
    }

    const Frame ret = {
      .w = static_cast<size_t>(w),
      .h = static_cast<size_t>(h),
      .rgba = buf,
    };
    frames_[n] = ret;
    return ret;
  } catch (const char* msg) {
    msg_ = msg;
    return {.w = 0, .h = 0, .rgba = nullptr};
  }
  size_t frames() noexcept override {
    if (ed_ <= st_) return 0;
    return static_cast<size_t>(ed_-st_);
  }

 private:
  std::string path_;
  int st_ = 1, ed_ = 100;

  std::string msg_;

  std::unordered_map<size_t, Frame> frames_;
};

}
}  // namespace pg