#include "stdafx.h"

#include "Hierarchy.h"

static unsigned long NumSubNames( char *pName )
{
	int numSub = 1;
	char *pC;
	
	for( pC = pName; *pC; pC++ )
	{
		if( *pC == '\\' )
			numSub++;
		if( *pC == ':' && pC[1] == ':' )
		{
			pC++;
			numSub++;
		}
	}
	return numSub;
}

static char *SubName( char *pName, int subNameNo )
{
	static char subName[256];
	char *pC;
	char *pCOut;
	
	for( pC = pName; *pC && subNameNo; pC++ )
	{
		if( *pC == '\\' )
			subNameNo--;
		if( *pC == ':' && pC[1] == ':' )
		{
			pC++;
			subNameNo--;
		}
	}

	for( pCOut = subName; *pC; pC++ )
	{
		if( *pC == '\\' )
			break;
		if( *pC == ':' && pC[1] == ':' )
			break;
		*pCOut++ = *pC;
	}

	*pCOut++ = '\0';
	return subName;
}

static char *PathName( char *pName, int depth )
{
	static char pathName[256];
	char *pC;
	char *pCOut = pathName;

	for( pC = pName; *pC; pC++ )
	{
		if( *pC == '\\' || ( *pC == ':' && pC[1] == ':' ) )
			if( !depth-- )
				break;
		*pCOut++ = *pC;
	}
	*pCOut = '\0';
	return pathName;
}


unsigned long HierNode::NumSubNames()
{
	return ::NumSubNames( m_pFullName );
}

char *HierNode::SubName( int subNameNo )
{
	return ::SubName( m_pFullName, subNameNo );
}

HierNode::HierNode( char *pName )
{
	m_pSibling = NULL;
	m_pChild = NULL;
	m_pData = NULL;
	m_Flags = HierPresent;
	m_pFullName  = (char *)malloc( strlen(pName) + 1 );
	strcpy( m_pFullName, pName );
	m_pBaseName = m_pFullName + strlen(m_pFullName) - 1;
	while( m_pBaseName > m_pFullName )
	{
		if( *(m_pBaseName-1) == '\\' )
			break;
		if( *(m_pBaseName-1) == ':' )
		{
			if( ( m_pBaseName > m_pFullName+1 ) && ( *(m_pBaseName-2) == ':' ) )
				break;
		}
		m_pBaseName--;
	}
}

HierNode::~HierNode()
{
	if( m_pData )
		free( m_pData );
	free( m_pFullName );
}

char *HierNode::ParentName()
{
	static char ParentName[256];
	unsigned long len = m_pBaseName - m_pFullName;
	if( len >= 255 )
	{
		len = 255;
	}
	memcpy( ParentName, m_pFullName, len );
	ParentName[len] = '\0';
	return ParentName;
}



Hierarchy::Hierarchy()
{
	m_nNodes = 0;
	m_nVisibleNodes = 0;
	m_pFirstNode = NULL;
	m_nVisibleNodeTableSize = 0;
	m_fVisibleValid = false;
	m_pVisibleNode = NULL;
}

Hierarchy::~Hierarchy()
{
	DeleteTree( m_pFirstNode );
}

void Hierarchy::DeleteTree( HierNode *pRoot )
{
	HierNode *pNode;
	while( pRoot )
	{
		if( pRoot->m_pChild )
			DeleteTree(pRoot->m_pChild);
		pNode = pRoot;
		pRoot = pRoot->m_pSibling;
		delete pNode;
		m_fVisibleValid = false;
		m_nNodes--;
	}	
}


HierNode *Hierarchy::Find( char *pName )
{
	int numLevels = NumSubNames(pName);
	int level = 0;
	HierNode *pNode = m_pFirstNode;
	char *pSubName = SubName(pName,0);

	while( pNode )
	{
		if( strcmp( pNode->BaseName(), pSubName ) == 0 )
		{
			if( numLevels == level )
				return pNode;
			if( !pNode->m_pChild )
				return NULL;
			level++;
			pSubName = SubName(pName,level);
			pNode = pNode->m_pChild;
		}
		else
			pNode = pNode->m_pSibling;
	}
	return NULL;
}



HierNode *Hierarchy::FindOrInsertUnder( HierNode **ppNext, char *pName )
{
	while( *ppNext )
	{
		int comp = strcmp( (*ppNext)->m_pFullName, pName );
		if( comp == 0 )
			return *ppNext;
		if( comp > 0 )
			break;
		ppNext = &((*ppNext)->m_pSibling);
	}
	// need to insert a new node
	HierNode *pNode = new HierNode( pName );
	m_fVisibleValid = false;
	m_nNodes++;
	pNode->m_pSibling = *ppNext;
	*ppNext = pNode;
	pNode->m_Flags = HierPresent;
	return pNode;
}

HierNode *Hierarchy::FindOrCreate( char *pName )
{
	int numLevels = NumSubNames(pName);

	HierNode **ppNode = &m_pFirstNode;
	HierNode *pNode;

	for( int level=0; level < numLevels; level++ )
	{
		pNode = FindOrInsertUnder( ppNode, PathName( pName, level ) );
		pNode->m_Flags |= HierPresent;
		ppNode = &(pNode->m_pChild);
	}

	return pNode;
}


HierNode *Hierarchy::VisibleNode( unsigned long LineNo )	// Returns a real or imaginary visible node
{
	if( !m_fVisibleValid )
		RebuildVisibleNodeTable();
	return ( LineNo < m_nVisibleNodes ) ? m_pVisibleNode[LineNo] : NULL;			
}

unsigned long	Hierarchy::NumVisibleNodes()
{
	if( !m_fVisibleValid )
		RebuildVisibleNodeTable();
	return m_nVisibleNodes;
}


void Hierarchy::AppendVisibleNode( HierNode *pNode )
{
	if( m_nVisibleNodes >= m_nVisibleNodeTableSize )
	{
		m_nVisibleNodeTableSize += 64;
		if( m_pVisibleNode )
			m_pVisibleNode = (HierNode **)realloc( m_pVisibleNode, m_nVisibleNodeTableSize * sizeof(HierNode *) );
		else
			m_pVisibleNode = (HierNode **)malloc( m_nVisibleNodeTableSize * sizeof(HierNode *) );
	}
	m_pVisibleNode[m_nVisibleNodes++] = pNode;
}

void Hierarchy::AccumulateVisible( HierNode *pNode )
{
	while( pNode )
	{
		AppendVisibleNode( pNode );
		if( pNode->m_pChild && ( pNode->m_Flags & HierOpen ) )
			AccumulateVisible( pNode->m_pChild );
		pNode = pNode->m_pSibling;
	}
}

void Hierarchy::RebuildVisibleNodeTable()
{
	m_nVisibleNodes = 0;
	if( m_pFirstNode )
		AccumulateVisible( m_pFirstNode );
	m_fVisibleValid = true;
}


void Hierarchy::OpenClose( unsigned long LineNo )		// Toggle open/closed state of a visible node by line no
{
	if( LineNo < m_nVisibleNodes )
		m_pVisibleNode[LineNo]->m_Flags ^= HierOpen;
	m_fVisibleValid = false;
}

void Hierarchy::DeleteNonPresentTree( HierNode **ppNext )
{
	while( *ppNext )
	{
		if( (*ppNext)->m_Flags & HierPresent )
		{
			if( (*ppNext)->m_pChild )
				DeleteNonPresentTree( &((*ppNext)->m_pChild) );
			ppNext = &((*ppNext)->m_pSibling);
		}
		else
		{
			HierNode *pOld = *ppNext;
			*ppNext = pOld->m_pSibling;
			pOld->m_pSibling = NULL;	// so DeleteTree doesn't delete siblings yet
			DeleteTree( pOld );
		}
	}
}

void Hierarchy::DeleteNonPresent()
{
	DeleteNonPresentTree( &m_pFirstNode );
}

void Hierarchy::MarkNonPresentTree( HierNode *pRoot )
{
	while( pRoot )
	{
		if( pRoot->m_pChild )
			MarkNonPresentTree( pRoot->m_pChild );
		pRoot->m_Flags &= ~HierPresent;
		pRoot = pRoot->m_pSibling;
	}
}

void Hierarchy::RebuildListStart()
{
	MarkNonPresentTree( m_pFirstNode );
}

/*

int main(int argc, char* argv[])
{

	Hierarchy h;
	unsigned long i;

	h.RebuildListStart();

	h.FindOrCreate("A");
	h.FindOrCreate("C");
	h.FindOrCreate("Z::Z::ZZTop::Z");
	h.FindOrCreate("A::B");

	// h.RebuildListStart();

	h.FindOrCreate("A::D");

	// h.DeleteNonPresent();

	printf("At Start");


	for( i=0; i<h.NumVisibleNodes(); i++ )
	{
		printf("%d:  '%s'\n", i, h.VisibleNode(i)->FullName() );
	}

	printf("Opening top...\n");

	h.OpenClose(0);

	for( i=0; i<h.NumVisibleNodes(); i++ )
	{
		printf("%d:  '%s'\n", i, h.VisibleNode(i)->FullName() );
	}

	printf("Opening all...\n");

	for( i=0; i<h.NumVisibleNodes(); i++ )
	{
		printf("%d:  '%s'\n", i, h.VisibleNode(i)->FullName() );
		if( !(h.VisibleNode(i)->IsOpen()) )
			h.OpenClose(i);
	}


	h.RebuildListStart();

	h.FindOrCreate("Z::Z::ZZTop");

	h.DeleteNonPresent();

	printf("Displaying all...\n");

	for( i=0; i<h.NumVisibleNodes(); i++ )
	{
		printf("%d:  '%s'\n", i, h.VisibleNode(i)->FullName() );
		if( !(h.VisibleNode(i)->IsOpen()) )
			h.OpenClose(i);
	}


	printf("Hello World!\n");
	return 0;
}
*/
