//  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$
//  ------------------------------------------------------------------
//  GoldED nodelist indexing.
//  ------------------------------------------------------------------

#include <gcrcall.h>
#include <gfilutil.h>
#include <gmemdbg.h>
#include <gstrall.h>
#include <gftnnlge.h>
#include <stdlib.h>

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

static char* make_goldnode_name(char* inp)
{

    if(*inp == NUL)
        return inp;

    char buf[100];
    char* p;
    char* q;

    // Convert underlines to spaces
    p = inp;
    while(*p)
        if(*(p++) == '_')
            *(p-1) = ' ';

    // Strip leading spaces
    p = inp;
    while(isspace(*p))
        p++;
    q = &inp[strlen(p)-1];

    // Strip trailing spaces
    while(isspace(*q))
        *q-- = NUL;

    // Search for last space or point
    while(*q != ' ' and *q != '.' and q > p)
        q--;

    // If last char is a point, find last space instead
    if(*(q+1) == 0)
        while(*q != ' ' and q > p)
            q--;

    // Exchange last name and first name(s)
    if(p != q)
    {
        strcpy(stpcpy(buf, q+1), ", ");
        *(q+(*q == '.' ? 1 : 0)) = NUL;
        strcat(buf, p);
        strcpy(inp, buf);
    }
    struplow(inp);

    return inp;
}


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

void ftn_golded_nodelist_index::fetchdata()
{

    int currfileno = (int)((current.pos >> 24) & 0xFF);

    // currfileno == 0xFF: Entry is taken from a userlist, no nodelist available
    if(currfileno != lastfileno)
    {
        if(fhx != -1)
            ::close(fhx);

        if(currfileno != 0xff)
        {
            fhx = ::sopen(nodelist[currfileno].filename, O_RDONLY|O_BINARY, SH_DENYNO, S_STDRD);
            lastfileno = currfileno;
        }
        else
        {
            fhx = -1;
            lastfileno = -1;
        }
    }

    strunrevname(data.name, current.name);
    *data.status = NUL;
    *data.system = NUL;
    *data.location = NUL;
    *data.phone = NUL;
    *data.flags = NUL;
    *data.baud = NUL;

    if(fhx != -1)
    {

        char buf[256];
        buf[255] = NUL;

        lseekset(fhx, current.pos & 0x00FFFFFFL);
        read(fhx, buf, 255);
        if(*buf != ';')
        {

            /*
                  char* end = strchr(buf, '\r');
                  if(end)
                    *end = NUL;
            */
            strtok(buf,"\r\n");

            data.unpack(buf);
        }
    }

    data.addr = current.addr;
    data.addr.make_string(data.address);
}


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

void ftn_golded_nodelist_index::getnode()
{

#ifdef DEBUG
    printf("<%05ld> ", node);
#endif

    long seekto = node - 1L;
    if(not namebrowse)
    {
        if(index32)
        {
            lseekset(fha, seekto*(long)sizeof(dword));
            dword nseekto;
            read(fha, &nseekto, sizeof(dword));
            seekto = nseekto;
        }
        else
        {
            lseekset(fha, seekto*(long)sizeof(word));
            word nseekto;
            read(fha, &nseekto, sizeof(word));
            seekto = nseekto;
        }
    }
    lseekset(fhn, seekto*(long)sizeof(_GEIdx));
    read(fhn, &current, sizeof(_GEIdx));
}


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

int ftn_golded_nodelist_index::namecmp() const
{

#ifdef DEBUG
    printf("[%s] [%s] ", searchname, current.name);
#endif

    const char* a = searchname;
    const char* b = current.name;
    int n = 1;
    int d;
    while(1)
    {
        d = g_tolower(*a) - g_tolower(*b);
        if((d != 0) or (*a == NUL) or (*b == NUL))
            break;
        a++;
        b++;
        n++;
    }
    return d != 0 ? (d > 0 ? n : -n) : 0;
}


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

int ftn_golded_nodelist_index::addrcmp() const
{

#ifdef DEBUG
    printf("[%d:%d/%d.%d] [%d:%d/%d.%d] ",
           searchaddr.zone,  searchaddr.net,  searchaddr.node,  searchaddr.point,
           current.addr.zone, current.addr.net, current.addr.node, current.addr.point
          );
#endif

    return searchaddr.compare(current.addr);
}


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

bool ftn_golded_nodelist_index::searchfirst()
{

    if(maxnode)
    {

        long mid;
        long left = 0;
        long right = maxnode;
        int diff = 0;
        int prevdiff;

        do
        {
            mid = (left+right)/2;
            node = mid + 1;
            getnode();
            prevdiff = diff;
            diff = namebrowse ? namecmp() : addrcmp();
#ifdef DEBUG
            printf("(%d)\n", diff);
#endif
            if(diff < 0)
                right = mid - 1;
            else if(diff > 0)
                left = mid + 1;
            else
            {
                exactmatch = true;
                fetchdata();
                return true;
            }
        }
        while(left < right);

        if(left < maxnode)
        {
            node = left + 1;
            getnode();
            prevdiff = diff;
            diff = namebrowse ? namecmp() : addrcmp();
#ifdef DEBUG
            printf("(%d)\n", diff);
#endif
            if(diff == 0)
            {
                exactmatch = true;
                fetchdata();
                return true;
            }
        }

        if(absolute(prevdiff) > absolute(diff))
            previous();
        else
        {
            prevdiff = diff;
            if(next())
            {
                diff = namebrowse ? namecmp() : addrcmp();
#ifdef DEBUG
                printf("(%d)\n", diff);
#endif
                if(absolute(prevdiff) >= absolute(diff))
                    previous();
            }
            else
                fetchdata();
        }
    }

    exactmatch = false;
    return false;
}


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

bool ftn_golded_nodelist_index::search()
{

    if(searchfirst())
    {
        // Search backwards until first exact match is found
        while(previous())
        {
            if(not exactmatch)
            {
                next();
                break;
            }
        }
        return true;
    }

    return false;
}


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

ftn_golded_nodelist_index::ftn_golded_nodelist_index()
{

    fha = fhn = fhx = -1;
    nodelist = NULL;
    nodelists = 0;
    lastfileno = -1;
    isopen = false;
}


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

ftn_golded_nodelist_index::~ftn_golded_nodelist_index()
{

    if(isopen)
        close();
}


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

bool ftn_golded_nodelist_index::open()
{

    if(isopen)
        close();

    fha = ::sopen(AddPath(nlpath, "goldnode.gxa"), O_RDONLY|O_BINARY, SH_DENYNO, S_STDRD);
    if(fha == -1)
    {
        close();
        return false;
    }

    fhn = ::sopen(AddPath(nlpath, "goldnode.gxn"), O_RDONLY|O_BINARY, SH_DENYNO, S_STDRD);
    if(fhn == -1)
    {
        close();
        return false;
    }

    FILE* fp = fopen(AddPath(nlpath, "goldnode.gxl"), "rt");
    if(fp == NULL)
    {
        close();
        return false;
    }

    // Read the list index
    char buf[256];
    nodelists = 0;
    while(fgets(buf, sizeof(buf), fp))
    {
        nodelist = (fstamp*)throw_realloc(nodelist, (nodelists+1)*sizeof(fstamp));
        char* key;
        char* val=buf;
        getkeyval(&key, &val);
        strcpy(nodelist[nodelists].filename, key);
        MapPath(nodelist[nodelists].filename);
        nodelist[nodelists].stamp = atol(val);
        nodelists++;
    }
    fclose(fp);

    maxnode = filelength(fha) / sizeof(word);
    if(filelength(fhn) / sizeof(_GEIdx) < (size_t)maxnode)
    {
        maxnode = filelength(fha) / sizeof(dword);
        index32 = true;
    }
    else
        index32 = false;

    isopen = true;

    return true;
}


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

void ftn_golded_nodelist_index::close()
{

    throw_release(nodelist);
    nodelists = 0;

    lastfileno = -1;

    if(fha != -1)  ::close(fha);
    fha = -1;
    if(fhn != -1)  ::close(fhn);
    fhn = -1;
    if(fhx != -1)  ::close(fhx);
    fhx = -1;

    isopen = false;
}


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

bool ftn_golded_nodelist_index::find(const char* lookup_name)
{

    namebrowse = true;
    make_goldnode_name(strcpy(searchname, lookup_name));
    return search();
}


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

bool ftn_golded_nodelist_index::find(const ftn_addr& addr)
{

    namebrowse = false;
    searchaddr = addr;
    return search();
}


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

bool ftn_golded_nodelist_index::previous()
{

    if(node > 1)
    {
        node--;
        getnode();
        fetchdata();
        compare();
        return true;
    }

    return false;
}


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

bool ftn_golded_nodelist_index::next()
{

    if(node < maxnode)
    {
        node++;
        getnode();
        fetchdata();
        compare();
        return true;
    }

    return false;
}


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

void ftn_golded_nodelist_index::first()
{

    node = 1;
    getnode();
    fetchdata();
    compare();
}


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

void ftn_golded_nodelist_index::last()
{

    node = maxnode;
    getnode();
    fetchdata();
    compare();
}


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

void ftn_golded_nodelist_index::push_state()
{

    statenode = node;
}


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

void ftn_golded_nodelist_index::pop_state()
{

    node = statenode;
    getnode();
    fetchdata();
    compare();
}


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

const char* ftn_golded_nodelist_index::index_name() const
{

#if defined(__UNIX__)
    return namebrowse ? "goldnode.gxn" : "goldnode.gxa";
#else
    return namebrowse ? "GOLDNODE.GXN" : "GOLDNODE.GXA";
#endif
}


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

const char* ftn_golded_nodelist_index::nodelist_name() const
{

    if(lastfileno == -1)
        return index_name();

    const char* ptr = strrchr(nodelist[lastfileno].filename, '\\');
    return ptr ? ptr+1 : nodelist[lastfileno].filename;
}


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