#include "priv.h"

//***   FDSA -- small/fast DSA routines
// DESCRIPTION
//  We attempt to behave as much like an array as possible (semantics
//  and performance and to some extent allocation).  In particular,
//  - indexing (FDSA_GetItemXxx) is done entirely inline; and...
//  - ... involves only 1 extra indirection vs. a true array; and...
//  - ... knows the data type so sizeof is a constant; and...
//  - ... does no range checks (except possibly in DEBUG versions).
//  - part of the FDSA is statically alloc'ed, so one can put critical
//  items in the static part to ensure that they'll be there even under
//  low memory conditions.
// NOTES
//  For now we only implement:
//      Initialize, Destroy, GetItemPtr, GetItemCount, InsertItem, AppendItem

// FEATURE eventually these come from comctl...
#define DABreak()   /*NOTHING*/
// ReAlloc and Free come from inc/heapaloc.h

//***   SDSA_PITEM -- get item the hard way
//
#define SDSA_PITEM(pfdsa, i) \
    ((void *)(((BYTE *)(pfdsa)->aItem) + ((i) * (pfdsa)->cbItem)))

//***   FDSA_Initialize -- initialize
//
BOOL WINAPI FDSA_Initialize(int cbItem, int cItemGrow,
    PFDSA pfdsa, void * aItemStatic, int cItemStatic)
{
    ASSERT(pfdsa != NULL);      // WARNING how handle?

    if (cItemGrow == 0)
        cItemGrow = 1;

    // for implementation simplicity, cItemStatic must be a multiple of
    // cItemGrow.  o.w. our 1st grow from static->dynamic is messy and
    // probably buggy.
    if (cItemStatic % cItemGrow != 0) {
        AssertMsg(0, TEXT("CItemStatic must be a multiple of cItemGrow"));
        return FALSE;
    }

    if (aItemStatic != NULL) {
        // since we're (eventually) in comctl, we can't assume caller's
        // buffer was 0'ed
        ZeroMemory(aItemStatic, cItemStatic * cbItem);
    }

    if (pfdsa) {
        pfdsa->cItem = 0;
        pfdsa->cItemAlloc = cItemStatic;
        pfdsa->aItem = aItemStatic;
        pfdsa->fAllocated = FALSE;

        pfdsa->cbItem = cbItem;
        ASSERT(pfdsa->cbItem == cbItem);        // bitfield overflow

        pfdsa->cItemGrow = cItemGrow;
        ASSERT(pfdsa->cItemGrow == cItemGrow);  // bitfield overflow
    }

    return TRUE;
}

BOOL WINAPI FDSA_Destroy(PFDSA pdsa)
{
    if (pdsa == NULL)       // allow NULL for low memory cases
        return TRUE;

    if (pdsa->fAllocated && pdsa->aItem && !LocalFree(pdsa->aItem))
        return FALSE;

    return TRUE;
}

void* _LocalReAlloc(void* p, UINT uBytes)
{
    if (uBytes) {
        if (p) {
            return LocalReAlloc(p, uBytes, LMEM_MOVEABLE | LMEM_ZEROINIT);
        } else {
            return LocalAlloc(LPTR, uBytes);
        }
    } else {
        if (p)
            LocalFree(p);
        return NULL;
    }
}

//***   FDSA_InsertItem -- insert an item
// ENTRY/EXIT
//  index   insertion point; index > count (e.g. DA_LAST) means append
// NOTES
//  param called 'pdsa' (vs. pfdsa) for easy diff'ing w/ DSA_InsertItem
int WINAPI FDSA_InsertItem(PFDSA pdsa, int index, void FAR* pitem)
{
    ASSERT(pitem);

    if (index < 0) {
        TraceMsg(DM_ERROR, "FDSA: InsertItem: Invalid index: %d", index);
        DABreak();
        return -1;
    }

    if (index > pdsa->cItem)
        index = pdsa->cItem;

    if (pdsa->cItem + 1 > pdsa->cItemAlloc) {
        void FAR* aItemNew = _LocalReAlloc(pdsa->fAllocated ? pdsa->aItem : NULL,
                (pdsa->cItemAlloc + pdsa->cItemGrow) * pdsa->cbItem);
        if (!aItemNew)
            return -1;

        if (!pdsa->fAllocated) {
            // when we go from static->dynamic, we need to copy
            pdsa->fAllocated = TRUE;
            hmemcpy(aItemNew, pdsa->aItem, pdsa->cItem * pdsa->cbItem);
        }

        pdsa->aItem = aItemNew;
        pdsa->cItemAlloc += pdsa->cItemGrow;
    }

    if (index < pdsa->cItem) {
        hmemcpy(SDSA_PITEM(pdsa, index + 1), SDSA_PITEM(pdsa, index),
            (pdsa->cItem - index) * pdsa->cbItem);
    }
    pdsa->cItem++;
    hmemcpy(SDSA_PITEM(pdsa, index), pitem, pdsa->cbItem);

    return index;
}


BOOL WINAPI FDSA_DeleteItem(PFDSA pdsa, int index)
{
    ASSERT(pdsa);

    if (index < 0 || index >= pdsa->cItem)
    {
        TraceMsg(TF_ERROR, "FDSA: DeleteItem: Invalid index: %d", index);
        DABreak();
        return FALSE;
    }

    if (index < pdsa->cItem - 1)
    {
        hmemcpy(SDSA_PITEM(pdsa, index), SDSA_PITEM(pdsa, index + 1),
            (pdsa->cItem - (index + 1)) * pdsa->cbItem);
    }
    pdsa->cItem--;

#if 0
    // this doesn't work yet for FDsA
    if (pdsa->cItemAlloc - pdsa->cItem > pdsa->cItemGrow)
    {
        void FAR* aItemNew = ReAlloc(pdsa->aItem,
                (pdsa->cItemAlloc - pdsa->cItemGrow) * pdsa->cbItem);

        ASSERT(aItemNew);
        pdsa->aItem = aItemNew;
        pdsa->cItemAlloc -= pdsa->cItemGrow;
    }
#endif
    return TRUE;
}