
#include "actconfig.h"
#include "actcomdef.h"
#include "actstdlib.h"
#include "acttestfixturelist.h"
#include "acttestfixturelist_private.h"

#ifdef _WIN32
    #ifdef _DEBUG
    #define LIST_DEBUG
    #define MSG_PRINT(x) act_print(x)
    #else
    #define MSG_PRINT(x) 
    #endif
#else 
    #define MSG_PRINT(x) 
#endif 

static ITestFixtureListVtbl g_ITestFixtureListVtbl =
{
    List_GetHead,
    List_GetTail,
    List_RemoveHead,
    List_RemoveTail,
    List_AddHead,
    List_AddTail,
    List_RemoveAll,
    List_GetHeadPosition,
    List_GetTailPosition,
    List_GetNext,
    List_GetPrev,
    List_GetAt,
    List_SetAt,
    List_RemoveAt,
    List_InsertBefore,
    List_InsertAfter,
    List_Find,
    List_FindIndex,
    List_GetNo,
    List_GetCount,
    List_IsEmpty,
};

/// Construction/Destruction


    /** Constructs an empty ordered list. */


//====================================================================
//
// Construction/Destruction
//
//====================================================================


/** Constructs an empty ordered list. */
ITestFixtureList* 	ITestFixtureList_New (void)
{
	ITestFixtureList* pList;

	pList = (ITestFixtureList*) act_malloc(sizeof(ITestFixtureList));
	if(pList == NULL)
	{
		MSG_PRINT("Fail no memory");
		return NULL;
	}
	
	act_memset(pList,0,sizeof(ITestFixtureList));

    pList->pvt = &g_ITestFixtureListVtbl;

	return pList;
    
}


/** Free a List, but don't free values if bDataDelete is false*/
void 	ITestFixtureList_Delete (ITestFixtureList * pMe, boolean bDataDelete)
{
	if ( pMe != NULL )
	{
		List_RemoveAll(pMe, bDataDelete);
		act_free(pMe);
		pMe = NULL;
	}
}


//====================================================================
//
//  Head/Tail Access
//
//====================================================================

/// Returns the head element of the list (cannot be empty).
static const ITestFixture* List_GetHead(ITestFixtureList *pMe)
{
	if ( pMe->lpNodeHead == NULL )
	{
		return NULL;
	}
	return pMe->lpNodeHead->pData;
}

/// Returns the tail element of the list (cannot be empty).
static const ITestFixture* List_GetTail(ITestFixtureList *pMe)
{
	if ( pMe->lpNodeTail == NULL )
	{
		return NULL;
	}
	return pMe->lpNodeTail->pData;	
}

/// Operations

//====================================================================
//
// Operations
//
//====================================================================

/// Removes the element from the head of the list.
// Return Value: The Data pointer previously at the head of the list.
static ITestFixture* List_RemoveHead(ITestFixtureList *pMe, boolean bDataDelete) 
{
	ITestFixtureNode*		tmpNode;
	ITestFixture*	tmpData;
	if ( pMe->lpNodeHead == NULL )
		return NULL;

	tmpNode = pMe->lpNodeHead;
	tmpData = pMe->lpNodeHead->pData;

	pMe->lpNodeHead = tmpNode->pNext;
	if ( pMe->lpNodeHead != NULL )
	{
		pMe->lpNodeHead->pPrev = NULL;
	}
	else
	{
		pMe->lpNodeTail = NULL;
	}

	if ( bDataDelete ) 
	{
		if ( tmpData != NULL )
		{
			//act_free(tmpData);
            ITestFixture_Delete(tmpData);
			tmpData = NULL;
		}
	}
	act_free(tmpNode);

	pMe->nNodeCount--;
#ifdef LIST_DEBUG
	if ( pMe->nNodeCount < 0 )
	{
		MSG_PRINT("List_RemoveHead(pMe, BOOL bDataDelete) - pMe->nNodeCount is minus.");
	}
#endif
	return tmpData;
}


/// Removes the element from the tail of the list. 
static ITestFixture* List_RemoveTail(ITestFixtureList *pMe, boolean bDataDelete)
{
	ITestFixtureNode*		tmpNode;
	ITestFixture*	            tmpData;
	if ( pMe->lpNodeTail == NULL )
		return NULL;

	tmpNode = pMe->lpNodeTail;
	tmpData = pMe->lpNodeTail->pData;

	pMe->lpNodeTail = tmpNode->pPrev;
	if ( pMe->lpNodeTail != NULL )
	{
		pMe->lpNodeTail->pNext = NULL;
	}
	else
	{
		pMe->lpNodeHead = NULL;
	}

	if ( bDataDelete ) 
	{
		if ( tmpData != NULL )
		{
			//act_free(tmpData);
            ITestFixture_Delete(tmpData);
			tmpData = NULL;
		}
	}
	act_free(tmpNode);

	pMe->nNodeCount--;

#ifdef LIST_DEBUG
	if ( pMe->nNodeCount < 0 )
	{
		MSG_PRINT("List_RemoveTail(pMe, BOOL bDataDelete) - pMe->nNodeCount is minus.");
	}
#endif

	return tmpData;
}

/// Adds an element (or all the elements in another list) to the head of the list (makes a new head).
static ITestFixtureNode* List_AddHead(ITestFixtureList *pMe, ITestFixture* pData)
{
	ITestFixtureNode* tmpNode = (ITestFixtureNode*)act_malloc(sizeof(ITestFixtureNode));
    tmpNode->pData = pData;
	tmpNode->pPrev = NULL;
	tmpNode->pNext = pMe->lpNodeHead;

    if ( pMe->lpNodeHead != NULL )
	    pMe->lpNodeHead->pPrev = tmpNode;
    else
        pMe->lpNodeTail = tmpNode;

	pMe->lpNodeHead = tmpNode;

	pMe->nNodeCount++;
#ifdef LIST_DEBUG
	if ( pMe->nNodeCount < 0 )
	{
		MSG_PRINT("List_AddHead(pMe, ListItemPtr pData) - pMe->nNodeCount is minus.");
	}
#endif
	return pMe->lpNodeHead;
}

/// Adds an element (or all the elements in another list) to the tail of the list (makes a new tail).
static ITestFixtureNode* List_AddTail(ITestFixtureList *pMe, ITestFixture* pData)
{
	ITestFixtureNode* tmpNode = (ITestFixtureNode*)act_malloc(sizeof(ITestFixtureNode));
    tmpNode->pData = pData;
	tmpNode->pNext = NULL;
	tmpNode->pPrev = pMe->lpNodeTail;

    if ( pMe->lpNodeTail != NULL )
        pMe->lpNodeTail->pNext = tmpNode;
    else
        pMe->lpNodeHead = tmpNode;
    
	pMe->lpNodeTail = tmpNode;

	pMe->nNodeCount++;
#ifdef LIST_DEBUG
	if ( pMe->nNodeCount < 0 )
	{
		MSG_PRINT("List_AddTail(pMe, ListItemPtr pData) - pMe->nNodeCount is minus.");
	}
#endif	
 	return pMe->lpNodeTail;
}


/// Removes all the elements from this list.
// bDataDelete = FALSE 이면 리스트내의 데이터들은 반드시 사용자가 delete시켜줘야 한다.
static void List_RemoveAll(ITestFixtureList *pMe, boolean bDataDelete)
{
	ITestFixtureNode* Node = pMe->lpNodeHead;
	ITestFixtureNode* NextNode;

	while ( Node != NULL )
	{
		NextNode = Node->pNext;
		if ( Node != NULL )
		{
			if ( Node->pData != NULL && bDataDelete )
			{
				//act_free(Node->pData);
                ITestFixture_Delete(Node->pData);
				Node->pData = NULL;
			}
		}
		act_free(Node);
		Node = NextNode;
	}
	pMe->lpNodeHead = pMe->lpNodeTail = NULL;
	pMe->nNodeCount = 0;
}

/// Iteration

//====================================================================
//
// Iteration
//====================================================================

/// Returns the position of the head element of the list.
static const ITestFixtureNode* List_GetHeadPosition(ITestFixtureList *pMe)
{
	return pMe->lpNodeHead;
}

/// Returns the position of the tail element of the list.
static const ITestFixtureNode* List_GetTailPosition(ITestFixtureList *pMe)
{
	return pMe->lpNodeTail;
}

/// Gets the next element for iterating.
static const ITestFixture* List_GetNext(ITestFixtureList *pMe, ITestFixtureNode** ppPosition)
{
    ITestFixture* pData;
    ITestFixtureNode* pNext;
    
    if ( pMe->lpNodeHead == NULL )
    {
        *ppPosition = NULL;
        pData = NULL;
    }
    
    if(ppPosition != NULL)
    {
        if( (*ppPosition)->pNext != NULL)
        {
            pData = (*ppPosition)->pData;
            pNext = (*ppPosition)->pNext;                
        }
        else
        {
            pData = (*ppPosition)->pData;
            pNext = NULL;
        }
    }
    else
    {
        pData = NULL;
        pNext = NULL;            
    }
    
    if(ppPosition != NULL)
    {
        *ppPosition = pNext;
    }
    
    return pData;
}

/// Gets the previous element for iterating.
static const ITestFixture* List_GetPrev(ITestFixtureList *pMe, ITestFixtureNode** ppPosition)
{
    ITestFixture* pData;
    ITestFixtureNode* pPrev;
    
    if ( pMe->lpNodeHead == NULL )
    {
        *ppPosition = NULL;
        pData = NULL;
    }
    
    if(ppPosition != NULL)
    {
        if( (*ppPosition)->pPrev != NULL)
        {
            pData = (*ppPosition)->pData;
            pPrev = (*ppPosition)->pPrev;                
        }
        else
        {
            pData = (*ppPosition)->pData;
            pPrev = NULL;
        }
    }
    else
    {
        pData = NULL;
        pPrev = NULL;            
    }
    
    if(ppPosition != NULL)
    {
        *ppPosition = pPrev;
    }
    
    return pData;
}

/// Retrieval/Modification

//====================================================================
//
// Retrieval/Modification
//
//====================================================================

/// Gets the element at a given position.
static const ITestFixture* List_GetAt(ITestFixtureList *pMe, ITestFixtureNode* position)
{
	if ( position == NULL ) return NULL;
	return position->pData;
}


/// Sets the element at a given position.
static void List_SetAt(ITestFixtureList *pMe, ITestFixtureNode* pos, ITestFixture* newElement, boolean bDeleteData)
{
#ifdef LIST_DEBUG
	if ( pos == NULL )
	{
		MSG_PRINT("List_SetAt(LPJNODE pos, JDataPtr newElement) - position is NULL.");
		return;
	}
#endif

	if ( bDeleteData )
	{
		act_free(pos->pData);
		pos->pData = NULL;
	}
	pos->pData = newElement;
}

/// Removes an element from this list, specified by position.
static void List_RemoveAt(ITestFixtureList *pMe, ITestFixtureNode* position, boolean bDeleteData)
{
	ITestFixtureNode* tmpNode = (ITestFixtureNode*) position;

#ifdef LIST_DEBUG
	if ( position == NULL )
	{
		MSG_PRINT("List_RemoveAt(LPJNODE position) - position is NULL.");
		return;
	}
#endif


	if ( tmpNode == pMe->lpNodeHead )
	{
        List_RemoveHead(pMe, bDeleteData);
        return;
	}
	if ( tmpNode == pMe->lpNodeTail )
	{
        List_RemoveTail(pMe, bDeleteData);
        return;
	}

	tmpNode->pPrev->pNext = tmpNode->pNext;
	tmpNode->pNext->pPrev = tmpNode->pPrev;

	if ( bDeleteData )
	{
		if ( tmpNode->pData != NULL )
		{
			//act_free(tmpNode->pData);
            ITestFixture_Delete(tmpNode->pData);
			tmpNode->pData = NULL;
		}
	}
	act_free(tmpNode);
	pMe->nNodeCount--;

#ifdef LIST_DEBUG
	if ( pMe->nNodeCount < 0 )
	{
		MSG_PRINT( "List_RemoveAt(LPJNODE position) - pMe->nNodeCount is minus.");
	}
#endif
}
//====================================================================
//
// Insertion
//
//====================================================================

/// Inserts a new element before a given position.
static ITestFixtureNode* List_InsertBefore(ITestFixtureList *pMe, ITestFixtureNode* pos, ITestFixture* newElement)
{
	ITestFixtureNode* tmpNode;
	if ( pos == NULL ) return List_AddHead(pMe, newElement);
	tmpNode = (ITestFixtureNode*)act_malloc(sizeof(ITestFixtureNode*));

	tmpNode->pData = newElement;
	tmpNode->pNext = pos;
	tmpNode->pPrev = pos->pPrev;

	if ( pos->pPrev == NULL )
	{
#ifdef LIST_DEBUG
		if ( pos != pMe->lpNodeHead )
		{
			MSG_PRINT("List::InsertBefore(LPJNODE pos, JDataPtr newElement) - pos != pMe->lpNodeHead.");
		}
#endif
		pMe->lpNodeHead = tmpNode;
	}
	else
	{
		pos->pPrev->pNext = tmpNode;
	}

	pos->pPrev = tmpNode;

	pMe->nNodeCount++;
#ifdef LIST_DEBUG
	if ( pMe->nNodeCount < 0 )
	{
		MSG_PRINT("List::InsertBefore(LPJNODE pos, JDataPtr newElement) - pMe->nNodeCount is minus.");
	}
#endif
	return tmpNode;
}

/// Inserts a new element after a given position.
static ITestFixtureNode* List_InsertAfter(ITestFixtureList *pMe, ITestFixtureNode* pos, ITestFixture*newElement)
{
	ITestFixtureNode* tmpNode;
	if ( pos == NULL ) return List_AddTail(pMe, newElement);
	tmpNode = (ITestFixtureNode*)act_malloc(sizeof(ITestFixtureNode));

	tmpNode->pData = newElement;
	tmpNode->pPrev = pos;
	tmpNode->pNext = pos->pNext;	

	if ( pos->pNext == NULL )
	{
#ifdef LIST_DEBUG
		if ( pos != pMe->lpNodeTail )
		{
			MSG_PRINT("List::InsertAfter(LPJNODE pos, JDataPtr newElement) - pos != pMe->lpNodeTail.");
		}
#endif
		pMe->lpNodeTail = tmpNode;
	}
	else
	{
		pos->pNext->pPrev = tmpNode;
	}

	pos->pNext = tmpNode;

	pMe->nNodeCount++;
#ifdef LIST_DEBUG
	if ( pMe->nNodeCount < 0 )
	{
		MSG_PRINT("List::InsertAfter(LPJNODE pos, JDataPtr newElement) - pMe->nNodeCount is minus.");
	}
#endif
	return tmpNode;
}

/// Searching

//====================================================================
//
// Searching
//
//====================================================================

/// Gets the position of an element specified by pointer value.
static const ITestFixtureNode* List_Find(ITestFixtureList *pMe, ITestFixture* searchValue, ITestFixtureNode* startAfter)
{
	ITestFixtureNode* tmpNode;

       if( startAfter == NULL ) 
       {
            tmpNode =  (ITestFixtureNode*)pMe->lpNodeHead ;
        }
       else 
        {
           tmpNode =   (ITestFixtureNode*)startAfter;
       }

	for ( ; tmpNode != NULL; tmpNode = tmpNode->pNext )
	{
		if ( tmpNode->pData == searchValue ) return (ITestFixtureNode*)tmpNode;
	}
	return NULL;
}

/// Gets the position of an element specified by a zero-based index.
static const ITestFixtureNode* List_FindIndex(ITestFixtureList *pMe, int nIndex)
{
	ITestFixtureNode* tmpNode;
	if ( nIndex >= pMe->nNodeCount || nIndex < 0 ) return NULL;

	tmpNode = pMe->lpNodeHead;
	while( nIndex-- )
	{
		tmpNode = tmpNode->pNext;
	}

	return (ITestFixtureNode*)tmpNode;
}


/// Gets the number of an element specified by pointer value.
static const int List_GetNo(ITestFixtureList *pMe, ITestFixture* searchValue, ITestFixtureNode* startAfter)
{
    	int nNo = 0;
	ITestFixtureNode* tmpNode;
       if( startAfter == NULL ) 
       {
            tmpNode =  (ITestFixtureNode*)pMe->lpNodeHead ;
        }
       else 
        {
           tmpNode =   (ITestFixtureNode*)startAfter;
       }

	for ( ; tmpNode != NULL; tmpNode = tmpNode->pNext )
	{
        nNo++;
		if ( tmpNode->pData == searchValue ) return nNo;
	}
	return 0;
}


//====================================================================
//
// Status
//
//====================================================================

/// Returns the number of elements in this list.
static int List_GetCount(ITestFixtureList *pMe)
{
	return pMe->nNodeCount;
}


/// Tests for the empty list condition (no elements).
/// Nonzero if this list is empty; otherwise 0.
static boolean List_IsEmpty(ITestFixtureList *pMe)
{
	return (pMe->nNodeCount == 0);
}
