//
//  $Log: Envoy.c,v $
//  Revision 1.7  1994/03/02  23:03:21  hakan
//  40.2-Kludge eingebaut
//
//  Revision 1.5  1993/12/11  23:44:14  hakan
//  ChipRevBitsValid eingepopelt
//
//  Revision 1.4  1993/11/30  19:59:48  hakan
//  So halt
//
//  Revision 1.3  1993/11/17  18:16:16  hakan
//  Übergang von AllocVec zu AllocMem
//
//  Revision 1.2  1993/10/03  17:13:22  hakan
//  Bis auf das popelige stop-gadget geht's...
//
//  Revision 1.1  1993/08/19  00:47:42  hakan
//  Initial revision
//
//

#include <config.h>
#include <machine.h>
#include <protoypes.h>

#include <exec/memory.h>

#include <ui-Mui.h>


#define MAXHOSTNAMELEN      40

#ifdef ENABLE_SERIAL_DEBUGGING
extern void KPrintF (char* format, ...);
#define D(x)    x
#else
#define D(x)    ;
#endif


static void ClearRealmList (void);
static void ClearHostList (void);
static void DoScanHosts (char* RealmToScan);
static void StartInquiryA (APTR Tags);
__saveds static ULONG ASM hookEntry(REG(a0) struct Hook *h, REG(a2) VOID *o, REG(a1) VOID *msg);
__saveds static ULONG DecodeNIPCInquiry (struct Hook *h, VOID *o, VOID *msg);
static void SortList (struct List* list);
static void SortAllLists (void);



static void*            Pool;
static long             HostCount;
static struct List      HostList;
static long             RealmCount;
static struct List      RealmList;

static struct Hook      h;
static unsigned long    retValue;

static BOOL             Kludge402;

static long             maxSeconds;




void SetupEnvoy (void)
{
    NewList (&HostList);
    NewList (&RealmList);

    h.h_Entry       = (ULONG (*)()) hookEntry;
    h.h_SubEntry    = DecodeNIPCInquiry;
    h.h_Data        = NULL;
}


void ShutDownEnvoy (void)
{
    ClearHostList ();
    ClearRealmList ();
}



BOOL getRealmServerRunning (void)
{
    char HostName [MAXHOSTNAMELEN+1];
    struct Entity*  local;
    BOOL RS = FALSE;


    if (local = CreateEntity (TAG_END))
    {
        if (GetHostName (local, HostName, MAXHOSTNAMELEN))
        {
            if (strchr (HostName, ':'))
            {
                //
                //  We have a ':' in our HostName ->
                //  We are in a network with a running Realmserver
                //

                RS = TRUE;
            }
        }

        DeleteEntity (local);
    }


    return RS;
}



static void StartInquiryA (APTR Tags)
{
    retValue   = TRUE;

    if (NIPCInquiryA (&h, NP_ScanTime, NP_ScanPackets, Tags))
    {
        ShowScan ();
    }
    else
    {
        DisplayBeep (NULL);
    }

    //
    //  Tell nipc to stop sending packets...
    //

    retValue = FALSE;
}



static void ClearRealmList (void)
{
    struct Node* work;
    struct Node* next;

    if (!(IsListEmpty (&RealmList)))
    {
        work = RealmList.lh_Head;
        while (next = work->ln_Succ)
        {
            FreeMem (work, sizeof (RealmNode));
            work = next;
        }
    }

    NewList (&(RealmList));
    RealmCount = 0;
}



static void ClearHostList (void)
{
    struct Node* work;
    struct Node* next;

    struct Node* innerwork;
    struct Node* innernext;

    Host*       innerhost;


    if (!(IsListEmpty (&HostList)))
    {
        work = HostList.lh_Head;
        while (next = work->ln_Succ)
        {
            innerhost = (Host*) work;

            if (!(IsListEmpty (&(innerhost->Entities))))
            {
                innerwork = innerhost->Entities.lh_Head;
                while (innernext = innerwork->ln_Succ)
                {
                    FreeMem (innerwork, sizeof (TextNode));
                    innerwork = innernext;
                }
            }

            if (!(IsListEmpty (&(innerhost->Services))))
            {
                innerwork = innerhost->Services.lh_Head;
                while (innernext = innerwork->ln_Succ)
                {
                    FreeMem (innerwork, sizeof (TextNode));
                    innerwork = innernext;
                }
            }

            FreeMem (work, sizeof (Host));
            work = next;
        }
    }

    NewList (&(HostList));
    HostCount = 0;
}



static void SortAllLists (void)
{
    if (!(IsListEmpty (&RealmList)))
    {
        SortList (&RealmList);
    }


    if (!(IsListEmpty (&HostList)))
    {
        struct Node*    work;
        struct List*    list;

        SortList (&HostList);

        for (work = (struct Node*) HostList.lh_Head; work->ln_Succ; work = work->ln_Succ)
        {
            list = (struct List*) (&((Host*) work)->Services);

            if (!(IsListEmpty (list)))
            {
                SortList (list);
            }

            list = (struct List*) (&((Host*) work)->Entities);

            if (!(IsListEmpty (list)))
            {
                SortList (list);
            }
        }
    }
}



static void SortList (struct List* list)
{
    struct Node* mynode;
    struct Node* my2node;
    BOOL         sorted;

    do
    {
        sorted = TRUE;
        for (mynode = (struct Node*) list->lh_Head; mynode->ln_Succ; mynode = mynode->ln_Succ)
        {
            if (mynode->ln_Succ->ln_Succ)
            {
                if (strcmp (mynode->ln_Name, mynode->ln_Succ->ln_Name) > 0)
                {
                    my2node = mynode->ln_Succ;

                    Remove (my2node);

                    my2node->ln_Pred         = mynode->ln_Pred;
                    my2node->ln_Succ         = mynode;

                    mynode->ln_Pred->ln_Succ = my2node;
                    mynode->ln_Pred          = my2node;

                    sorted = FALSE;
                }
            }
        }
    }
    while (!sorted);
}


__saveds static ULONG ASM hookEntry(REG(a0) struct Hook *h, REG(a2) VOID *o, REG(a1) VOID *msg)
{
    return ((*(ULONG (*)(struct Hook *,VOID *,VOID *))(h->h_SubEntry))(h, o, msg));
}



__saveds static ULONG DecodeNIPCInquiry (struct Hook *h, VOID *o, VOID *msg)
{
    TextNode*       text;
    RealmNode*      realm;
    Host*           host;

    struct TagItem* tstate;
    struct TagItem* tag;


    D (KPrintF ("DecodeNIPCInquiry() entered\n"));


    //
    //  The 'Object' is the address of the NetProbe Task.
    //  I've got no use for this...
    //

    //  if (o)
    //  {
    //  }


    //
    //  Here goes the 40.2-Kludge
    //

    if (Kludge402)
    {
        tstate = (struct TagItem*) msg;

        while (tag = NextTagItem (&tstate))
        {
            if (tag->ti_Tag ==  QUERY_NIPCVERSION)
            {
                if (tag->ti_Data < ((40 << 16) | 2))
                {
                    return TRUE;
                }
            }
        }
    }


    //
    //  Now, we need out working Host
    //

    tstate = (struct TagItem*) msg;

    while (tag = NextTagItem (&tstate))
    {
        if (tag->ti_Tag == QUERY_HOSTNAME)
        {
            if (!(host = (Host*) FindName (&(HostList), (char*) tag->ti_Data)))
            {
                if (!(host = (Host*) AllocMem (sizeof (Host), MEMF_PUBLIC | MEMF_CLEAR)))
                {
                    return FALSE;
                }

                strcpy (host->HostName, (char*) tag->ti_Data);
                host->m_node.ln_Name = host->HostName;

                AddHead (&(HostList), host);

                HostCount++;

                NewList (&(host->Entities));
                NewList (&(host->Services));
            }
        }
    }


    //
    //  Now, we fill in our real data
    //

    tstate = (struct TagItem*) msg;

    while (tag = NextTagItem (&tstate))
    {
        ULONG tidata = tag->ti_Data;

        switch (tag->ti_Tag)
        {
            case QUERY_IPADDR:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_IPADDR: '%ld.%ld.%ld.%ld'\n", ((tidata >> 24) & 0xff), ((tidata >> 16) & 0xff), ((tidata >> 8) & 0xff), (tidata & 0xff)));
                host->IPAddr = tidata;
                break;

            case QUERY_REALMS:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_REALMS: '%s'\n", (char*) tidata));
                if (!(realm = (RealmNode*) FindName (&(RealmList), (char*) tidata)))
                {
                    if (!(realm = AllocMem (sizeof (RealmNode), MEMF_PUBLIC|MEMF_CLEAR)))
                    {
                        return FALSE;
                    }

                    strcpy (realm->Name, (char*) tidata);
                    realm->r_node.ln_Name = realm->Name;

                    D (KPrintF ("DecodeNIPCInquiry:: '%s' added\n", realm->Name));
                    AddHead (&(RealmList), realm);

                    RealmCount++;
                }
                break;

            case QUERY_HOSTNAME:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_HOSTNAME: '%s'\n", (char*) tidata));
                break;

            case QUERY_SERVICE:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_SERVICE: '%s'\n", (char*) tidata));
                if (!(text = (TextNode*) FindName (&(host->Services), (char*) tidata)))
                {
                    if (!(text = AllocMem (sizeof (TextNode), MEMF_PUBLIC | MEMF_CLEAR)))
                    {
                        return FALSE;
                    }

                    strcpy (text->Text, (char*) tidata);
                    text->t_node.ln_Name = text->Text;
                    AddTail (&(host->Services), text);
                }
                break;

            case QUERY_ENTITY:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_ENTITY: '%s'\n", (char*) tidata));
                if (!(text = (TextNode*) FindName (&(host->Entities), (char*) tidata)))
                {
                    if (!(text = AllocMem (sizeof (TextNode), MEMF_PUBLIC | MEMF_CLEAR)))
                    {
                        return FALSE;
                    }

                    strcpy (text->Text, (char*) tidata);
                    text->t_node.ln_Name = text->Text;
                    AddTail (&(host->Entities), text);
                }
                break;

            case QUERY_OWNER:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_OWNER: '%s'\n", (char*) tidata));
                strcpy (host->Owner, (char*) tidata);
                break;

            case QUERY_MACHDESC:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_MACHDESC: '%08lx'\n", tidata));
                break;

            case QUERY_ATTNFLAGS:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_ATTNFLAGS: '%08lx'\n", tidata));
                host->ATTNFlags = tidata;
                break;

            case QUERY_LIBVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_LIBVERSION: '%08lx'\n", tidata));
                break;

            case QUERY_CHIPREVBITS:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_CHIPREVBITS: '%08lx'\n", tidata));
                host->ChipRevBits = tidata;
                break;

            case QUERY_MAXFASTMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_MAXFASTMEM: '%8ld'\n", tidata));
                host->MaxFastMem = tidata;
                break;

            case QUERY_AVAILFASTMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_AVAILFASTMEM: '%8ld'\n", tidata));
                host->AvailChipMem = tidata;
                break;

            case QUERY_MAXCHIPMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_MAXCHIPMEM: '%8ld'\n", tidata));
                host->MaxChipMem = tidata;
                break;

            case QUERY_AVAILCHIPMEM:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_AVAILCHIPMEM: '%8ld'\n", tidata));
                host->AvailChipMem = tidata;
                break;

            case QUERY_KICKVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_KICKVERSION: '%ld.%ld'\n", ((tidata >> 16) & 0xff), (tidata & 0xff)));
                host->KickVersion = tidata;
                break;

            case QUERY_WBVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_WBVERSION: '%ld.%ld'\n", ((tidata >> 16) & 0xff), (tidata & 0xff)));
                host->WBVersion = tidata;
                break;

            case QUERY_NIPCVERSION:
                D (KPrintF ("DecodeNIPCInquiry::QUERY_NIPCVERSION: '%ld.%ld'\n", ((tidata >> 16) & 0xff), (tidata & 0xff)));
                host->NIPCVersion = tidata;
                break;

            default:
                break;
        }
    }
    

    D (KPrintF ("DecodeNIPCInquiry() left\n\n"));

    return retValue;
}



void ScanRealms (void)
{
    struct TagItem queryTags[] =
    {
        QUERY_REALMS,   0,
        TAG_END,        0,
    };



    ClearRealmList ();
    SetupFuelGauge (0, NP_ScanTime);
    StartInquiryA ((APTR) &queryTags);
    ShutDownFuelGauge ();

    SortAllLists ();
}



void ScanHosts (char* realmname)
{
    ClearHostList ();

    SetupFuelGauge (0, NP_ScanTime);

    DoScanHosts (realmname);

    ShutDownFuelGauge ();

    SortAllLists ();
}



void ScanAllHosts (void)
{
    struct Node*    realm;
    long            round;


    ScanRealms ();

    ClearHostList ();

    maxSeconds  = RealmCount * NP_ScanTime;
    round       = 0;

    SetupFuelGauge (0, maxSeconds);

    for (realm = getRealmList()->lh_Head; realm->ln_Succ; realm = realm->ln_Succ)
    {
        SetupFuelGauge (NP_ScanTime * (round++), maxSeconds);

        DoScanHosts (realm->ln_Name);
    }

    ShutDownFuelGauge ();

    SortAllLists ();
}



static void DoScanHosts (char* RealmToScan)
{
    long n = 0;
    struct TagItem queryTags [100];



    //
    //  Do we need our filter-kludge?
    //
    
    if (NP_Configuration & NP_402_Kludge)
    {
        Kludge402 = TRUE;
    }
    else
    {
        Kludge402 = FALSE;
    }


    //
    //  Build the Tag array
    //

    //
    //  We ALWAYS want to hear our Hostname
    //

    queryTags [n].ti_Tag    = (ULONG) QUERY_HOSTNAME;
    queryTags [n].ti_Data   = (ULONG) 0;
    n++;


    //
    //  Maybe a realm,  too?
    //

    if (RealmToScan)
    {
        queryTags [n].ti_Tag  = (ULONG) MATCH_REALM;
        queryTags [n].ti_Data = (ULONG) RealmToScan;
        n++;
    }


    //
    //  Whom does this Host belong?
    //

    if (NP_Configuration & NP_Owner)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_OWNER;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What IP-Adress do we have?  (Might not be supported in later versions)
    //

    if (NP_Configuration & NP_IPAddr)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_IPADDR;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Tell us your Kickstart-version
    //

    if (NP_Configuration & NP_KickVersion)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_KICKVERSION;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Tell your Workbench, too
    //

    if (NP_Configuration & NP_WBVersion)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_WBVERSION;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Some might care about the NIPC-Library
    //

    if (NP_Configuration & NP_NIPCVersion)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_NIPCVERSION;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What kind of Fast Memory do we have?
    //

    if (NP_Configuration & NP_MaxFastMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_MAXFASTMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }

    if (NP_Configuration & NP_AvailFastMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_AVAILFASTMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What kind of Chip Memory do we have?
    //

    if (NP_Configuration & NP_MaxChipMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_MAXCHIPMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }

    if (NP_Configuration & NP_AvailChipMem)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_AVAILCHIPMEM;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  How about our Processor / FPU
    //

    if (NP_Configuration & NP_ATTNFlags)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_ATTNFLAGS;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  What Custom chips do we have?
    //

    if (NP_Configuration & NP_ChipRevBits)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_CHIPREVBITS;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Show us all your Services
    //

    if (NP_Configuration & NP_Services)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_SERVICE;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Show all your Entities, too
    //

    if (NP_Configuration & NP_Entities)
    {
        queryTags [n].ti_Tag    = (ULONG) QUERY_ENTITY;
        queryTags [n].ti_Data   = (ULONG) 0;
        n++;
    }


    //
    //  Simply clean up
    //

    queryTags [n].ti_Tag        = (ULONG) TAG_END;
    queryTags [n].ti_Data       = (ULONG) 0;


    //
    //  Start the Inquiry
    //

    StartInquiryA ((APTR) &queryTags);
}



long getRealmCount (void)
{
    return RealmCount;
}



struct List* getRealmList (void)
{
    return &(RealmList);
}



long getHostCount (void)
{
    return HostCount;
}



struct List* getHostList (void)
{
    return &(HostList);
}



Host* getHost (char* hostname)
{
    return ((Host*) FindName (&(HostList), (char*) hostname));
}
