#ifndef _T_JObjTableData
#define _T_JObjTableData

/******************************************************************************
 JObjTableData.tmpl

	Template for storing objects in a table.

	*** Since PtrArray is used to store the data, this is best for objects.
		To store structs, use JValueTableData or JPackedTableData instead.

	BASE CLASS = JTableData

	Copyright © 1996 John Lindal. All rights reserved.

 ******************************************************************************/

#include <JObjTableData.h>
#include <JMinMax.h>
#include <jAssert.h>

/******************************************************************************
 Constructor

 ******************************************************************************/

template <class T>
JObjTableData<T>::JObjTableData()
	:
	JTableData()
{
	itsCols = new JPtrArray< JPtrArray<T> >;
	assert( itsCols != NULL );
}

/******************************************************************************
 Copy constructor

 ******************************************************************************/

template <class T>
JObjTableData<T>::JObjTableData
	(
	const JObjTableData<T>& source
	)
	:
	JTableData(source)
{
	itsCols = new JPtrArray< JPtrArray<T> >;
	assert( itsCols != NULL );

	const JSize colCount = (source.itsCols)->GetElementCount();
	for (JIndex j=1; j<=colCount; j++)
		{
		const JPtrArray<T>* origColData = (source.itsCols)->NthElement(j);

		JPtrArray<T>* newColData = new JPtrArray<T>;
		assert( newColData != NULL );
		itsCols->InsertAtIndex(j, newColData);

		const JSize rowCount = origColData->GetElementCount();
		for (JIndex i=1; i<=rowCount; i++)
			{
			const T* origElement = origColData->NthElement(i);
			T* newElement  = new T(*origElement);
			newColData->InsertAtIndex(i, newElement);
			}
		}
}

/******************************************************************************
 Destructor

 ******************************************************************************/

template <class T>
JObjTableData<T>::~JObjTableData()
{
	const JSize colCount = itsCols->GetElementCount();
	for (JIndex i=1; i<=colCount; i++)
		{
		(itsCols->NthElement(i))->DeleteAll();
		}
	itsCols->DeleteAll();
	delete itsCols;
}

/******************************************************************************
 GetElement

 ******************************************************************************/

template <class T>
const T&
JObjTableData<T>::GetElement
	(
	const JIndex row,
	const JIndex col
	)
	const
{
	JPtrArray<T>* colData = itsCols->NthElement(col);
	return *(colData->NthElement(row));
}

template <class T>
const T&
JObjTableData<T>::GetElement
	(
	const JPoint& cell
	)
	const
{
	JPtrArray<T>* colData = itsCols->NthElement(cell.x);
	return *(colData->NthElement(cell.y));
}

/******************************************************************************
 SetElement

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::SetElement
	(
	const JIndex	row,
	const JIndex	col,
	const T&		data
	)
{
	JPtrArray<T>* colData = itsCols->NthElement(col);
	T* element = colData->NthElement(row);
	*element = data;
	Broadcast(JTableData::RectChanged(row,col));
}

template <class T>
void
JObjTableData<T>::SetElement
	(
	const JPoint&	cell,
	const T&		data
	)
{
	JPtrArray<T>* colData = itsCols->NthElement(cell.x);
	T* element = colData->NthElement(cell.y);
	*element = data;
	Broadcast(JTableData::RectChanged(cell));
}

/******************************************************************************
 GetRow

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::GetRow
	(
	const JIndex	index,
	JPtrArray<T>*	rowData
	)
	const
{
	rowData->DeleteAll();

	const JSize colCount = GetColCount();
	for (JIndex i=1; i<=colCount; i++)
		{
		const JPtrArray<T>* dataCol = itsCols->NthElement(i);
		const T* origElement = dataCol->NthElement(index);
		T* newElement = new T(*origElement);
		assert( newElement != NULL );
		rowData->InsertAtIndex(i, newElement);
		}
}

/******************************************************************************
 SetRow

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::SetRow
	(
	const JIndex		index,
	const JPtrArray<T>&	rowData
	)
{
	const JSize colCount = GetColCount();
	assert( rowData.GetElementCount() == colCount );

	for (JIndex i=1; i<=colCount; i++)
		{
		JPtrArray<T>* dataCol = itsCols->NthElement(i);
		T* element = dataCol->NthElement(index);
		*element   = *(rowData.NthElement(i));
		}

	Broadcast(JTableData::RectChanged(JRect(index, 1, index+1, colCount+1)));
}

/******************************************************************************
 GetCol

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::GetCol
	(
	const JIndex	index,
	JPtrArray<T>*	colData
	)
	const
{
	colData->DeleteAll();

	const JSize rowCount = GetRowCount();
	const JPtrArray<T>* dataCol = itsCols->NthElement(index);
	for (JIndex i=1; i<=rowCount; i++)
		{
		const T* origElement = dataCol->NthElement(i);
		T* newElement = new T(*origElement);
		assert( newElement != NULL );
		colData->InsertAtIndex(i, newElement);
		}
}

/******************************************************************************
 SetCol

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::SetCol
	(
	const JIndex		index,
	const JPtrArray<T>&	colData
	)
{
	const JSize rowCount = GetRowCount();
	assert( colData.GetElementCount() == rowCount );

	JPtrArray<T>* dataCol = itsCols->NthElement(index);
	for (JIndex i=1; i<=rowCount; i++)
		{
		T* element = dataCol->NthElement(i);
		*element   = *(colData.NthElement(i));
		}

	Broadcast(JTableData::RectChanged(JRect(1, index, rowCount+1, index+1)));
}

/******************************************************************************
 InsertRow

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::InsertRow
	(
	const JIndex		index,
	const JPtrArray<T>*	initData
	)
{
	assert( initData == NULL || initData->GetElementCount() == GetColCount() );

	const JIndex trueIndex = JMin(index, GetRowCount()+1);

	const JSize colCount = GetColCount();
	for (JIndex i=1; i<=colCount; i++)
		{
		T* element = NULL;
		if (initData != NULL)
			{
			element = new T(*(initData->NthElement(i)));
			}
		else
			{
			element = new T;
			}
		assert( element != NULL );

		JPtrArray<T>* colData = itsCols->NthElement(i);
		colData->InsertAtIndex(trueIndex, element);
		}

	RowAdded();
	Broadcast(JTableData::RowInserted(trueIndex));
}

/******************************************************************************
 Row manipulations

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::PrependRow
	(
	const JPtrArray<T>* initData
	)
{
	InsertRow(1, initData);
}

template <class T>
void
JObjTableData<T>::AppendRow
	(
	const JPtrArray<T>* initData
	)
{
	InsertRow(GetRowCount()+1, initData);
}

/******************************************************************************
 DuplicateRow

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::DuplicateRow
	(
	const JIndex origIndex,
	const JIndex newIndex
	)
{
	const JIndex trueIndex = JMin(newIndex, GetRowCount()+1);

	const JSize colCount = GetColCount();
	for (JIndex i=1; i<=colCount; i++)
		{
		JPtrArray<T>* colData = itsCols->NthElement(i);

		T* origElement = colData->NthElement(origIndex);
		T* newElement  = new T(*origElement);
		assert( newElement != NULL );

		colData->InsertAtIndex(trueIndex, newElement);
		}

	RowAdded();
	Broadcast(JTableData::RowDuplicated(origIndex, trueIndex));
}

/******************************************************************************
 RemoveRow

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::RemoveRow
	(
	const JIndex index
	)
{
	const JSize colCount = GetColCount();
	for (JIndex i=1; i<=colCount; i++)
		{
		JPtrArray<T>* colData = itsCols->NthElement(i);
		colData->DeleteElement(index);
		}

	RowDeleted();
	Broadcast(JTableData::RowRemoved(index));
}

/******************************************************************************
 RemoveAllRows

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::RemoveAllRows()
{
	const JSize colCount = GetColCount();
	for (JIndex i=1; i<=colCount; i++)
		{
		JPtrArray<T>* colData = itsCols->NthElement(i);
		colData->DeleteAll();
		}

	SetRowCount(0);
	Broadcast(JTableData::AllRowsRemoved());
}

/******************************************************************************
 MoveRow

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::MoveRow
	(
	const JIndex origIndex,
	const JIndex newIndex
	)
{
	if (origIndex != newIndex)
		{
		const JSize colCount = GetColCount();
		for (JIndex i=1; i<=colCount; i++)
			{
			JPtrArray<T>* colData = itsCols->NthElement(i);
			colData->MoveElementToIndex(origIndex, newIndex);
			}

		Broadcast(JTableData::RowMoved(origIndex, newIndex));
		}
}

/******************************************************************************
 InsertCol

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::InsertCol
	(
	const JIndex		index,
	const JPtrArray<T>*	initData
	)
{
	assert( initData == NULL || initData->GetElementCount() == GetRowCount() );

	const JIndex trueIndex = JMin(index, GetColCount()+1);

	JPtrArray<T>* colData = new JPtrArray<T>;
	assert( colData != NULL );
	itsCols->InsertAtIndex(trueIndex, colData);

	const JSize rowCount = GetRowCount();
	for (JIndex i=1; i<=rowCount; i++)
		{
		T* element = NULL;
		if (initData != NULL)
			{
			element = new T(*(initData->NthElement(i)));
			}
		else
			{
			element = new T;
			}
		assert( element != NULL );

		colData->InsertAtIndex(i, element);
		}

	ColAdded();
	Broadcast(JTableData::ColInserted(trueIndex));
}

/******************************************************************************
 Column manipulations

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::PrependCol
	(
	const JPtrArray<T>* initData
	)
{
	InsertCol(1, initData);
}

template <class T>
void
JObjTableData<T>::AppendCol
	(
	const JPtrArray<T>* initData
	)
{
	InsertCol(GetColCount()+1, initData);
}

/******************************************************************************
 DuplicateCol

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::DuplicateCol
	(
	const JIndex origIndex,
	const JIndex newIndex
	)
{
	const JIndex trueIndex = JMin(newIndex, GetColCount()+1);

	JPtrArray<T>* origColData = itsCols->NthElement(origIndex);

	JPtrArray<T>* newColData = new JPtrArray<T>;
	assert( newColData != NULL );
	itsCols->InsertAtIndex(trueIndex, newColData);

	const JSize rowCount = GetRowCount();
	for (JIndex i=1; i<=rowCount; i++)
		{
		T* origElement = origColData->NthElement(i);
		T* newElement  = new T(*origElement);
		assert( newElement != NULL );
		newColData->InsertAtIndex(i, newElement);
		}

	ColAdded();
	Broadcast(JTableData::ColDuplicated(origIndex, trueIndex));
}

/******************************************************************************
 RemoveCol

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::RemoveCol
	(
	const JIndex index
	)
{
	JPtrArray<T>* colData = itsCols->NthElement(index);
	colData->DeleteAll();

	itsCols->DeleteElement(index);

	ColDeleted();
	Broadcast(JTableData::ColRemoved(index));
}

/******************************************************************************
 RemoveAllCols

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::RemoveAllCols()
{
	const JSize colCount = GetColCount();
	for (JIndex i=1; i<=colCount; i++)
		{
		JPtrArray<T>* colData = itsCols->NthElement(i);
		colData->DeleteAll();
		}

	itsCols->DeleteAll();

	SetColCount(0);
	Broadcast(JTableData::AllColsRemoved());
}

/******************************************************************************
 MoveCol

 ******************************************************************************/

template <class T>
void
JObjTableData<T>::MoveCol
	(
	const JIndex origIndex,
	const JIndex newIndex
	)
{
	if (origIndex != newIndex)
		{
		itsCols->MoveElementToIndex(origIndex, newIndex);
		Broadcast(JTableData::ColMoved(origIndex, newIndex));
		}
}

#endif

// Instantiate the template for the specified type.

#if defined JTemplateType && ! defined JOnlyWantTemplateDefinition
	#define JTemplateName JObjTableData
	#include <instantiate_template.h>
	#undef JTemplateName
#endif
