
//  ------------------------------------------------------------------
//  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.
//  ------------------------------------------------------------------

#if defined(_MSC_VER)
    /* C4786: 'identifier' : identifier was truncated to 'number'
    characters in the debug information
    */
    #pragma warning(disable: 4786)
#endif
#include <algorithm>
#include <limits.h>
#include <golded.h>

#if defined(__USE_ALLOCA__)
    #include <malloc.h>
#endif


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

AreaList AL;

int AreaTypeOrder[17] =
{
    0,
    1,      // GMB_NET
    2,      // GMB_EMAIL
    3,      // GMB_NET | GMB_EMAIL
    4,      // GMB_ECHO
    5,
    6,
    7,
    8,      // GMB_NEWSGROUP
    9,
    10,
    11,
    12,     // GMB_ECHO | GMB_NEWSGROUP
    13,
    14,
    15,
    16      // GMB_LOCAL
};


//  ------------------------------------------------------------------
//  Areagroups compare

int compare_groups(int _ga, int _gb)
{
    int ga = _ga ? _ga : INT_MAX;
    int gb = _gb ? _gb : INT_MAX;

    const char *gap = NULL;
    const char *gbp = NULL;

    const char *g;

    for(g = CFG->arealistgrouporder; *g != NUL;)
    {

        int gr = getgroup(g);

        if(gr == ga)
            gap = g;

        if(gr == gb)
            gbp = g;

        if(*g == '#')
        {
            do
            {
                g++;
            }
            while(isdigit(*g));
        }
        else
            g++;
    }

    if(gap == NULL)
    {
        if(gbp != NULL)
            return 1;
        else
            return compare_two(ga, gb);
    }
    else
    {
        if(gbp == NULL)
            return -1;
        else
            return compare_two(gap, gbp);
    }
}


//  ------------------------------------------------------------------
//  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;

    bool sepfirst = false;

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

    while(*ptr)
    {
        switch(*ptr)
        {
        case '-':
            rev = true;
            A = b;
            B = a;
            break;
        case '+':
            rev = false;
            A = a;
            B = b;
            break;
        case 'A':
        case 'a':
            sepfirst = true;
            if((cmp = A->aka().compare(B->aka())) != 0)
                return cmp;
            break;
        case 'B':
        case 'b':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                if(B->board())
                    return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                if(A->board())
                    return sepfirst?1:-1;
            }
            else if((cmp = compare_two(A->board(), B->board())) != 0)
                return cmp;
            break;
        case 'D':
        case 'd':
            if((cmp = stricmp(A->desc(), B->desc())) != 0)
                return cmp;
            break;
        case 'E':
        case 'e':
            if((cmp = stricmp(A->echoid(), B->echoid())) != 0)
                return cmp;
            break;
        case 'F':
        case 'f':
            if(*area_maybe)
            {
                bool amay = make_bool(striinc(area_maybe, A->echoid()));
                bool bmay = make_bool(striinc(area_maybe, B->echoid()));

                if((cmp = compare_two(bmay, amay)) != 0)
                    return cmp;
            }
            break;
        case 'G':
        case 'g':
            sepfirst = true;
            if((cmp = compare_groups(A->groupid(), B->groupid())) != 0)
                return cmp;
            break;
        case 'M':
        case 'm':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                if(B->ismarked())
                    return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                if(A->ismarked())
                    return sepfirst?1:-1;
            }
            else if((cmp = compare_two(B->ismarked(), A->ismarked())) != 0)
                return cmp;
            break;
        case 'P':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                if(B->PMrk.Count())
                    return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                if(A->PMrk.Count())
                    return sepfirst?1:-1;
            }
            else
            {
                int aunread = A->PMrk.Count();
                int bunread = B->PMrk.Count();

                aunread = (rev or aunread) ? aunread : INT_MAX;
                bunread = (rev or bunread) ? bunread : INT_MAX;
                if((cmp = compare_two(aunread, bunread)) != 0)
                    return cmp;
            }
            break;
        case 'p':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                if(B->PMrk.Count())
                    return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                if(A->PMrk.Count())
                    return sepfirst?1:-1;
            }
            else if((cmp = compare_two(B->PMrk.Count()?1:0, A->PMrk.Count()?1:0)) != 0)
                return cmp;
            break;
        case 'O':
        case 'o':
            if((cmp = compare_two(A->areaid(), B->areaid())) != 0)
                return cmp;
            break;
        case 'T':
        case 't':
            sepfirst = true;
            if((cmp = compare_two(CFG->areatypeorder[A->type()&0xFF], CFG->areatypeorder[B->type()&0xFF])) != 0)
                return cmp;
            break;
        case 'U':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                if(B->unread)
                    return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                if(A->unread)
                    return sepfirst?1:-1;
            }
            else
            {
                int aunread = A->unread;
                int bunread = B->unread;

                aunread = (rev or aunread) ? aunread : INT_MAX;
                bunread = (rev or bunread) ? bunread : INT_MAX;
                if((cmp = compare_two(aunread, bunread)) != 0)
                    return cmp;
            }
            break;
        case 'u':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                if(B->unread)
                    return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                if(A->unread)
                    return sepfirst?1:-1;
            }
            else if((cmp = compare_two(B->unread?1:0, A->unread?1:0)) != 0)
                return cmp;
            break;
        case 'X':
        case 'x':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                return sepfirst?1:-1;
            }
            else if ((cmp = strcmp(A->basetype().c_str(), B->basetype().c_str())) != 0)
                return cmp;
            break;
        case 'Y':
        case 'y':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
            {
                if(B->isnewmail())
                    return sepfirst?-1:1;
            }
            else if(B->isseparator())
            {
                if(A->isnewmail())
                    return sepfirst?1:-1;
            }
            else if((cmp = compare_two(B->isnewmail(), A->isnewmail())) != 0)
                return cmp;
            break;
        case 'Z':
        case 'z':
            if(A->isseparator() and B->isseparator())
                break;
            else if(A->isseparator())
                return sepfirst?-1:1;
            else if(B->isseparator())
                return sepfirst?1:-1;
            else if((cmp = stricmp(A->path(), B->path())) != 0)
                return cmp;
            break;
        case 'S':
        case 's':
            if((cmp = compare_two(B->isseparator(), A->isseparator())) != 0)
                return cmp;
            break;
        }
        ptr++;
    }

    if(cmp == 0)
    {
        cmp = compare_two(B->isseparator(), A->isseparator());
    }

    return cmp;
}

static bool AreaListCmp2(const Area* a, const Area* b)
{

    return AreaListCmp(&a, &b) < 0;
}

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

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

    if(specs)
        strxcpy(sortspec, specs, sizeof(sortspec));
    else
        strcpy(sortspec, CFG->arealistsort);
    if(last == -1)
        last = idx.size();
    if(*sortspec)
    {
        std::sort(idx.begin()+first, idx.begin()+last, AreaListCmp2);
    }
}


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

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;
    int DESC_LEN;

    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, LGREY_|_BLACK);
    window.title(title, tattr);
    window.shadow(C_SHADOW);
    update();
}

void SelMaskPick::close()
{

    window.close();

}

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

#if defined(__USE_ALLOCA__)
    char *buf = (char*)alloca(DESC_LEN+3);
#else
    __extension__ char buf[DESC_LEN+3];
#endif

    *buf = ' ';
    strxcpy(buf+1, AL.alistselections[idx], DESC_LEN);
    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], DESC_LEN-1, LNG->SelectMarksEdit, H_SelectMarks);
            display_bar();
        }
        break;
    }

    return true;
}

void SelMaskPick::Run()
{

    DESC_LEN = (sizeof(Desc) > (MAXCOL-6)) ? MAXCOL-6 : sizeof(Desc);

    ypos = (MAXROW-18)/2;
    xpos = (MAXCOL-DESC_LEN-4)/2-1;
    ylen = 16;
    xlen = DESC_LEN+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;
}


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