
/*! \mainpage Keystone Persistence Library
 *
 * \section intro_sec Introduction
 *
 * @note Interfacing with this library is done through the following two objects:
 *
 * @note	1) DatabaseHandler - All interaction with the database is done through this object.
 *
 * @note	2) LacunaObj - Super class from which all persistence objects should inherited for them to be usable by the DatabaseHander.
 *
 *
 *
 *
 *
 *
 *
 * All other objects supplied by kdb are used internally by the above two objects, when using this library you do not have to worry about the rest of the objects.
 */


/**
 *@file DatabaseDriver.h
 *@author Stephan Krynauw (stephan@kses.net)
 *@version 1.2
 */

#ifndef DATABASEHANDLER_H_
#define DATABASEHANDLER_H_

#include <iostream>
#include <stdio.h>
#include <sqlite3.h>
#include <vector>
#include <string>

#include "DatabaseDriver.h"
#include "sqlite3Driver.h"
#include "QueryBuilder.h"
#include "LacunaObj.h"


using namespace std;

/**
 * @enum DatabaseType Database types supported by the Handler.
 * @note For each type named here, a subsequent Driver class needs to be present.
 */
enum DatabaseType {
	SQLITE3
};


/**
 * Class used to supply the link between LacunaObjects and the database from the user side.
 *
 * @note Before using a method, read the notes/warnings of the method, they describe limitations of the input objects which are usable.
 * @bug When segfaults happen inside the DatabaseHandler, it is usually the result of the LacunaObj's not being created EXACTLY as they should be,
 * when ->next variables inside structures aren't specifically pointed to NULL when there are no next objects, the handler segfaults.
 */
class DatabaseHandler {
public:

	/**
	 * Constructor for database handler class.
	 *
	 * @param type Specifies the type of database used. Valid types SQLITE3.
	 * @param databaseName Name of already created database, or database to be created (depending on createIfNotExists value).
	 * @param openInMemory If true, open database in memory, thus increasing speed but risk of loss of data.
	 * @param createIfNotExist If true, will create the database if it doesn't exist.
	 * @returns Nothing returned ,always call the getErrorMsg() method after creating a DatabaseHandler object in order to confirm whether the object has been created successfully.
	 * @returns getErrMsg() will return "" if handler was created successfully, otherwise an error msg will be returned.
	 */
	DatabaseHandler(DatabaseType type, const char* databaseName,bool openInMemory,bool createIfNotExists);

	/**
	 * Adds one LacunaObj object to the database.
	 *
	 * If this object inherits from another object, a row will be added to all tables corresponding to the inheritance structure.
	 * @note If multiple objects of the same type has to be added, rather use Add(vector<LacunaObj*>* objVector)
	 *
	 * @param obj LacunaObj to be added to the database
	 * @returns Returns 1 if successful
	 * @returns Should call getErrorMsg() if a 0 is returned
	 */
	int Add(LacunaObj* obj);

	/**
	 * Adds a vector of objects to the database.
	 *
	 * The use of the vector addition is much faster than single additions (more than 10 times faster)
	 * @warn These objects have to be of the same type.
	 *
	 * @param objVector vector of LacunaObj's to be added to the database
	 * @returns Returns 1 if successful
	 * @returns Should call getErrorMsg() if a 0 is returned
	 */
	int Add(vector<LacunaObj*>* objVector);

	/**
	 * Perform a SELECT query on the database with all values set in the LacunaObj.
	 *
	 * @note Call returnData->free() in order to free memory created within this method.
	 * @param obj Object used to fill query values.
	 * @returns returnData TableData pointer to data returned from query, will be NULL if no results were found.
	 */
	int Find(LacunaObj* obj, TableData** returnData);

	/**
	 * Perform a SELECT query on the database from the primary key values in the LacunaObj.
	 * @note Call returnData->free() in order to free memory created within this method.
	 * @param obj Object used to fill query values.
	 * @retval returnData TableData pointer to data returned from query, will be NULL if no results were found.
	 */
	int FindIdxOnly(LacunaObj* obj, TableData** returnData);

	/**
	 * Perform a SELECT query on the database from the primary key value between parameters specified
	 *
	 * @note Call returnData->free() in order to free memory created within this method.
	 * @warning Should only be used with objects which only have a single INTEGER primary key.
	 * @param obj Object used to fill query values.
	 * @param from Int value to specify start of query
	 * @param to Int value to specify end of query
	 * @retval returnData TableData pointer to data returned from query, will be NULL if no results were found.
	 */
	int FindIdxOnlyBetween(LacunaObj* obj, TableData** returnData, int from, int to);

	/**
	 * Perform a SELECT query on the database from the primary key values in the LacunaObj and only return a limited number of rows.
	 *
	 * @note Call returnData->free() in order to free memory created within this method. Rows returned from this Query will be returned in reverse order (Issue with the way the queries are built, done to save processing)
	 * @param obj Object used to fill query values.
	 * @param maxReturnRows Maximum number of rows which will be returned
	 * @retval returnData TableData pointer to data returned from query, will be NULL if no results were found.
	 */
	int FindIdxOnlyWithLimit(LacunaObj* obj, TableData** returnData, int maxReturnRows);

	/**
	 * Perform a SELECT query on the database from the primary key value specified in the from paramater and only return a limited number of rows.
	 *
	 * @note Call returnData->free() in order to free memory created within this method.
	 * @warning Should only be used with objects which only have a single INTEGER primary key.
	 * @param obj Object used to fill query values.
	 * @param from Int value to specify start of query
	 * @param maxReturnRows Maximum number of rows which will be returned
	 * @retval returnData TableData pointer to data returned from query, will be NULL if no results were found.
	 */
	int FindIdxOnlyFromWithLimit(LacunaObj* obj, TableData** returnData, int from, int maxReturnRows);

	/**
	 * Perform multiple SELECT queries on the database with all values set in the LacunaObj.
	 *
	 * @note Call returnData->free() in order to free memory created within this method.
	 * @param objVector vector of LacunaObj's used to fill query values.
	 * @retval returnVector Vector of TableData pointers to data returned from query, will be NULL if no results were found.
	 */
	int Find(vector<LacunaObj*>* objVector,vector<TableData*>** returnVector);

	/**
	 * Perform a REMOVE query on the database with all values set in the LacunaObj.
	 *
	 * @param obj Object used to fill query values.
	 */
	int Remove(LacunaObj* obj);

	/**
	 * Perform a REMOVE query on the database with primary key values set in the LacunaObj.
	 *
	 * @param obj Object used to fill query values.
	 */
	int RemoveIdxOnly(LacunaObj* obj);

	/**
	 * Perform multiple REMOVE queries on the database with all values set in the LacunaObj.
	 *
	 * @param obj vector of LacunaObj's used to fill query values.
	 */
	int Remove(vector<LacunaObj*>* obj);

	/**
	 * Perform a REMOVE query on the database by querieng only the primary key values up to the specified limit.
	 *
	 * @warning Can only be used with LacunaObjects with a single primary key value
	 * @param obj Object used to fill query values.
	 * @param val Int value to remove objects up to this value
	 */
	int RemoveUpTo(LacunaObj* obj, int val);

	/**
	 * Perform a REMOVE query on the database by querieng only the primary key values up to the specified limit.
	 *
	 * @warning Can only be used with LacunaObjects with a single primary key value
	 * @param obj Object used to fill query values.
	 * @param val Int value to remove objects from this value
	 */
	int RemoveFrom(LacunaObj* obj, int val);

	/**
	 * Perform an UPDATE query on the database with all values set in the LacunaObj.
	 *
	 * @note Primary Key values will be used to "FIND" the object while other values set in the object are changed in the Database.
	 * Thus the primary key values of the row can't be changed from this method
	 *
	 *
	 * @param obj Object used to fill query values.
	 */
	int Update(LacunaObj* obj);

	/**
	 * Perform an UPDATE query on the database with all values set in the LacunaObjects.
	 *
	 * @param findObj Object used to find the row which will be updated.
	 * @param updateObj Object used from which the values will be set, only initialized values will be changed.
	 */
	int Update(LacunaObj* findObj, LacunaObj* updateObj);

	/**
	 * Perform a Schema check with this object specified
	 * Checks whether a table exists which matches the Inherited LacunaObj's structure
	 *
	 * @param obj Object used to query the database, no values need to be set in the object
	 * @return 1 if it does exist and matches the object
	 * @return 0 if table does not exist
	 * @return -1 if table exists but fields do not match
	 */
	int CheckSchema(LacunaObj* obj);

	/**
	 * Creates a table from the object specified
	 * Checks whether a table exists which matches the Inherited LacunaObj's structure
	 *
	 * @param obj Object used to query the database, no values need to be set in the object
	 * @return 1 Table created
	 * @return 0 Table with name specified already exists
	 */
	int CreateTable(LacunaObj* obj);

	/**
	 * Returns the latest error message generated by the DatabaseHandler
	 * @note It is advised that this method is called after each use of a method in the DatebaseHandler
	 */
	char* getErrorMsg();
	virtual ~DatabaseHandler();
private:
	void clearErrorMsg() {
		strcpy(errString, "");
	}
	char* errString;
	DatabaseType Type;
	DatabaseDriver* Driver;
	QueryBuilder* Builder;
};
#endif /* DATABASEHANDLER_H_ */
