//
// query.c
//
// perform database queries
//
#include <stddef.h>
#include <string.h>
#if defined(OS2)
#define INCL_NOCOMMON
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DOSFILEMGR
#define INCL_DOSERRORS
#define INCL_DOSMISC
#include <os2.h>
#else
#include <windows.h>
#endif

#include <dos.h>

#include "hungary.h"
#include "bsc.h"
#include "bscsup.h"

// these keep track of the current query, they are globally visible so
// that users can see how the query is progressing
//
// you may not write on these

IDX far idxQyStart;
IDX far idxQyCur;
IDX far idxQyMac;

// this is auxilliary information about the current bob which some
// queries may choose to make available
//

static BOOL fWorking;
static LSZ lszModLast = NULL;	// for removing duplicate modules

// prototypes for the query worker functions
//

static BOB BobQyFiles(VOID);
static BOB BobQySymbols (VOID);
static BOB BobQyContains (VOID);
static BOB BobQyCalls (VOID);
static BOB BobQyCalledBy (VOID);
static BOB BobQyUses (VOID);
static BOB BobQyUsedBy (VOID);
static BOB BobQyUsedIn (VOID);
static BOB BobQyDefinedIn(VOID);
static BOB BobQyRefs(VOID);
static BOB BobQyDefs(VOID);

// current bob worker function
static BOB (*bobFn)(VOID) = NULL;

BOOL BSC_API
InitBSCQuery (QY qy, BOB bob)
// do the request query on the given bob
//
{
    fWorking = FALSE;

    if (lszModLast == NULL)
	lszModLast = LpvAllocCb(1024);	// REVIEW -- how much to alloc? [rm]

    // no memory -- no query
    if (lszModLast == NULL)
	return FALSE;

    strcpy(lszModLast, "");

    switch (qy) {

    case qyFiles:
	bobFn	   = BobQyFiles;
	idxQyStart = (IDX)0;
	idxQyMac   = (IDX)ImodMac();
	break;

    case qySymbols:
	bobFn	   = BobQySymbols;
	idxQyStart = (IDX)0;
	idxQyMac   = (IDX)IinstMac();
	break;

    case qyContains:
	{
	IMS ims, imsMac;

	bobFn	   = BobQyContains;

	if (ClsOfBob(bob) != clsMod) return FALSE;
	MsRangeOfMod(ImodFrBob(bob), &ims, &imsMac);

	idxQyStart = (IDX)ims;
	idxQyMac   = (IDX)imsMac;

	break;
	}

    case qyCalls:
	{
	IUSE iuse, iuseMac;

	bobFn	   = BobQyCalls;
	if (ClsOfBob(bob) != clsInst) return FALSE;
	UseRangeOfInst(IinstFrBob(bob), &iuse, &iuseMac);

	idxQyStart = (IDX)iuse;
	idxQyMac   = (IDX)iuseMac;

	break;
	}

    case qyUses:
	{
	IUSE iuse, iuseMac;

	bobFn	   = BobQyUses;
	if (ClsOfBob(bob) != clsInst) return FALSE;
	UseRangeOfInst(IinstFrBob(bob), &iuse, &iuseMac);

	idxQyStart = (IDX)iuse;
	idxQyMac   = (IDX)iuseMac;

	break;
	}

    case qyCalledBy:
	{
	IUBY iuby, iubyMac;

	bobFn	   = BobQyCalledBy;
	if (ClsOfBob(bob) != clsInst) return FALSE;
	UbyRangeOfInst(IinstFrBob(bob), &iuby, &iubyMac);

	idxQyStart = (IDX)iuby;
	idxQyMac   = (IDX)iubyMac;

	break;
	}

    case qyUsedBy:
	{
	IUBY iuby, iubyMac;

	bobFn	   = BobQyUsedBy;
	if (ClsOfBob(bob) != clsInst) return FALSE;
	UbyRangeOfInst(IinstFrBob(bob), &iuby, &iubyMac);

	idxQyStart = (IDX)iuby;
	idxQyMac   = (IDX)iubyMac;

	break;
	}

    case qyUsedIn:
	{
	IREF iref, irefMac;

	bobFn	   = BobQyUsedIn;
	if (ClsOfBob(bob) != clsInst) return FALSE;
	RefRangeOfInst(IinstFrBob(bob), &iref, &irefMac);

	idxQyStart = (IDX)iref;
	idxQyMac   = (IDX)irefMac;

	break;
	}

    case qyDefinedIn:
	{
	IDEF idef, idefMac;

	bobFn	   = BobQyDefinedIn;
	if (ClsOfBob(bob) != clsInst) return FALSE;
	DefRangeOfInst(IinstFrBob(bob), &idef, &idefMac);

	idxQyStart = (IDX)idef;
	idxQyMac   = (IDX)idefMac;

	break;
	}

    case qyRefs:
	{
	IINST iinst, iinstMac;

	bobFn	   = BobQyRefs;

	switch (ClsOfBob(bob)) {

	default:
	    return FALSE;

	case clsSym:
	    InstRangeOfSym(IsymFrBob(bob), &iinst, &iinstMac);

	    idxQyStart = (IDX)iinst;
	    idxQyMac   = (IDX)iinstMac;
	    break;

	case clsInst:
	    idxQyStart = (IDX)IinstFrBob(bob);
	    idxQyMac   = idxQyStart+1;
	    break;
	}

	break;
	}

    case qyDefs:
	{
	IINST iinst, iinstMac;

	bobFn	   = BobQyDefs;

	switch (ClsOfBob(bob)) {

	default:
	    return FALSE;

	case clsSym:
	    InstRangeOfSym(IsymFrBob(bob), &iinst, &iinstMac);

	    idxQyStart = (IDX)iinst;
	    idxQyMac   = (IDX)iinstMac;
	    break;

	case clsInst:
	    idxQyStart = (IDX)IinstFrBob(bob);
	    idxQyMac   = idxQyStart+1;
	    break;
	}

	break;
	}
    }

    idxQyCur   = idxQyStart;
    return TRUE;
}

BOB BSC_API
BobNext()
// return the next Bob in the query
{
    if (idxQyCur < idxQyMac && bobFn != NULL)
	return (*bobFn)();

    return bobNil;
}

static BOB
BobQyFiles()
// return the next File in a file query
//
{
    BOB bob;

    while (idxQyCur < idxQyMac) {
	IMS ims1, ims2;

	MsRangeOfMod((IMOD)idxQyCur, &ims1, &ims2);
	if (ims1 != ims2) {
	    bob = BobFrClsIdx(clsMod, idxQyCur);
	    idxQyCur++;
	    return bob;
	}
	else
	    idxQyCur++;
    }
    return bobNil;
}

static BOB
BobQySymbols ()
// get the next symbol in a symbol query
//
{
    BOB bob;

    bob = BobFrClsIdx(clsInst, idxQyCur);
    idxQyCur++;
    return bob;
}

static BOB
BobQyContains ()
// get the next symbol in a contains query
//
{
    BOB bob;

    bob = BobFrClsIdx(clsInst, IinstOfIms((IMS)idxQyCur));
    idxQyCur++;
    return bob;
}

static BOB
BobQyCalls ()
// get the next symbol which query focus calls
//
{
    WORD cuse;
    IINST iinst;
    ISYM  isym;
    TYP typ;
    ATR atr;
    BOB bob;
	
    for (; idxQyCur < idxQyMac; idxQyCur++) {

	UseInfo((IUSE)idxQyCur, &iinst, &cuse);
	InstInfo(iinst, &isym, &typ, &atr);

	if (typ > INST_TYP_LABEL)
	    continue;

	bob = BobFrClsIdx(clsInst, iinst);
	idxQyCur++;
	return bob;
    }
    return bobNil;
}

static BOB
BobQyCalledBy ()
// get the next symbol which query focus is called by
//
{
    WORD cuse;
    IINST iinst;
    ISYM  isym;
    TYP typ;
    ATR atr;
    BOB bob;
	
    for (; idxQyCur < idxQyMac; idxQyCur++) {

	UbyInfo((IUBY)idxQyCur, &iinst, &cuse);
	InstInfo(iinst, &isym, &typ, &atr);

	if (typ > INST_TYP_LABEL)
	    continue;

	bob = BobFrClsIdx(clsInst, iinst);
	idxQyCur++;
	return bob;
    }
    return bobNil;
}

static BOB
BobQyUses ()
// get the next symbol which query focus calls
//
{
    WORD cuse;
    IINST iinst;
    BOB bob;
	
    UseInfo((IUSE)idxQyCur, &iinst, &cuse);
    bob = BobFrClsIdx(clsInst, iinst);
    idxQyCur++;
    return bob;
}

static BOB
BobQyUsedBy ()
// get the next symbol which query focus calls
//
{
    WORD cuse;
    IINST iinst;
    BOB bob;
	
    UbyInfo((IUBY)idxQyCur, &iinst, &cuse);
    bob = BobFrClsIdx(clsInst, iinst);
    idxQyCur++;
    return bob;
}

static BOB
BobQyUsedIn ()
// get the next module which query focus is used in
//
{
    WORD wLine;
    BOB  bob;
    LSZ  lszMod;

    for ( ; idxQyCur < idxQyMac ; idxQyCur++) {
	RefInfo((IREF)idxQyCur, &lszMod, &wLine);
	
        if (strcmp(lszMod, lszModLast) == 0)
	    continue;

        strcpy(lszModLast, lszMod);

	bob = BobFrClsIdx(clsMod, ImodFrLsz(lszMod));
	idxQyCur++;
	return bob;
    }
    return bobNil;
}

static BOB
BobQyDefinedIn ()
// get the next module which query focus is defined in
//
{
    WORD wLine;
    LSZ  lszMod;
    BOB  bob;

    for ( ; idxQyCur < idxQyMac ; idxQyCur++) {
	DefInfo((IDEF)idxQyCur, &lszMod, &wLine);
	
        if (strcmp(lszMod, lszModLast) == 0)
	    continue;

        strcpy(lszModLast, lszMod);

	bob = BobFrClsIdx(clsMod, ImodFrLsz(lszMod));
	idxQyCur++;
	return bob;
    }
    return bobNil;
}

LSZ BSC_API
LszNameFrBob(BOB bob)
// return the name of the given bob
//
{
    switch (ClsOfBob(bob)) {

    case clsMod:
	return LszNameFrMod(ImodFrBob(bob));

    case clsSym:
	return LszNameFrSym(IsymFrBob(bob));

    case clsInst:
	{
	ISYM isym;
	TYP typ;
	ATR atr;

	InstInfo(IinstFrBob(bob), &isym, &typ, &atr);
	return LszNameFrSym(isym);
	}

    case clsRef:
	{
	LSZ lsz;
	WORD wLine;

	RefInfo(IrefFrBob(bob), &lsz, &wLine);
	return lsz;
	}

    case clsDef:
	{
	LSZ lsz;
	WORD wLine;

	DefInfo(IdefFrBob(bob), &lsz, &wLine);
	return lsz;
	}

    default:
	return "?";
    }
}

BOB BSC_API
BobFrName(LSZ lszName)
// return the best bob we can find from the given name
//
{
    ISYM isym;
    IMOD imod, imodMac;
    IINST iinst, iinstMac;

    if ((isym = IsymFrLsz(lszName)) != isymNil) {
	InstRangeOfSym(isym, &iinst, &iinstMac);
	return BobFrClsIdx(clsInst, iinst);
    }

    if ((imod = ImodFrLsz(lszName)) != imodNil) {
	return BobFrClsIdx(clsMod, imod);
    }

    imodMac = ImodMac();

    // no exact match -- try short names
    lszName = LszBaseName(lszName);
    for (imod = 0; imod < imodMac; imod++) 
        if (_stricmp(lszName, LszBaseName(LszNameFrMod(imod))) == 0)
	    return BobFrClsIdx(clsMod, imod);

    return bobNil;
}

static BOB
BobQyRefs()
// return the next File in a file query
//
{
    BOB bob;
    static IREF iref, irefMac;

    for (;;) {
	if (!fWorking) {
	    for ( ; idxQyCur < idxQyMac ; idxQyCur++) {

		RefRangeOfInst((IINST)idxQyCur, &iref, &irefMac);
		if (iref != irefMac) 
		    break;
	    }
	    if (idxQyCur >= idxQyMac)
		    return bobNil;

	    fWorking = TRUE;
	}

	if (iref < irefMac) {
	    bob = BobFrClsIdx(clsRef, iref);
	    iref++;
	    return bob;
	}

	idxQyCur++;
	fWorking = FALSE;
    }
}

static BOB
BobQyDefs()
// return the next File in a file query
//
{
    BOB bob;
    static IDEF idef, idefMac;

    for (;;) {
	if (!fWorking) {
	    for ( ; idxQyCur < idxQyMac ; idxQyCur++) {

		DefRangeOfInst((IINST)idxQyCur, &idef, &idefMac);
		if (idef != idefMac) 
		    break;
	    }
	    if (idxQyCur >= idxQyMac)
		    return bobNil;

	    fWorking = TRUE;
	}

	if (idef < idefMac) {
	    bob = BobFrClsIdx(clsDef, idef);
	    idef++;
	    return bob;
	}

	idxQyCur++;
	fWorking = FALSE;
    }
}