/**
 *@file LacunaObj.h
 *@author Stpehan Krynauw (stephan@kses.net)
 *@date March 2014
 */

#ifndef LACUNAOBJ_H_
#define LACUNAOBJ_H_

#define MAX_NAME_LENGTH 128
#define MAX_UNION_DATA_LENGTH 1024

#include <stdio.h>
#include <string.h>
#include <vector>
#include <cmath>


enum sqlType{
	SQL_TYPE_ERROR,
	SQL_TYPE_INTEGER,
	SQL_TYPE_TEXT,
	SQL_TYPE_FLOAT,
	SQL_TYPE_BLOB,
	SQL_TYPE_NULL
};

/**
 * @struct ColumnDataList
 * Stores the data in each field of a row
 *
 * Refer to the TableData class for a visual depiction of how the structures are linked
 */
struct ColumnDataList{

	/**
	 * Specific value of a field in the database.
	 * Each underlying value can be reached through this union, the type of the union is stored in the ColumnNameList structure
	 */
	union {
		long valI;
		double valF;
		char valT[MAX_UNION_DATA_LENGTH];
		unsigned char valB[MAX_UNION_DATA_LENGTH];
		void* valN;
	} value;

	/**Pointer to next ColumnDataList object, can be seen as pointer to next field in database*/
	ColumnDataList* next;
};

/**
 * @struct ColumnNameList
 * Stores names of columns in a table
 *
 *
 * Refer to the TableData class for a visual depiction of how the structures are linked
 */
struct ColumnNameList {
	/**Name of a column*/
	char name[MAX_NAME_LENGTH];
	/**Used in conjuction with ColumnDataList in order to determine type of data to be accessed in the union*/
	sqlType type;
	/**Is this column a primary key*/
	bool isPrimaryKey;
	/**Pointer to the next column in the table*/
	ColumnNameList* next;
};

/**
 * @struct RowList
 * Stores a pointer to the first ColumnDataList, this pointer can then be used to iterate through all fields in the row.
 *
 * Refer to the TableData class for a visual depiction of how the structures are linked
 */
struct RowList {
	/**Pointer to first field in row*/
	ColumnDataList* head;
	/**Pointer to next row*/
	RowList* next;
};

/*
 * @class TableData
 * TableData object is used to provide a single pointer in which numerous rows of data across different tables can be stored.
 * Look in header file for visual depiction of how they are structured
 *
 * VISUAL DEPICTION OF HOW TableData and the structures are linked.
 *
 * 								 ColumnNameList Object;					 ColumnNameList Object;
 * TableData*
 *
 * 		-> headColNames -> (name SeqNum, type INT, isPrimary true) next ->  (name Label, type TEXT, isPrimary false)
 *
 * 								 ColumnDataList Object;						ColumnDataList Object;
 * 		-> headRowList -> head -> (value.valI = 1)						 next -> value.valT = "mySite"
 * 					  -> next -> (value.valI = 2)						 next -> value.valT = "anotherSite"
 * 					  -> next -> (value.valI = 3)						 next -> value.valT = "yetAnotherSite"
 *
 * 	    -> next    (This will point to another table, each TableData pointer shows which table is currently used,
 * 	    then the data and column names are inside the headColNames and headRowList respectively
 */
class TableData {

public:
	TableData() {
		headColNames = NULL;
		headRowList = NULL;
		next = NULL;
	}

	/**
	 * Should be called as the destructor of this object
	 */
	void free() {
		freeObj(this);
	}

	/**
	 * Prints structured lists of rows and tables which data is held in this object
	 */
	void print();

	~TableData() {
	}

	/**Stores the name of the table	*/
	char tableName[MAX_NAME_LENGTH];
	/**Pointer to the first ColumnNameList structure*/
	ColumnNameList* headColNames;
	/**Pointer to the first RowList structure*/
	RowList* headRowList;
	/**Pointer to data in a next table, used when a LacunaObj inherits from one or more other LacunaObj's*/
	TableData* next;
private:
	void freeObj(TableData* theData);
};
//End structures used to create a table representation in memory

//Super class for objects persisted by the library
class LacunaObj {
public:
	LacunaObj() {};
	/**Copy Constructor*/
	LacunaObj(const LacunaObj& obj) {};
	/**
	 * Constructor to create a LacunaObj from retrieved TableData
	 * @param tableData TableData object from which LacunaObj created.
	 * @param rowNum RowNumber from which the LacunaObj should be created.
	 * */
	LacunaObj(const TableData* tableData, int rowNum) {};

	/**
	 * Creates a TableData object which could be used to check if the object corresponds with the schema of a Table in a database.
	 * This method does not use any data specific to the LacunaObj, only checks whether the way in which the ColumnNames and Datatypes are specified are correct
	 */
	virtual TableData* toSchemaCheck() = 0;
	/**
	 * Takes the current data inside the LacunaObj and converts it to a TableData format to be used by the DatabaseHandler.
	 */
	virtual TableData* toDatabaseFormat() = 0;
	virtual bool allPrimaryKeyFieldsSet() = 0;
	virtual ~LacunaObj() {};
protected:
	/**
	 * Create a LacunaObj from a TableData object.
	 * @param tableData TableData object from which LacunaObj created.
	 * @param rowNum RowNumber from which the LacunaObj should be created.
	 */
	virtual void toObjectFormat(const TableData* tableData, int rowNum) = 0;
};


/**
 * @class PResultSet
 * Class used to store a set of LacunaObj objects.
 */
class PResultSet
{
private:
   std::vector<LacunaObj*> mList;
   int mCurrIdx;
public:
   PResultSet()
   {
      mCurrIdx = 0;
   }

   ~PResultSet()
   {
      for(int ctr = 0;ctr < (int)mList.size();ctr++)
      {
         delete mList[ctr];
      }
   }

   /**
    * @returns Returns LacunaObj at current index as stored by the Object
    */
   LacunaObj* get()
   {

	   LacunaObj* r = NULL;
      if(mCurrIdx < (int)mList.size())
      {
         r = mList[mCurrIdx];
         mCurrIdx++;
      }
      return r;
   }

   /**
    * @returns Returns the size of the list
    */
   int size()
   {
      return mList.size();
   }

   /**
    * Resets current index
    */
   void first()
   {
      mCurrIdx = 0;
   }

   /**
    * @param obj Adds LacunaObj to the list
    */
   void add(LacunaObj *obj)
   {
      mList.push_back(obj);
   }
};

#endif /* LACUNAOBJ_H_ */
