#include "config.h"

#include <stdexcept>
#include <algorithm>

#include "canvas.h"
#include "globals.h"
#include "manager.h"
#include "window.h"

namespace display {

Manager::Manager() {
  m_scheduler.external_set_thread_id(std::this_thread::get_id());

  m_task_update.slot() = std::bind(&Manager::receive_update, this);
}

Manager::~Manager() {
  torrent::this_thread::scheduler()->erase(&m_task_update);
}

void
Manager::force_redraw() {
  m_force_redraw = true;
}

void
Manager::schedule(Window* w, std::chrono::microseconds t) {
  m_scheduler.external_set_cached_time(torrent::this_thread::cached_time());
  m_scheduler.update_wait_until(w->task_update(), t);

  schedule_update(50ms);
}

void
Manager::unschedule(Window* w) {
  m_scheduler.external_set_cached_time(torrent::this_thread::cached_time());
  m_scheduler.erase(w->task_update());

  schedule_update(50ms);
}

void
Manager::adjust_layout() {
  Canvas::redraw_std();
  m_root_frame.balance(0, 0, Canvas::get_screen_width(), Canvas::get_screen_height());

  schedule_update(0ms);
}

void
Manager::receive_update() {
  if (m_force_redraw) {
    m_force_redraw = false;

    display::Canvas::resize_term(display::Canvas::term_size());
    keypad(stdscr, TRUE);
    Canvas::redraw_std();

    adjust_layout();
    m_root_frame.redraw();
  }

  Canvas::refresh_std();

  m_scheduler.external_perform(torrent::this_thread::cached_time());
  m_root_frame.refresh();

  Canvas::do_update();

  m_time_last_update = torrent::this_thread::cached_time();
  schedule_update(50ms);
}

void
Manager::schedule_update(std::chrono::microseconds min_interval) {
  if (m_scheduler.empty()) {
    torrent::this_thread::scheduler()->erase(&m_task_update);
    return;
  }

  auto cached = torrent::this_thread::cached_time();
  m_scheduler.external_set_cached_time(cached);
  auto next = cached + m_scheduler.next_timeout(cached);

  if (m_task_update.is_scheduled() && m_task_update.time_or_zero() <= next)
    return;

  torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(next, m_time_last_update + min_interval));
}

}
