/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file LICENSE.rst or https://cmake.org/licensing for details.  */
#include "cmCursesLongMessageForm.h"

#include <cstdio>
#include <cstring>

#include "cmCursesForm.h"
#include "cmCursesMainForm.h"
#include "cmCursesStandardIncludes.h"
#include "cmStringAlgorithms.h"
#include "cmVersion.h"

inline int ctrl(int z)
{
  return (z & 037);
}

cmCursesLongMessageForm::cmCursesLongMessageForm(
  std::vector<std::string> const& messages, char const* title,
  ScrollBehavior scrollBehavior)
  : Scrolling(scrollBehavior)
{
  // Append all messages into on big string
  this->Messages = cmJoin(messages, "\n");
  this->Title = title;
  this->Fields[0] = nullptr;
  this->Fields[1] = nullptr;
}

cmCursesLongMessageForm::~cmCursesLongMessageForm()
{
  if (this->Fields[0]) {
    free_field(this->Fields[0]);
  }
}

void cmCursesLongMessageForm::UpdateContent(std::string const& output,
                                            std::string const& title)
{
  this->Title = title;

  if (!output.empty() && this->Messages.size() < MAX_CONTENT_SIZE) {
    this->Messages.push_back('\n');
    this->Messages.append(output);
    form_driver(this->Form, REQ_NEXT_LINE);
    form_driver(this->Form, REQ_BEG_LINE);
    this->DrawMessage(output.c_str());
  }

  this->UpdateStatusBar();
  touchwin(stdscr);
  refresh();
}

void cmCursesLongMessageForm::UpdateStatusBar()
{
  int x;
  int y;
  getmaxyx(stdscr, y, x);

  char bar[cmCursesMainForm::MAX_WIDTH];
  size_t size = this->Title.size();
  if (size >= cmCursesMainForm::MAX_WIDTH) {
    size = cmCursesMainForm::MAX_WIDTH - 1;
  }
  strncpy(bar, this->Title.c_str(), size);
  for (size_t i = size; i < cmCursesMainForm::MAX_WIDTH; i++) {
    bar[i] = ' ';
  }
  int width;
  if (x >= 0 && x < cmCursesMainForm::MAX_WIDTH) {
    width = x;
  } else {
    width = cmCursesMainForm::MAX_WIDTH - 1;
  }

  bar[width] = '\0';

  char version[cmCursesMainForm::MAX_WIDTH];
  char vertmp[128];
  snprintf(vertmp, sizeof(vertmp), "CMake Version %s",
           cmVersion::GetCMakeVersion());
  size_t sideSpace = (width - strlen(vertmp));
  for (size_t i = 0; i < sideSpace; i++) {
    version[i] = ' ';
  }
  snprintf(version + sideSpace, sizeof(version) - sideSpace, "%s", vertmp);
  version[width] = '\0';

  char fmt_s[] = "%s";
  move(y - 4, 0);
  attron(A_STANDOUT);
  printw(fmt_s, bar);
  attroff(A_STANDOUT);
  move(y - 3, 0);
  printw(fmt_s, version);
  pos_form_cursor(this->Form);
}

void cmCursesLongMessageForm::PrintKeys()
{
  int x;
  int y;
  getmaxyx(stdscr, y, x);
  if (x < cmCursesMainForm::MIN_WIDTH || y < cmCursesMainForm::MIN_HEIGHT) {
    return;
  }
  char firstLine[512];
  snprintf(firstLine, sizeof(firstLine), "Press [e] to exit screen");

  char fmt_s[] = "%s";
  move(y - 2, 0);
  printw(fmt_s, firstLine);
  pos_form_cursor(this->Form);
}

void cmCursesLongMessageForm::Render(int /*left*/, int /*top*/, int /*width*/,
                                     int /*height*/)
{
  int x;
  int y;
  getmaxyx(stdscr, y, x);

  if (this->Form) {
    unpost_form(this->Form);
    free_form(this->Form);
    this->Form = nullptr;
  }

  if (this->Fields[0]) {
    free_field(this->Fields[0]);
    this->Fields[0] = nullptr;
  }

  this->Fields[0] = new_field(y - 6, x - 2, 1, 1, 0, 0);

  field_opts_off(this->Fields[0], O_STATIC);

  this->Form = new_form(this->Fields);
  post_form(this->Form);

  form_driver(this->Form, REQ_BEG_FIELD);
  this->DrawMessage(this->Messages.c_str());

  this->UpdateStatusBar();
  touchwin(stdscr);
  refresh();
}

void cmCursesLongMessageForm::DrawMessage(char const* msg) const
{
  int i = 0;
  while (msg[i] != '\0' && i < MAX_CONTENT_SIZE) {
    if (msg[i] == '\n' && msg[i + 1] != '\0') {
      form_driver(this->Form, REQ_NEXT_LINE);
      form_driver(this->Form, REQ_BEG_LINE);
    } else {
      form_driver(this->Form, msg[i]);
    }
    i++;
  }
  if (this->Scrolling == ScrollBehavior::ScrollDown) {
    form_driver(this->Form, REQ_END_FIELD);
  } else {
    form_driver(this->Form, REQ_BEG_FIELD);
  }
}

void cmCursesLongMessageForm::HandleInput()
{
  if (!this->Form) {
    return;
  }

  char debugMessage[128];

  for (;;) {
    this->PrintKeys();
    int key = getch();

#ifdef _WIN32
    if (key == KEY_RESIZE) {
      HandleResize();
    }
#endif // _WIN32

    snprintf(debugMessage, sizeof(debugMessage),
             "Message widget handling input, key: %d", key);
    cmCursesForm::LogMessage(debugMessage);

    // quit
    if (key == 'o' || key == 'e') {
      break;
    }
    if (key == KEY_DOWN || key == ctrl('n') || key == 'j') {
      form_driver(this->Form, REQ_SCR_FLINE);
    } else if (key == KEY_UP || key == ctrl('p') || key == 'k') {
      form_driver(this->Form, REQ_SCR_BLINE);
    } else if (key == KEY_NPAGE || key == ctrl('d')) {
      form_driver(this->Form, REQ_SCR_FPAGE);
    } else if (key == KEY_PPAGE || key == ctrl('u')) {
      form_driver(this->Form, REQ_SCR_BPAGE);
    }

    this->UpdateStatusBar();
    touchwin(stdscr);
    wrefresh(stdscr);
  }
}
