//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       Stack.cpp
//
//  History:    31-Mar-1997 pberkman   created
//
//--------------------------------------------------------------------------

#include    "global.hxx"
#include    "Stack.hxx"

Stack_::Stack_(CRITICAL_SECTION *pCS)
{
    dwStackCount            = 0;
    psBottom                = NULL;
    ppsSorted               = NULL;
    ppsGet                  = NULL;
    pSortCriticalSection    = pCS;
}

Stack_::~Stack_(void)
{
    if (psBottom)
    {
        StackStruct_    *psCurrent;

        for (;;)
        {
            psCurrent = psBottom->psNext;

            delete psBottom->pvData;
            delete psBottom;

            psBottom    = psCurrent;

            if (!(psBottom))
            {
                break;
            }
        }
    }

    DELETE_OBJECT(ppsSorted);

    FlushGetStack();
}

BOOL Stack_::Add(DWORD cbData, void *pvData)
{
    BYTE    *pb;

    if (pb = (BYTE *)this->Add(cbData))
    {
        memcpy(pb, pvData, cbData);
        return(TRUE);
    }

    return(FALSE);
}

void *Stack_::Add(DWORD cbData)
{
    StackStruct_    *psWork;

    psWork = new StackStruct_;

    if (!(psWork))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return(NULL);
    }

    psWork->psNext = psBottom;

    psWork->pvData = new BYTE[cbData];

    if (!(psWork->pvData))
    {
        delete psWork;

        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return(FALSE);
    }

    psWork->cbData = cbData;

    psBottom = psWork;

    dwStackCount++;

    FlushGetStack();

    return((void *)psWork->pvData);
}

void *Stack_::Get(DWORD dwPosition, DWORD *cbData)
{
    DWORD           i;
    StackStruct_    *psRet;

    if (!(psBottom))
    {
        return(NULL);
    }

    if (dwPosition > (dwStackCount - 1))
    {
        return(NULL);
    }

    if ( InitGetStackIfNecessary() == TRUE )
    {
        DWORD Index = dwPosition;

        if ( cbData != NULL )
        {
            *cbData = ppsGet[ Index ]->cbData;
        }

        return( ppsGet[ Index ]->pvData );
    }

    psRet = psBottom;

    if (cbData)
    {
        *cbData = 0;
    }

    for (i = 0; i < dwPosition; i++)
    {
        psRet = psRet->psNext;
    }

    if ((psRet) && (psRet->pvData))
    {
        if (cbData)
        {
            *cbData = psRet->cbData;
        }

        return(psRet->pvData);
    }

    return(NULL);
}

void *Stack_::Get(DWORD cbStartIn_pvData, DWORD cbLengthIn_pvData, BYTE fbType, void *pvMemberOf_pvData)
{
    DWORD   dwEnd;
    DWORD   dwMiddle;
    DWORD   dwStart;
    DWORD   dwHalf;
    DWORD   dwCur;
    void    *pv;
    int     cmp;

    dwStart     = 0;
    dwEnd       = this->Count();
    dwHalf = dwMiddle    = dwEnd / 2L;
    dwCur       = 0;

    for EVER
    {
        if (dwCur++ > this->Count())
        {
            break;
        }

        pv = this->Get(dwMiddle);

        if (!(pv))
        {
            break;
        }

        switch (fbType)
        {
	    case STACK_SORTTYPE_PWSZ:
                cmp = wcscmp(*(WCHAR **)((BYTE *)pv + cbStartIn_pvData), (WCHAR *)pvMemberOf_pvData);
                break;

            case STACK_SORTTYPE_PSZ:
                cmp = strcmp(*(char **)((BYTE *)pv + cbStartIn_pvData), (char *)pvMemberOf_pvData);
                break;

	    case STACK_SORTTYPE_PWSZ_I:
		cmp = _wcsicmp(*(WCHAR **)((BYTE *)pv + cbStartIn_pvData), (WCHAR *)pvMemberOf_pvData);
                break;

	    case STACK_SORTTYPE_PSZ_I:
		cmp = _stricmp(*(char **)((BYTE *)pv + cbStartIn_pvData), (char *)pvMemberOf_pvData);
                break;

            default:
                cmp = memcmp((BYTE *)pv + cbStartIn_pvData, (BYTE *)pvMemberOf_pvData, cbLengthIn_pvData);
                break;
        }

        if (cmp == 0)
        {
            return(pv);
        }

        if ((dwMiddle == 0) || (dwMiddle == this->Count()) ||
            ((dwHalf == 0) && (dwMiddle == dwStart)))
        {
            break;
        }

        if (cmp < 0)
        {
            dwEnd   = dwMiddle;
        }
        else
        {
            dwStart = dwMiddle;
        }

        dwHalf      = (dwEnd - dwStart) / 2L;
        dwMiddle    = dwStart + dwHalf;
    }

    return(NULL);
}

static DWORD            cbSortOffset;
static DWORD            cbSortLength;
static BYTE             fbType;

int __cdecl Stack_compare(const void *arg1, const void *arg2)
{
    StackStruct_    **p1 = (StackStruct_ **)arg1;
    StackStruct_    **p2 = (StackStruct_ **)arg2;

    switch (fbType)
    {
	case STACK_SORTTYPE_PWSZ:
                return(wcscmp(*(WCHAR **)((BYTE *)(*p1)->pvData + cbSortOffset),
                              *(WCHAR **)((BYTE *)(*p2)->pvData + cbSortOffset)));


        case STACK_SORTTYPE_PSZ:
                return(strcmp(*(char **)((BYTE *)(*p1)->pvData + cbSortOffset),
                              *(char **)((BYTE *)(*p2)->pvData + cbSortOffset)));

	case STACK_SORTTYPE_PWSZ_I:
		return(_wcsicmp(*(WCHAR **)((BYTE *)(*p1)->pvData + cbSortOffset),
                              *(WCHAR **)((BYTE *)(*p2)->pvData + cbSortOffset)));


	case STACK_SORTTYPE_PSZ_I:
		return(_stricmp(*(char **)((BYTE *)(*p1)->pvData + cbSortOffset),
                              *(char **)((BYTE *)(*p2)->pvData + cbSortOffset)));

        case STACK_SORTTYPE_BINARY:
        default:
            break;
    }

    return(memcmp((void **)((BYTE *)(*p1)->pvData + cbSortOffset),
                  (void **)((BYTE *)(*p2)->pvData + cbSortOffset), cbSortLength));
}

void Stack_::Sort(DWORD cbOffset_pvData, DWORD cbLength_pvData, BYTE fbTypeIn)
{
    int     i;

    if (!(psBottom))
    {
        return;
    }

    //
    //  assign out offset & length for compare function
    //
    DELETE_OBJECT(ppsSorted);   // wipe the last one.

    if (!(ppsSorted = new StackStruct_ * [this->Count()]))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return;
    }

    //
    //  assign the contiguous array to be sorted.
    //
    ppsSorted[0] = psBottom;

    for (i = 1; i < (int)this->Count(); i++)
    {
        ppsSorted[i] = ppsSorted[i - 1]->psNext;
    }

    //
    //  let's do the sort!
    //

    EnterCriticalSection(pSortCriticalSection);

    cbSortOffset    = cbOffset_pvData;
    cbSortLength    = cbLength_pvData;
    fbType          = fbTypeIn;

    qsort( (void *)ppsSorted, (size_t)this->Count(), sizeof(StackStruct_ *), Stack_compare);

    LeaveCriticalSection(pSortCriticalSection);

    //
    //  now, we need to adjust out psNext's
    //

    for (i = 0; i < (int)(this->Count() - 1); i++)
    {
        ppsSorted[i]->psNext = ppsSorted[i + 1];
    }

    ppsSorted[i]->psNext = NULL;

    psBottom = ppsSorted[0];

    FlushGetStack();
}

BOOL Stack_::InitGetStackIfNecessary ()
{
    DWORD         cCount;
    StackStruct_* psGet;

    if ( ppsGet != NULL )
    {
        return( TRUE );
    }

    ppsGet = new StackStruct_ * [ dwStackCount ];

    if ( ppsGet == NULL )
    {
        return( FALSE );
    }

    psGet = psBottom;

    for ( cCount = 0; cCount < dwStackCount; cCount++ )
    {
        ppsGet[ cCount ] = psGet;
        psGet = psGet->psNext;
    }

    return( TRUE );
}

VOID Stack_::FlushGetStack ()
{
    delete ppsGet;
    ppsGet = NULL;
}