//  ------------------------------------------------------------------
//  GoldED+
//  Copyright (C) 1990-1999 Odinn Sorensen
//  Copyright (C) 1999-2000 Alexander S. Aganichev
//  ------------------------------------------------------------------
//  This program 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 program 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, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//  MA 02111-1307 USA
//  ------------------------------------------------------------------
//  $Id$
//  ------------------------------------------------------------------
//  Arealist functions.
//  ------------------------------------------------------------------

#include <golded.h>


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

AreaList AL;

int AreaTypeOrder[17] = {
  0,
  1,      // AT_NET
  2,      // AT_EMAIL
  3,      // AT_NET | AT_EMAIL
  4,      // AT_ECHO
  5,
  6,
  7,
  8,      // AT_NEWSGROUP
  9,
  10,
  11,
  12,     // AT_ECHO | AT_NEWSGROUP
  13,
  14,
  15,
  16      // AT_LOCAL
};


//  ------------------------------------------------------------------
//  Arealist compare

extern "C" int AreaListCmp(const Area** __a, const Area** __b) {

  const Area* a = *__a;
  const Area* b = *__b;
  const Area* A = a;
  const Area* B = b;
  int cmp = 0;
  int aunread;
  int bunread;

  bool rev = false;
  char* ptr = AL.sortspec;

  while(*ptr) {
    switch(toupper(*ptr)) {
      case '-':
        rev = true;
        A = b;  B = a;
        break;
      case '+':
        rev = false;
        A = a;  B = b;
        break;
      case 'A':
        if((cmp = A->aka().compare(B->aka())) != 0)
          return cmp;
        break;
      case 'B':
        if((cmp = compare_two(A->board(), B->board())) != 0)
          return cmp;
        break;
      case 'D':
        if((cmp = stricmp(A->desc(), B->desc())) != 0)
          return cmp;
        break;
      case 'E':
        if((cmp = stricmp(A->echoid(), B->echoid())) != 0)
          return cmp;
        break;
      case 'F':
        if(*area_maybe) {
          if(striinc(area_maybe, A->echoid()) and not striinc(area_maybe, B->echoid()))
            return -1;
          if(striinc(area_maybe, B->echoid()) and not striinc(area_maybe, A->echoid()))
            return 1;
        }
        break;
      case 'G':
        {
          int ga = A->groupid();
          int gb = B->groupid();

          char* p = strpbrk(AL.sortspec, "tT");
          bool groupfirst = p > ptr;
          bool ignoretype = p == NULL or groupfirst;

          if(ga == 0 and ((not A->isseparator()^rev) or ignoretype))
            ga = INT_MAX;
          if(gb == 0 and ((not B->isseparator()^rev) or ignoretype))
            gb = INT_MAX;

          if((A->isseparator() or B->isseparator()) and ga == gb) {
            if(groupfirst and (cmp = compare_two(CFG->areatypeorder[A->type()&0xFF], CFG->areatypeorder[B->type()&0xFF])) != 0)
              return cmp;
            if((cmp = compare_two(b->isseparator(), a->isseparator())) != 0)
              return cmp;
          }
          else {
            if((cmp = compare_two(ga, gb)) != 0)
              return cmp;
          }
        }
        break;
      case 'M':
        if(A->ismarked() and not B->ismarked())
          return -1;
        if(B->ismarked() and not A->ismarked())
          return 1;
        break;
      case 'P':
        if((cmp = compare_two(B->PMrk.Count()?1:0, A->PMrk.Count()?1:0)) != 0)
          return cmp;
        break;
      case 'O':
        if((cmp = compare_two(A->areaid(), B->areaid())) != 0)
          return cmp;
        break;
      case 'T':
        if((cmp = compare_two(CFG->areatypeorder[A->type()&0xFF], CFG->areatypeorder[B->type()&0xFF])) != 0)
          return cmp;
        break;
      case 'U':
        aunread = A->Msgn.Count() - A->lastread();
        bunread = B->Msgn.Count() - B->lastread();
        if(rev)
          cmp = compare_two(aunread, bunread);
        else
          cmp = compare_two(aunread ? aunread : INT_MAX, bunread ? bunread : INT_MAX);
        if(cmp != 0)
          return cmp;
        break;
      case 'X':
        if((cmp = compare_two(A->msgbase(), A->msgbase())) != 0)
          return cmp;
        break;
      case 'Y':
        aunread = A->Msgn.Count() - A->lastread();
        bunread = B->Msgn.Count() - B->lastread();
        if(aunread and not bunread)
          return -1;
        if(bunread and not aunread)
          return 1;
        break;
      case 'Z':
        if((cmp = stricmp(A->path(), B->path())) != 0)
          return cmp;
        break;
    }
    ptr++;
  }

  return cmp;
}

//  ------------------------------------------------------------------
//  Arealist sort areas

void AreaList::Sort(const char* specs, int first, int last) {

  if(specs)
    strcpy(sortspec, specs);
  else
    strcpy(sortspec, CFG->arealistsort);
  if(last == -1)
    last = idx.size();
  if(*sortspec)
    qsort(&idx[first], last-first, sizeof(Area *), (StdCmpCP)AreaListCmp);
}


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

int AreaList::AreaEchoToNo(const char* echoid) {

  for(uint n=0; n<idx.size(); n++)
    if(strieql(echoid, idx[n]->echoid()))
      return n;
  return -1;
}


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

Area* AreaList::AreaEchoToPtr(const char* echoid) {

  for(uint n=0; n<idx.size(); n++)
    if(strieql(echoid, idx[n]->echoid()))
      return idx[n];
  return NULL;
}


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

int AreaList::AreaNoToId(int __areano) {

  if(in_range(__areano, 0, int(idx.size()-1)))
    return idx[__areano]->areaid();
  else {
    LOG.ErrIndex();
    LOG.printf("! Arealist index out of bounds.");
    LOG.printf(": Tried to access record %i, but the range was only 0 - %u.", __areano, (uint)(idx.size()-1));
    LOG.printf("+ Advice: Report this to the author.");
    IndexErrorExit();
    return 0;
  }
}


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

Area* AreaList::AreaNoToPtr(int __areano) {

  if(in_range(__areano, 0, int(idx.size()-1)))
    return idx[__areano];
  else {
    LOG.ErrIndex();
    LOG.printf("! Arealist index out of bounds.");
    LOG.printf(": Tried to access record %i, but the range was only 0 - %u.", __areano, (uint)(idx.size()-1));
    LOG.printf("+ Advice: Report this to the author.");
    IndexErrorExit();
    return 0;
  }
}


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

int AreaList::AreaIdToNo(int __areaid) {

  uint _areano = 0;
  while(_areano < idx.size()) {
    if(idx[_areano]->areaid() == __areaid)
      return _areano;
    _areano++;
  }
  return -1;
}


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

Area* AreaList::AreaIdToPtr(int __areaid) {

  int _areano = AreaIdToNo(__areaid);
  if(_areano != -1)
    return idx[_areano];
  return NULL;
}


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

int AreaList::SetActiveAreaId(int __areaid) {

  int _areano = AreaIdToNo(__areaid);
  SetActiveAreaNo(_areano);
  return _areano;   // Return the areano
}


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

class SelMaskPick : public gwinpick {

  gwindow window;

  void open();                        // Called after window is opened
  void close();                       // Called after window is closed
  void print_line(uint idx, uint pos, bool isbar);
  bool handle_key();                  // Handles keypress

public:

  void Run();

};

void SelMaskPick::open() {

  window.openxy(ypos, xpos, ylen+2, xlen+2, btype, battr, 7);
  window.title(title, tattr);
  window.shadow(C_SHADOW);
  update();
}

void SelMaskPick::close() {

  window.close();

}

void SelMaskPick::print_line(uint idx, uint pos, bool isbar) {

  char buf[sizeof(Desc)+3];

  *buf = ' '; strcpy(buf+1, AL.alistselections[idx]);
  window.printns(pos, 0, isbar ? sattr : wattr, buf, xlen);
}

bool SelMaskPick::handle_key() {

  switch(key) {
    case Key_Esc:
      aborted = true;

      /// Drop through
    case Key_Ent:
      return false;

    case Key_Tick:
      break;

    default:
      if(key < KK_Commands) {
        kbput(key);
        edit_string(AL.alistselections[index], sizeof(Desc)-1, LNG->SelectMarksEdit, H_SelectMarks);
        display_bar();
      }
      break;
  }

  return true;
}

void SelMaskPick::Run() {

  ypos = (MAXROW-18)/2;
  xpos = (MAXCOL-sizeof(Desc)-4)/2-1;
  ylen = 16;
  xlen = sizeof(Desc)+2;
  btype = W_BMENU;
  battr = C_MENUB;
  wattr = C_MENUW;
  tattr = C_MENUT;
  sattr = C_MENUS;
  hattr = C_MENUQ;

  title = LNG->SelectMarks;
  helpcat = H_SelectMarks;
  listwrap = true;

  maximum_index = 15;
  maximum_position = 16;

  index = position = AL.mask;

  run_picker();

  if(not aborted)
    AL.mask = (byte)index;

};


void AreaList::Select_Mask() {

  SelMaskPick& SelMask = *new SelMaskPick;
  throw_new(&SelMask);

  SelMask.Run();

  extern bool in_arealist;
  extern GPickArealist* PickArealist;

  if(in_arealist) {
    PickArealist->update();
    PickArealist->do_delayed();
  }

  delete &SelMask;
}


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