/*
 * dselect - Debian package maintenance user interface
 * basecmds.cc - base list keyboard commands display
 *
 * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
 * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org>
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <config.h>
#include <compat.h>

#include <errno.h>
#include <string.h>
#include <stdio.h>

#include <dpkg/i18n.h>
#include <dpkg/dpkg.h>
#include <dpkg/dpkg-db.h>

#include "dselect.h"
#include "helpmsgs.h"

void baselist::kd_scrollon() {
  topofscreen += list_height;
  if (topofscreen > nitems - list_height) topofscreen= nitems - list_height;
  if (topofscreen < 0) topofscreen= 0;
  if (cursorline < topofscreen)
    setcursor(topofscreen);
  refreshlist();
}

void baselist::kd_scrollon1() {
  if (topofscreen >= nitems - list_height) return;
  topofscreen++;
  if (cursorline < topofscreen)
    setcursor(topofscreen);
  refreshlist();
}

void baselist::kd_scrollback() {
  topofscreen -= list_height;
  if (topofscreen < 0) topofscreen= 0;
  if (cursorline >= topofscreen + list_height)
    setcursor(topofscreen + list_height - 1);
  refreshlist();
}

void baselist::kd_scrollback1() {
  if (topofscreen <= 0) return;
  topofscreen--;
  if (cursorline >= topofscreen + list_height)
    setcursor(topofscreen + list_height - 1);
  refreshlist();
}

void baselist::kd_top() {
  topofscreen= 0; setcursor(0);
}

void baselist::kd_bottom() {
  topofscreen= nitems - list_height;
  if (topofscreen < 0) topofscreen= 0;
  setcursor(min(topofscreen + list_height - 1, nitems - 1));
}

void baselist::kd_redraw() {
  clearok(curscr,TRUE);
  redrawall();
  debug(dbg_general, "baselist[%p]::kd_redraw() done", this);
}

void baselist::kd_searchagain() {
  if (!searchstring[0]) { beep(); return; }
  dosearch();
}

bool
baselist::checksearch(char *str)
{
  return true;
}

bool
baselist::matchsearch(int index)
{
  int lendiff, searchlen, i;
  const char *name;

  name = itemname(index);
  if (!name)
    return false;	/* Skip things without a name (separators). */

  searchlen=strlen(searchstring);
  lendiff = strlen(name) - searchlen;
  for (i=0; i<=lendiff; i++)
    if (strncasecmp(name + i, searchstring, searchlen) == 0)
      return true;

  return false;
}

void baselist::kd_search() {
  char newsearchstring[128];
  strcpy(newsearchstring,searchstring);
  werase(querywin);
  mvwaddstr(querywin,0,0, _("Search for ? "));
  echo();
  if (wgetnstr(querywin,newsearchstring,sizeof(newsearchstring)-1) == ERR)
    searchstring[0]= 0;
  resize_window();
  noecho();
  if (whatinfo_height) { touchwin(whatinfowin); refreshinfo(); }
  else if (info_height) { touchwin(infopad); refreshinfo(); }
  else if (thisstate_height) redrawthisstate();
  else { touchwin(listpad); refreshlist(); }
  if (newsearchstring[0]) {
    if (!checksearch(newsearchstring)) {
      beep();
      return;
    }
    strcpy(searchstring,newsearchstring);
    dosearch();
  } else
    kd_searchagain();
}

void
baselist::displayerror(const char *str)
{
  const char *pr = _("Error: ");
  int prlen=strlen(pr);

  beep();
  werase(querywin);
  mvwaddstr(querywin,0,0,pr);
  mvwaddstr(querywin,0,prlen,str);
  wgetch(querywin);
  if (whatinfo_height) { touchwin(whatinfowin); refreshinfo(); }
  else if (info_height) { touchwin(infopad); refreshinfo(); }
  else if (thisstate_height) redrawthisstate();
}


void baselist::displayhelp(const struct helpmenuentry *helpmenu, int key) {
  int maxx, maxy, i, nextkey;

  wbkgdset(stdscr, ' ' | part_attr[helpscreen]);
  clearok(stdscr,TRUE);
  for (;;) {
    getmaxyx(stdscr, maxy, maxx);
    werase(stdscr);
    const struct helpmenuentry *hme = helpmenu;
    while (hme->key && hme->key != key)
      hme++;
    if (hme->key) {
      int x, y DPKG_ATTR_UNUSED;

      attrset(part_attr[helpscreen]);
      mvaddstr(1,0, gettext(hme->msg->text));
      attrset(part_attr[title]);
      mvaddstr(0,0, _("Help: "));
      addstr(gettext(hme->msg->title));
      getyx(stdscr,y,x);
      while (++x<maxx) addch(' ');
      attrset(part_attr[thisstate]);
      mvaddstr(maxy-1,0,
_("Press ? for help menu, . for next topic, <space> to exit help."));
      getyx(stdscr,y,x);
      while (++x<maxx) addch(' ');
      move(maxy,maxx);
      attrset(A_NORMAL);
      nextkey= hme[1].key;
    } else {
      mvaddstr(1,1, _("Help information is available under the following topics:"));
      for (i=0, hme=helpmenu; hme->key; hme++,i++) {
        attrset(A_BOLD);
        mvaddch(i+3,3, hme->key);
        attrset(A_NORMAL);
        mvaddstr(i+3,6, gettext(hme->msg->title));
      }
      mvaddstr(i+4,1,
               _("Press a key from the list above, <space> or 'q' to exit help,\n"
                 "  or '.' (full stop) to read each help page in turn. "));
      nextkey= helpmenu[0].key;
    }
    refresh();
    do {
      int curkey = key;

      key = getch();
      if (key == KEY_RESIZE) {
        resize_window();
        key = curkey;
      }
    } while (key == ERR && errno == EINTR);
    if (key == EOF) ohshite(_("error reading keyboard in help"));
    if (key == ' ' || key == 'q') {
      break;
    } else if (key == '?' || key == 'h') {
      key= 0;
    } else if (key == '.') {
      key= nextkey;
    }
  }
  werase(stdscr);
  clearok(stdscr,TRUE);
  wnoutrefresh(stdscr);

  redrawtitle();
  refreshcolheads();
  refreshlist();
  redrawthisstate();
  refreshinfo();
  wnoutrefresh(whatinfowin);
}

void baselist::kd_help() {
  displayhelp(helpmenulist(),0);
}

void baselist::setcursor(int index) {
  if (listpad && cursorline != -1) {
    redrawitemsrange(cursorline,cursorline+1);
    redraw1itemsel(cursorline,0);
  }
  if (cursorline != index) infotopofscreen= 0;
  cursorline= index;
  if (listpad) {
    redrawitemsrange(cursorline,cursorline+1);
    redraw1itemsel(cursorline,1);
    refreshlist();
    redrawthisstate();
  }
  redrawinfo();
}

void baselist::kd_down() {
  int ncursor= cursorline;
  // scroll by one line unless the bottom is already visible
  // or we're in the top half of the screen ...
  if (topofscreen < nitems - list_height &&
      ncursor >= topofscreen + list_height - 3) topofscreen++;
  // move cursor if there are any more ...
  if (cursorline+1 < nitems) ncursor++;
  setcursor(ncursor);
}

void baselist::kd_up() {
  int ncursor= cursorline;
  // scroll by one line if there are any lines not shown yet
  // and we're not in the bottom half the screen ...
  if (topofscreen > 0 &&
      ncursor <= topofscreen + 2) topofscreen--;
  // move cursor if there are any to move it to ...
  if (cursorline > 0) ncursor--;
  setcursor(ncursor);
}

void baselist::kd_iscrollon() {
  infotopofscreen += info_height;
  if (infotopofscreen > infolines - info_height)
    infotopofscreen= infolines - info_height;
  if (infotopofscreen < 0) infotopofscreen= 0;
  refreshinfo();
}

void baselist::kd_iscrollback() {
  infotopofscreen -= info_height;
  if (infotopofscreen < 0) infotopofscreen= 0;
  refreshinfo();
}

void baselist::kd_iscrollon1() {
  if (infotopofscreen >= infolines - info_height) return;
  infotopofscreen++; refreshinfo();
}

void baselist::kd_iscrollback1() {
  if (infotopofscreen <= 0) return;
  infotopofscreen--; refreshinfo();
}

void baselist::kd_panon() {
  leftofscreen += xmax/3;
  if (leftofscreen > total_width - xmax) leftofscreen= total_width - xmax;
  if (leftofscreen < 0) leftofscreen= 0;
  refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo();
}

void baselist::kd_panback() {
  leftofscreen -= xmax/3;
  if (leftofscreen < 0) leftofscreen= 0;
  refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo();
}

void baselist::kd_panon1() {
  leftofscreen++;
  if (leftofscreen > total_width - xmax) leftofscreen= total_width - xmax;
  if (leftofscreen < 0) leftofscreen= 0;
  refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo();
}

void baselist::kd_panback1() {
  leftofscreen--;
  if (leftofscreen < 0) leftofscreen= 0;
  refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo();
}