#include "torlinkc/ui/colored_window.hpp"

#include <algorithm>
#include <array>
#include <memory>
#include <string>
#include <utility>

#include <ftxui/dom/node.hpp>
#include <ftxui/dom/requirement.hpp>
#include <ftxui/dom/take_any_args.hpp>
#include <ftxui/screen/box.hpp>
#include <ftxui/screen/screen.hpp>

// Adapted from FTXUI v5.0.0's internal (unexported) `Border` node
// (src/ftxui/dom/border.cpp, MIT-licensed) with the ROUNDED charset and a
// mandatory foreground color baked in, since the public `window()` in this
// FTXUI version takes neither.

namespace torlinkc::ui {

namespace {

using ftxui::Box;
using ftxui::Color;
using ftxui::Element;
using ftxui::Elements;
using ftxui::Node;
using ftxui::Pixel;
using ftxui::Screen;

constexpr std::array<const char*, 6> kRoundedCharset = {"╭", "╮", "╰", "╯", "─", "│"};

class ColoredWindowNode : public Node {
 public:
  ColoredWindowNode(Elements children, Color color) : Node(std::move(children)), color_(color) {}

  void ComputeRequirement() override {
    Node::ComputeRequirement();
    requirement_ = children_[0]->requirement();
    requirement_.min_x += 2;
    requirement_.min_y += 2;
    requirement_.min_x = std::max(requirement_.min_x, children_[1]->requirement().min_x + 2);
    requirement_.selected_box.x_min++;
    requirement_.selected_box.x_max++;
    requirement_.selected_box.y_min++;
    requirement_.selected_box.y_max++;
  }

  void SetBox(Box box) override {
    Node::SetBox(box);
    Box title_box;
    title_box.x_min = box.x_min + 1;
    title_box.x_max = box.x_max - 1;
    title_box.y_min = box.y_min;
    title_box.y_max = box.y_min;
    children_[1]->SetBox(title_box);

    box.x_min++;
    box.x_max--;
    box.y_min++;
    box.y_max--;
    children_[0]->SetBox(box);
  }

  void Render(Screen& screen) override {
    children_[0]->Render(screen);

    if (box_.x_min >= box_.x_max || box_.y_min >= box_.y_max) return;

    screen.at(box_.x_min, box_.y_min) = kRoundedCharset[0];
    screen.at(box_.x_max, box_.y_min) = kRoundedCharset[1];
    screen.at(box_.x_min, box_.y_max) = kRoundedCharset[2];
    screen.at(box_.x_max, box_.y_max) = kRoundedCharset[3];

    for (int x = box_.x_min + 1; x < box_.x_max; ++x) {
      Pixel& p1 = screen.PixelAt(x, box_.y_min);
      Pixel& p2 = screen.PixelAt(x, box_.y_max);
      p1.character = kRoundedCharset[4];
      p2.character = kRoundedCharset[4];
      p1.automerge = true;
      p2.automerge = true;
    }
    for (int y = box_.y_min + 1; y < box_.y_max; ++y) {
      Pixel& p3 = screen.PixelAt(box_.x_min, y);
      Pixel& p4 = screen.PixelAt(box_.x_max, y);
      p3.character = kRoundedCharset[5];
      p4.character = kRoundedCharset[5];
      p3.automerge = true;
      p4.automerge = true;
    }

    children_[1]->Render(screen);

    // Overwriting the perimeter's foreground_color after the title renders
    // is what tints the title text too (it sits on the top edge), while
    // children_[0] (the interior) was already drawn above and is untouched.
    for (int x = box_.x_min; x <= box_.x_max; ++x) {
      screen.PixelAt(x, box_.y_min).foreground_color = color_;
      screen.PixelAt(x, box_.y_max).foreground_color = color_;
    }
    for (int y = box_.y_min; y <= box_.y_max; ++y) {
      screen.PixelAt(box_.x_min, y).foreground_color = color_;
      screen.PixelAt(box_.x_max, y).foreground_color = color_;
    }
  }

 private:
  Color color_;
};

}  // namespace

Element coloredWindow(Element title, Element content, Color color) {
  return std::make_shared<ColoredWindowNode>(ftxui::unpack(std::move(content), std::move(title)), color);
}

}  // namespace torlinkc::ui
