/////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "CStrTable.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

char *CStr::GetString(void)
{
	return szString;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStr::SetString(char *szNewString)
{
	if(szString)
	{
		free(szString);
	}

	szString = new char[strlen(szNewString) + 1];

	if(!szString)
	{
		return false;
	}

	strcpy(szString, szNewString);

	return true;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStr::CompareString(char *szComapreString)
{
	if(!szString)
	{
		return false;
	}

	return !strcmp(szString, szComapreString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

char *CStrEntry::GetEntryColumn(int iEntryColumn)
{
	if(iEntryColumn < 0 || iEntryColumn >= iNumEntryColumns)
	{
		return false;
	}

	if(!EntryColumns[iEntryColumn])
	{
		return false;
	}

	return EntryColumns[iEntryColumn]->GetString();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStrEntry::SetEntryColumn(int iEntryColumn, char *szNewString)
{
	if(!szNewString)
	{
		return false;
	}

	if(!strlen(szNewString))
	{
		return false;
	}

	if(iEntryColumn < 0 || iEntryColumn >= iNumEntryColumns)
	{
		if(iEntryColumn == iNumEntryColumns)
		{
			if(AddEntryColumn(szNewString) != -1)
			{
				return true;
			}
			return false;
		}
		return false;
	}

	if(EntryColumns[iEntryColumn])
	{
		delete EntryColumns[iEntryColumn];
	}

	EntryColumns[iEntryColumn] = new CStr;

	if(!EntryColumns[iEntryColumn])
	{
		return false;
	}

	return EntryColumns[iEntryColumn]->SetString(szNewString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrEntry::AddEntryColumn(char *szNewString)
{
	// Make sure its not just an empty string
	if(!strlen(szNewString))
	{
		return -1;
	}

	// Make sure all columns are unique strings, must not exist already
	if(iFindColumn(szNewString) != -1)
	{
		return -1;
	}

	// Get a column that has not yet been allocated
	int iEntryColumn = iGetNextEmptyColumn();

	// Keep the existing pointer to the column entry list
	CStr **BackupEntryColumns = EntryColumns;

	// Allocate a bigger list
	EntryColumns = new CStr *[iNumEntryColumns + 1];
	
	// Make sure it was successfully allocated
	if(!EntryColumns)
	{
		EntryColumns = BackupEntryColumns;
		return -1;
	}

	// Copy old list to new list
	if(BackupEntryColumns)
		memcpy(EntryColumns, BackupEntryColumns, sizeof(CStr *) * iNumEntryColumns);

	// Create the new entry
	EntryColumns[iEntryColumn] = new CStr;

	// Check it was created
	if(!EntryColumns[iEntryColumn])
	{
		// Roll back
		delete EntryColumns;
		EntryColumns = BackupEntryColumns;
		return -1;
	}
	
	if(!EntryColumns[iEntryColumn]->SetString(szNewString))
	{
		// Roll back
		delete EntryColumns[iEntryColumn];
		delete EntryColumns;
		EntryColumns = BackupEntryColumns;
		return -1;
	}

	// Increment the column entry count
	iNumEntryColumns++;

	// Delete the old list
	if(BackupEntryColumns)
	{
		delete BackupEntryColumns;
	}

	return iEntryColumn;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStrEntry::CompareEntryColumn(int iEntryColumn, char *szComapreString)
{
	if(iEntryColumn < 0 || iEntryColumn >= iNumEntryColumns)
	{
		return false;
	}

	if(!EntryColumns[iEntryColumn])
	{
		return false;
	}

	return EntryColumns[iEntryColumn]->CompareString(szComapreString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrEntry::iGetNumColumns(void)
{
	return iNumEntryColumns;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrEntry::iGetNextEmptyColumn(void)
{
	return iNumEntryColumns;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrEntry::iFindColumn(char *szColumn)
{
	for(int iColumn = 0; iColumn < iNumEntryColumns; iColumn++)
	{
		if(CompareEntryColumn(iColumn, szColumn))
		{
			return iColumn;
		}
	}

	return -1;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iGetNumColumns(void)
{
	return Columns->iGetNumColumns();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iGetNumEntries(void)
{
	return iNumEntries;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iGetNumEntryColumns(int iEntry)
{
	if(!Entries)
	{
		return -1;
	}

	if(!Entries[iEntry])
	{
		return -1;
	}

	return Entries[iEntry]->iGetNumColumns();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iFindColumn(char *szColumn)
{
	return Columns->iFindColumn(szColumn);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iFindNextEntry(int iStartEntrySearch, char *szColumn, char *szEntryString)
{
	// First work out column from column list
	int iColumn = iFindColumn(szColumn);

	// Now search through all entries
	iFindNextEntry(iStartEntrySearch, iColumn, szEntryString);

	return -1;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iFindNextEntry(int iStartEntrySearch, int iColumn, char *szEntryString)
{
	// Check we have valid entries
	if(!Entries)
		return -1;

	// Now search through all entries
	for(int iEntry = iStartEntrySearch; iEntry < iNumEntries; iEntry++)
	{
		if(Entries[iEntry])
		{
			if(Entries[iEntry]->CompareEntryColumn(iColumn, szEntryString))
			{
				// Found match, return
				return iEntry;
			}
		}
	}

	return -1;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iFindEntry(int iColumn, char *szEntryString)
{
	return iFindNextEntry(false, iColumn, szEntryString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iFindEntry(char *szColumn, char *szEntryString)
{
	return iFindNextEntry(false, szColumn, szEntryString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iGetNextEmptyEntry(void)
{
	return iNumEntries;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::iGetNextEmptyColumn(void)
{
	return Columns->iGetNumColumns();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

char *CStrTable::GetColumn(int iColumn)
{
	return Columns->GetEntryColumn(iColumn);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStrTable::SetColumn(int iColumn, char *szNewString)
{
	return Columns->SetEntryColumn(iColumn, szNewString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::AddColumn(char *szNewString)
{
	return Columns->AddEntryColumn(szNewString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CStrTable::AddEntry(void)
{
	// Get new entry
	int iEntry = iGetNextEmptyEntry();

	// Keep existing pointer to the entries
	CStrEntry **BackupEntries = Entries;

	// Allocate a bigger list
	Entries = new CStrEntry *[iNumEntries + 1];

	// Make sure it was successfully allocated
	if(!Entries)
	{
		Entries = BackupEntries;
		return -1;
	}

	// Copy old list to new list
	if(BackupEntries)
		memcpy(Entries, BackupEntries, sizeof(CStr *) * iNumEntries);

	// Create the new entry
	Entries[iEntry] = new CStrEntry;

	// Check it was created
	if(!Entries[iEntry])
	{
		// Roll back
		delete Entries;
		Entries = BackupEntries;
		return -1;
	}

	// Increment the entry count
	iNumEntries++;

	// Delete the old list
	if(BackupEntries)
	{
		delete BackupEntries;
	}

	// Ok
	return iEntry;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStrTable::RemoveEntry(int iEntry)
{
	// Make sure the entry exists
	if(iEntry < 0 || iEntry > iNumEntries)
	{
		return false;
	}

	// Check main entries pointer
	if(!Entries)
	{
		return false;
	}

	// Keep existing pointer to the entries
	CStrEntry **BackupEntries = Entries;

	// Allocate a smaller list
	Entries = new CStrEntry *[iNumEntries - 1];

	// Copy first part of old list to new list
	memcpy(Entries, BackupEntries, sizeof(CStr *) * iEntry);

	// Copy second part of old list to new list, omitting the entry we want to delete
	memcpy(Entries + (sizeof(CStr *) * iEntry), BackupEntries + (sizeof(CStr *) * (iEntry + 1)), sizeof(CStr *) * (iNumEntries - iEntry));

	// Decrement the entry count
	iNumEntries--;

	if(!iNumEntries)
		Entries = NULL;

	// Delete the old list
	if(BackupEntries)
	{
		// Delete the missed out entry
		if(BackupEntries[iEntry])
		{
			delete BackupEntries[iEntry];
		}
		delete BackupEntries;
	}

	// Ok
	return true;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

char *CStrTable::GetEntryColumn(int iEntry, int iColumn)
{
	if(iEntry < 0 || iEntry >= iNumEntries)
	{
		return false;
	}

	if(!Entries)
		return false;

	if(!Entries[iEntry])
	{
		return false;
	}

	return Entries[iEntry]->GetEntryColumn(iColumn);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStrTable::SetEntryColumn(int iEntry, int iColumn, char *szNewString)
{
	if(iEntry < 0 || iEntry >= iNumEntries)
	{
		if(iEntry == iNumEntries)
		{
			if(AddEntry() == -1)
			{
				return false;
			}
		}
		else
		{
			return false;
		}
	}

	if(!Entries)
	{
		return false;
	}

	if(!Entries[iEntry])
	{
		return false;
	}

	return Entries[iEntry]->SetEntryColumn(iColumn, (char *)szNewString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

char *CStrTable::GetEntryColumn(int iEntry, char *szColumn)
{
	int iColumn = Columns->iFindColumn(szColumn);

	if(iColumn == -1)
	{
		return false;
	}

	return GetEntryColumn(iEntry, iColumn);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool CStrTable::SetEntryColumn(int iEntry, char *szColumn, char *szNewString)
{
	int iColumn = Columns->iFindColumn(szColumn);

	if(iColumn == -1)
	{
		iColumn = AddColumn(szColumn);

		if(iColumn == -1)
		{
			return false;
		}
	}

	return SetEntryColumn(iEntry, iColumn, szNewString);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CStrTable::ClearColumns(void)
{
	if(Columns)delete Columns;
	Columns = new CStrEntry;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CStrTable::ClearEntries(void)
{
	if(Entries)
	{
		for(int iEntry = 0; iEntry < iNumEntries; iEntry++)
		{
			if(Entries[iEntry])
			{
				delete Entries[iEntry];
			}
		}
		delete Entries;
		Entries = false;
	}

	iNumEntries = NULL;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////