#ifndef GENERIC_TABLE_H
#define GENERIC_TABLE_H


#include <string>
#include <vector>
#include <stdarg.h>
#include <stdint.h>
#include <QVariant>
#include "NonCopyable.h"


class GenericTableBase
{
public:
	GenericTableBase(bool a_readOnly)
	{
		m_callbackData = 0;
		m_updateCallback = 0;
		m_readOnly = a_readOnly;
		m_currentRow = -1;
	}

	virtual ~GenericTableBase() {}
	virtual void notifyObservers() {}
	virtual void dropData(const char* a_string) {}
	virtual size_t size() { return 0; }
	virtual QVariant value(int a_row, int a_column) const { return "Invalid"; }
	virtual void setValue(int a_row, int a_column, const QVariant& a_value) {}

	// No need to call this directly, it should be called and hooked up by the UI code for the table
	void RegisterCallbackOnChange(void(*a_callback)(void *data), void* a_callbackData)
	{
		m_updateCallback = a_callback;
		m_callbackData = a_callbackData;
	}

	// Column names/titles
	std::vector<std::string>     m_columnNames;
	std::vector<QVariant::Type>  m_columnTypes;
	bool m_readOnly;
	int m_currentRow;
	void(*m_updateCallback)(void* data);
	void* m_callbackData;
};


template <typename RowType>
class GenericTable : public GenericTableBase
{
public:
	// When using, terminate argument list with an argument of 0
	GenericTable(bool a_readOnly, const char* a_str1 = 0, ...) : GenericTableBase(a_readOnly)
	{
		va_list args;
		va_start(args, a_str1);
		const char* str = a_str1;
		while (str)
		{
			m_columnNames.push_back(std::string(str));
			m_columnTypes.push_back((QVariant::Type)va_arg(args, int));
			str = va_arg(args, const char*);
		}
		va_end(args);
	}

	void push_back(RowType item)
	{
		m_data.push_back(item);
	}

	size_t size()
	{
		return m_data.size();
	}

	QVariant value(int a_row, int a_column) const
	{
		return m_data[a_row].value(a_column);
	}

	void setValue(int a_row, int a_column, const QVariant& a_value)
	{
		updateData(a_row, a_column, a_value);
	}

	bool updateData(uint32_t a_row, uint32_t a_column, const QVariant& a_val)
	{
		if (m_readOnly)
			return false;
		m_data[a_row].setValue(a_column, a_val);
		if (m_updateCallback)
			m_updateCallback(m_callbackData);
		notifyObservers();
		return true;
	}

	// When making changes to the table, create a Transaction object at the
	// beginning of the code block that surrounds the code that is making the changes
	// (currently everything is assumed to be single threaded)
	class Transaction : private NonCopyable
	{
	public:
		explicit Transaction(GenericTable<RowType>& a_parent) : m_parent(a_parent) {}
		~Transaction() { if (m_parent.m_updateCallback) m_parent.m_updateCallback(m_parent.m_callbackData); }
	private:
		GenericTable<RowType>& m_parent;
	};

private:
	std::vector<RowType> m_data;
};


#endif // GENERIC_TABLE_H
