//  This may look like C code, but it is really -*- C++ -*-

//  ------------------------------------------------------------------
//  The Goldware Library
//  Copyright (C) 1990-1999 Odinn Sorensen
//  Copyright (C) 1999-2000 Alexander S. Aganichev
//  ------------------------------------------------------------------
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Library General Public License for more details.
//
//  You should have received a copy of the GNU Library General Public
//  License along with this program; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//  MA 02111-1307, USA
//  ------------------------------------------------------------------
//  $Id$
//  ------------------------------------------------------------------
//  Pick engine.
//  ------------------------------------------------------------------

#include <gkbdcode.h>
#include <gmoubase.h>
#include <gtimall.h>
#include <gutlmisc.h>
#include <gwinall.h>
#include <gwinhelp.h>
#include <gwinpick.h>


//  ------------------------------------------------------------------

gwinpick::gwinpick() {

  // memset(this, 0, sizeof(gwinpick));  WHAT!?!?! ARE YOU NUTS?

  key = 0;
  keyok = false;
  ypos = xpos = ylen = xlen = 0;
  btype = 0;
  battr = wattr = tattr = sattr = hattr = loattr = sbattr = BLACK_|_BLACK;
  title = NULL;
  helpcat = 0;
  maximum_index = minimum_index = maximum_position = index = position = 0;
  aborted = listwrap = false;
  direction = 0;
  replylinkfloat = 0;

}


//  ------------------------------------------------------------------

void gwinpick::cursor_up() {

  bool done;

  do {
    if(index > minimum_index) {
      display_line();
      if(position)
        position--;
      else
        scroll(SDOWN);
      index--;
      if((done = is_selectable(index))==true)
        update_cursor();
    }
    else {
      if(listwrap)
        cursor_last();
      else if(not is_selectable(index))
        cursor_down();
      done = true;
    }
  } while(not done);
  direction = -1;
}


//  ------------------------------------------------------------------

void gwinpick::cursor_down() {

  bool done;

  do {
    if(index < maximum_index) {
      display_line();
      index++;
      if(position < maximum_position)
        position++;
      else
        scroll(SUP);

      if((done = is_selectable(index))!=false)
        update_cursor();
    }
    else {
      if(listwrap)
        cursor_first();
      else if(not is_selectable(index))
        cursor_up();
      done = true;
    }
  } while(not done);
  direction = 1;
}


//  ------------------------------------------------------------------

void gwinpick::cursor_pageup() {

  uint i = index - position;

  while(not is_selectable(i))    i++;

  uint min = i + position - index;

  if(position > min) {
    display_line();
    index = i;
    position = min;
    update_cursor();
  }
  else if(index > min) {
    i = maximum_position - min + position;
    index -= (index >= i) ? i : index;
    position = 0;
    display_page();
    if(not is_selectable(index))
      cursor_down();
  }

  direction = -1;
}


//  ------------------------------------------------------------------

void gwinpick::cursor_pagedown() {

  uint max_index = index + maximum_position - position;

  if(max_index > maximum_index)  max_index = maximum_index;
  while(not is_selectable(max_index))   --max_index;

  uint max_position = max_index - index + position;

  if(position < max_position) {
    if(index == max_index) {
      if(maximum_position < maximum_index) {
        position = maximum_position;
        index = maximum_index;
        display_page();
        update_cursor();
      }
    }
    else if(max_index >= max_position) {
      display_line();
      position = max_position;
      index = max_index;
      update_cursor();
    }
    else if(position != maximum_position) {
      display_line();
      index += maximum_position-position;
      position = maximum_position;
      update_cursor();
    }
  }
  else if(index < maximum_index) {
    if(index+maximum_position+(maximum_position - max_position)> maximum_index) {
      position = maximum_index - index;
      index = maximum_index;
    }
    else {
      index += maximum_position + (maximum_position - max_position);
      position = maximum_position;
    }
    display_page();
  }

  if(not is_selectable(index))
    cursor_up();

  direction = 1;
}


//  ------------------------------------------------------------------

void gwinpick::cursor_first() {

  uint min_select = minimum_index;

  while(not is_selectable(min_select))
    min_select++;

  if(index > min_select) {
    if(index <= position) {
      display_line();
      index = position = min_select;
      update_cursor();
    }
    else {
      index = position = min_select;
      display_page();
    }
  }

  direction = -1;
}


//  ------------------------------------------------------------------

void gwinpick::cursor_last() {

  uint max_select = maximum_index;

  while(not is_selectable(max_select))
    max_select--;

  if(index < max_select) {
    if(index + (maximum_position-position) >= maximum_index) {
      display_line();
      position += max_select - index;
      index = max_select;
      update_cursor();
    }
    else {
      position = (maximum_index-index) < maximum_position ? maximum_index-index : maximum_position;
      position -= maximum_index - max_select;
      index = max_select;
      display_page();
    }
  }
  else if(position < maximum_position) {
    if(maximum_position < maximum_index) {
      position = maximum_position + max_select - maximum_index;
      index = max_select;
      display_page();
    }
  }

  direction = 1;
}


//  ------------------------------------------------------------------

void gwinpick::cursor_scroll_up() {

  uint oldidx = index;
  do {
    if(index - position)
      index--;
    else
      while((not is_selectable(index)) and (index < maximum_index))
        index++;
  } while(not is_selectable(index));
  if(index != oldidx)
    display_page();

  direction = -1;
}


//  ------------------------------------------------------------------

void gwinpick::cursor_scroll_down() {

  uint oldidx = index;
  if(index != maximum_index) {
    do {
      if(index < maximum_index)
        index++;
      else
        while((not is_selectable(--index)) and (index > minimum_index))
          ;
    } while(not is_selectable(index));
  }
  if(index!=oldidx)
    display_page();

  direction = 1;
}


//  ------------------------------------------------------------------

void gwinpick::display_line(bool bar) {

  print_line(index, position, bar);
}


//  ------------------------------------------------------------------

void gwinpick::display_page() {

  if(index > position)
    index -= position;
  else
    index = 0;

  register uint m = maximum_index-index;

  uint n;

  if (replylinkfloat)
  {
    if (h_offset == new_hoffset)
    {
      for (n=0; n<=maximum_position and n<=m; n++)
        if (position == n)
          print_line(index+n, n, true);
    }

    h_offset = new_hoffset;
  }

  for(n=0; n<=maximum_position and n<=m; n++)
    print_line(index+n, n, (position == n));

  if(n < ylen)
    wputx(n, 0, battr|ACSET, _box_table(btype,1), xlen);

  for(++n; n<ylen; n++)
    wputx(n, 0, wattr, (vchar)' ', xlen);

  index += position;

  wgotoxy(position, 0);
}


//  ------------------------------------------------------------------

void gwinpick::open() { update(); }
void gwinpick::close() {}
void gwinpick::precursor() {}
void gwinpick::do_delayed() {}
void gwinpick::scroll(int direction) { wscroll(1, direction); }
bool gwinpick::handle_key() { return false; }
bool gwinpick::is_selectable(uint) { return true; }


//  ------------------------------------------------------------------

bool gwinpick::default_handle_key() {

  keyok = true;

  switch(key) {
    case Key_Up:    precursor(); cursor_up();           break;
    case Key_Dwn:   precursor(); cursor_down();         break;
    case Key_PgUp:  precursor(); cursor_pageup();       break;
    case Key_PgDn:  precursor(); cursor_pagedown();     break;
    case Key_Home:  precursor(); cursor_first();        break;
    case Key_End:   precursor(); cursor_last();         break;
    case Key_C_Up:  precursor(); cursor_scroll_up();    break;
    case Key_C_Dwn: precursor(); cursor_scroll_down();  break;
    default:
      keyok = handle_key();
  }

  return keyok;
}


//  ------------------------------------------------------------------

void gwinpick::update_cursor() {

  wgotoxy(position, 0);
  display_bar();
//  do_delayed();
}


//  ------------------------------------------------------------------

void gwinpick::center(int listmode) {

  if(maximum_index <= maximum_position) {
    position = index;
    update();
    return;
  }

  uint botroom = maximum_index - index;

  switch(listmode) {
    case LIST_TOP:
      position = (botroom > maximum_position) ? 0 : (maximum_position - botroom);
      break;
    case LIST_NEARTOP:
      {
        uint room;
        uint toproom = index;
        if(toproom > (maximum_position/4)) {
          if(botroom > (maximum_position/4))
            room = maximum_position/4;
          else if(botroom)
            room = maximum_position - botroom;
          else
            room = maximum_position;
        }
        else
          room = toproom;
        position = room;
      }
      break;
    case LIST_MIDDLE:
      {
        uint room;
        uint toproom = index;
        if(toproom > (maximum_position/2)) {
          if(botroom > (maximum_position/2))
            room = maximum_position/2;
          else if(botroom)
            room = maximum_position - botroom;
          else
            room = maximum_position;
        }
        else
          room = toproom;
        position = room;
      }
      break;
    case LIST_NEARBOTTOM:
      {
        uint room;
        uint toproom = index;
        if(toproom > 3*(maximum_position/4)) {
          if(botroom > 3*(maximum_position/4))
            room = 3*(maximum_position/4);
          else if(botroom)
            room = maximum_position - botroom;
          else
            room = maximum_position;
        }
        else
          room = toproom;
        position = room;
      }
      break;
    case LIST_BOTTOM:
      position = maximum_position;
      break;
  }
  update();
}


//  ------------------------------------------------------------------

int gwinpick::run_picker() {

  #ifdef GOLD_MOUSE
  gmou.HideCursor();
  #endif

  // Open and initialize

  open();
  if(helpcat)
    whelppcat(helpcat);

  if(not is_selectable(index))
    cursor_down();

  do {

    do_delayed();

    #ifdef GOLD_MOUSE
    gmou.ShowCursor();
    #endif

    keyok = true;

    do {
      key = getxchtick();
      if(key == Key_Tick)
        handle_key();
    } while(key == Key_Tick);

    #ifdef GOLD_MOUSE
    gmou.HideCursor();
    #endif

    keyok = default_handle_key();

    if (replylinkfloat && (h_offset != new_hoffset))
      display_page();

  } while(keyok);

  #ifdef GOLD_MOUSE
  gmou.HideCursor();
  #endif

  // Close and clean
  if(helpcat)
    whelpop();
  close();

  #ifdef GOLD_MOUSE
  gmou.ShowCursor();
  #endif

  return 0;
}


//  ------------------------------------------------------------------