/*
    File:       CList.c

    Contains:   Implementation of the CList class

*/

#include "CList.h"
#include "CListIterator.h"
#include "CItemComparer.h"
#include "IrDALog.h"

#if (hasTracing > 0 && hasCListTracing > 0)

enum TraceCodes {
    kLogNew = 1,
    kLogFree,
    kLogInit
};

static
EventTraceCauseDesc TraceEvents[] = {
    {kLogNew,           "CList: new, obj="},
    {kLogFree,          "CList: free, obj="},
    {kLogInit,          "CList: init, obj="}
};
#define XTRACE(x, y, z) IrDALogAdd( x, y, z, TraceEvents, true )
#else
#define XTRACE(x, y, z) ((void) 0)
#endif


//--------------------------------------------------------------------------------
#define super CDynamicArray
    OSDefineMetaClassAndStructors(CList, CDynamicArray);
//--------------------------------------------------------------------------------


CList *
CList::cList(long chunkSize)
{
    CList *obj = new CList;
    
    XTRACE(kLogNew, (int)obj >> 16, (short)obj);
    
    if (obj && !obj->init(chunkSize)) {
	obj->release();
	obj = nil;
    }
    return obj;
}

Boolean CList::init(ArrayIndex size)
{
    XTRACE(kLogInit, (int)this >> 16, (short)this);
    
    return super::init(kDefaultElementSize, size);
}

void CList::free(void)
{
    XTRACE(kLogFree, (int)this >> 16, (short)this);
    
    super::free();
}

//----------------------------------------------------------------------------
//      CList::At
//----------------------------------------------------------------------------
void* CList::At(ArrayIndex index)
{
    ULong* itemPtr = (ULong*) SafeElementPtrAt(index);
    return (itemPtr == nil) ? nil : (void*) (*itemPtr);

} // CList::At


//----------------------------------------------------------------------------
//      CList::InsertAt
//----------------------------------------------------------------------------
IrDAErr CList::InsertAt(ArrayIndex index, void* item)
{
    ULong data = (ULong) item;

    return InsertElementsBefore(index, &data, 1);

} // CList::InsertBefore


//----------------------------------------------------------------------------
//      CList::Remove
//----------------------------------------------------------------------------
IrDAErr CList::Remove(void* item)
{
    IrDAErr result;
    ArrayIndex index = GetIdentityIndex(item);

    if (index != kEmptyIndex)
	result = RemoveAt(index);
    else
	{
	result = errRangeCheck;
	//XPRINT(("CList::Remove: can't find item in list\n"));
	}

    return result;

} // CList::Remove


//----------------------------------------------------------------------------
//      CList::InsertUnique
//----------------------------------------------------------------------------
Boolean CList::InsertUnique(void* add)
    {
    Boolean result = !Contains(add);
    if (result)
	{
	IrDAErr ignored = InsertLast(add);
	XASSERTNOT(ignored);
	}
    return result;
    }


//----------------------------------------------------------------------------
//      CList::Replace
//----------------------------------------------------------------------------
IrDAErr CList::Replace(void* oldItem, void* newItem)
{
    IrDAErr result;
    ArrayIndex index = GetIdentityIndex(oldItem);

    if (index != kEmptyIndex)
	result = ReplaceAt(index, newItem);
    else
	{
	result = errRangeCheck;
	//XPRINT(("CList::Replace: can't find oldItem in list\n"));
	}

    return result;

} // CList::Replace


//----------------------------------------------------------------------------
//      CList::ReplaceAt
//----------------------------------------------------------------------------
IrDAErr CList::ReplaceAt(ArrayIndex index, void* newItem)
{
    ULong data = (ULong) newItem;

    return ReplaceElementsAt(index, &data, 1);
}

//----------------------------------------------------------------------------
//      CList::GetIdentityIndex
//----------------------------------------------------------------------------
ArrayIndex CList::GetIdentityIndex(void* item)
{
    ArrayIndex index = -1;
    CItemComparer *test =  CItemComparer::cItemComparer(item, nil);     // default comparer tests for identity

    require(test, Fail);
    
    (void) Search(test, index);

    test->release();
    
Fail:
    return index;

} // CList::GetIdentityIndex


//----------------------------------------------------------------------------
//      CList::Search
//----------------------------------------------------------------------------
void* CList::Search(CItemComparer* test, ArrayIndex& index)
/*
    Performs a linear search on the list.
    Returns the index and value of the item for which
    CItemTester::TestItem returns kItemEqualCriteria,
    or kEmptyIndex and nil respectively.
*/
{
    CListIterator *iter = CListIterator::cListIterator(this);
    index = kEmptyIndex;
    void* result = nil;
    void* item = nil;

    require(iter, Fail);
    
    for (item = iter->FirstItem(); iter->More(); item = iter->NextItem())
	if (test->TestItem(item) == kItemEqualCriteria)
	    {
	    result = item;
	    index = iter->fCurrentIndex;
	    break;
	    }

    iter->release();
    
Fail:
    return result;

} // CList::Search

