//+------------------------------------------------------------------------
//
//  File:       cary.cxx
//
//  Contents:   Generic dynamic array class
//
//  Classes:    CAry
//
//-------------------------------------------------------------------------

#include <headers.hxx>
#pragma hdrstop

//  CAry class


//+------------------------------------------------------------------------
//
//  Member:     CAry::CAry
//
//  Synopsis:   Constructor for the generic resizeable array class. Note that
//              the array does not initially have any storage; EnsureSize
//              must be called before any index is valid (even index 0!)
//
//  Arguments:  [cb]        Size of each element.
//
//-------------------------------------------------------------------------
CAry::CAry(size_t cb)
{
    _c = _cMac = 0;
    _cb = cb;
    _pv = NULL;

    Assert(_cb <= CARY_MAXELEMSIZE);
}



//+------------------------------------------------------------------------
//
//  Member:     CAry::~CAry
//
//  Synopsis:   Resizeable array destructor. Frees storage allocated for the
//              array.
//
//-------------------------------------------------------------------------
CAry::~CAry( )
{
    delete[] _pv;
}


//+------------------------------------------------------------------------
//
//  Member:     CAry::EnsureSize
//
//  Synopsis:   Ensures that the array is at least the given size. That is,
//              if EnsureSize(c) succeeds, then (c-1) is a valid index. Note
//              that the array maintains a separate count of the number of
//              elements logically in the array, which is obtained with the
//              Size/SetSize methods. The logical size of the array is never
//              larger than the allocated size of the array.
//
//  Arguments:  [c]         New allocated size for the array.
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------
HRESULT
CAry::EnsureSize(int c)
{
    void *  pv;

    if (c <= _cMac)
        return NOERROR;

    //  CONSIDER should we use a more sophisticated array-growing
    //    algorithm?
    c = ((c - 1) & -8) + 8;
    //pv = new (NullOnFail) BYTE[c * _cb];
    pv = new BYTE[c * _cb];
    if (!pv)
    {
        DOUT(TEXT("o2base/CAry::EnsureSize failed\r\n"));
        return E_OUTOFMEMORY;
    }

    if (_pv)
    {
        memcpy(pv, _pv, _c * _cb);
        delete[] _pv;
    }

    _cMac = c;
    _pv = pv;
    return NOERROR;
}



//+------------------------------------------------------------------------
//
//  Member:     CAry::Deref
//
//  Synopsis:   Returns a pointer to the i'th element of the array. This
//              method is normally called by type-safe methods in derived
//              classes.
//
//  Arguments:  [i]
//
//  Returns:    void *
//
//-------------------------------------------------------------------------
void *
CAry::Deref(int i)
{
    //  BUGBUG should be inline in non-DEBUG builds
    Assert(i >= 0);
    Assert(i < _cMac);

    return ((BYTE *) _pv) + i * _cb;
};



//+------------------------------------------------------------------------
//
//  Member:     CAry::Append
//
//  Synopsis:   Appends the given pointer to the end of the array, incrementing
//              the array's logical size, and growing its allocated size if
//              necessary. This method should only be called for arrays of
//              pointers; AppendIndirect should be used for arrays of
//              non-pointers.
//
//  Arguments:  [pv]        Pointer to append.
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------
HRESULT
CAry::Append(void * pv)
{
    HRESULT     hr;

    Assert(_cb == 4);

    hr = EnsureSize(_c + 1);
    if (hr)
        return hr;

    * (void **) Deref(_c) = pv;
    _c++;

    return NOERROR;
}



//+------------------------------------------------------------------------
//
//  Member:     CAry::AppendIndirect
//
//  Synopsis:   Appends the given element to the end of the array, incrementing
//              the array's logical size, and growing the array's allocated
//              size if necessary.  Note that the element is passed with a
//              pointer, rather than directly.
//
//  Arguments:  [pv]        --  Pointer to the element to be appended
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------
HRESULT
CAry::AppendIndirect(void * pv)
{
    HRESULT     hr;

    hr = EnsureSize(_c + 1);
    if (hr)
        return hr;

    memcpy(Deref(_c), pv, _cb);
    _c++;

    return NOERROR;
}



//+------------------------------------------------------------------------
//
//  Member:     CAry::Delete
//
//  Synopsis:   Removes the i'th element of the array, shuffling all elements
//              that follow one slot towards the beginning of the array.
//
//  Arguments:  [i]         Element to delete
//
//-------------------------------------------------------------------------
void
CAry::Delete(int i)
{
    Assert(i >= 0);
    Assert(i < _c);

    memmove(
        ((BYTE *) _pv) + (i * _cb),
        ((BYTE *) _pv) + ((i + 1) * _cb),
        (_c - i - 1) * _cb);
    _c--;
}


//+------------------------------------------------------------------------
//
//  Member:     CAry::DeleteAll
//
//  Synopsis:   Efficient method for emptying array of any contents
//
//-------------------------------------------------------------------------
void
CAry::DeleteAll(void)
{
    delete[] _pv;
    _pv = NULL;
    _c = 0;
    _cMac = 0;
}

//+------------------------------------------------------------------------
//
//  Member:     CAry::Insert
//
//  Synopsis:   Inserts a pointer pv at index i. The element previously at
//              index i, and all elements that follow it, are shuffled one
//              slot away towards the end of the array.
//              This method should only be called for arrays of
//              pointers; InsertIndirect should be used for arrays of
//              non-pointers.

//
//  Arguments:  [i]         Index to insert...
//              [pv]        ...this pointer at
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------
HRESULT
CAry::Insert(int i, void * pv)
{
    HRESULT     hr;

    hr = EnsureSize(_c + 1);
    if (hr)
        return hr;

    memmove(
        ((BYTE *) _pv) + ((i + 1) * _cb),
        ((BYTE *) _pv) + (i * _cb),
        (_c - i ) * _cb);

    ((void **) _pv)[i] = pv;
    _c++;
    return NOERROR;
}

//+------------------------------------------------------------------------
//
//  Member:     CAry::InsertIndirect
//
//  Synopsis:   Inserts a pointer pv at index i. The element previously at
//              index i, and all elements that follow it, are shuffled one
//              slot away towards the end of the array.Note that the 
//              clement is passed with a pointer, rather than directly.
//
//  Arguments:  [i]         Index to insert...
//              [pv]        ...this pointer at
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------
HRESULT
CAry::InsertIndirect(int i, void *pv)
{
    HRESULT     hr;

    hr = EnsureSize(_c + 1);
    if (hr)
        return hr;

    memmove(
        ((BYTE *) _pv) + ((i + 1) * _cb),
        ((BYTE *) _pv) + (i * _cb),
        (_c - i ) * _cb);

    memcpy(Deref(i), pv, _cb);
    _c++;
    return NOERROR;

}

//+------------------------------------------------------------------------
//
//  Member:     CAry::BringToFront
//
//  Synopsis:   Moves the i'th element to the front of the array, shuffling
//              intervening elements to make room.
//
//  Arguments:  [i]
//
//-------------------------------------------------------------------------
void
CAry::BringToFront(int i)
{
    BYTE    rgb[CARY_MAXELEMSIZE];

    memcpy(rgb, ((BYTE *) _pv) + (i * _cb), _cb);
    memmove(((BYTE *) _pv) + _cb, _pv, i * _cb);
    memcpy(_pv, rgb, _cb);
}



//+------------------------------------------------------------------------
//
//  Member:     CAry::SendToBack
//
//  Synopsis:   Moves the i'th element to the back of the array (that is,
//              the largest index less than the logical size.) Any intervening
//              elements are shuffled out of the way.
//
//  Arguments:  [i]
//
//-------------------------------------------------------------------------
void
CAry::SendToBack(int i)
{
    BYTE    rgb[CARY_MAXELEMSIZE];

    memcpy(rgb, ((BYTE *) _pv) + (i * _cb), _cb);
    memmove(
        ((BYTE *) _pv) + (i * _cb),
        ((BYTE *) _pv) + ((i + 1) * _cb),
        (_c - i - 1) * _cb);
    memcpy(((BYTE *) _pv) + ((_c - 1) * _cb), rgb, _cb);
}



//+------------------------------------------------------------------------
//
//  Member:     CAry::Find
//
//  Synopsis:   Returns the index at which the given pointer is found, or -1
//              if it is not found. The pointer values are compared directly;
//              there is no compare function.
//
//  Arguments:  [pv]        Pointer to find
//
//  Returns:    int; index of pointer, or -1 if not found
//
//-------------------------------------------------------------------------
int
CAry::Find(void * pv)
{
    int     i;
    void ** ppv;

    Assert(_cb == 4);

    for (i = 0, ppv = (void **) _pv;
         i < _c;
         i++, ppv++)
    {
        if (pv == *ppv)
            return i;
    }
    return -1;
}




//+------------------------------------------------------------------------
//
//  Member:     CAry::EnumElements
//
//  Synopsis:   Creates and returns an enumerator for the elements of the
//              array. Since the type of the elements (and therefore the type
//              of the enumerator) is unknown by this class, the enumerator
//              type is passed in.
//
//  Arguments:  [iid]       Type of the enumerator
//              [ppv]       Enumerator is returned in *ppv
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------
HRESULT
CAry::EnumElements(REFIID iid, LPVOID * ppv, BOOL fAddRef)
{
    CEnumGeneric *  pEnum;

    pEnum = CEnumGeneric::Create(iid, _cb, _c, _pv, fAddRef);
    if (!pEnum)
    {
        DOUT(TEXT("o2base/CAry::EnumElements failed\r\n"));
        return E_OUTOFMEMORY;
    }

    *ppv = pEnum;
    return NOERROR;
}