
/**
 * Simulates a connection to a database by making asynchronous requests to a
 * remote REST service that returns the results in JSON format.
 * Using this class can perform any operation that can be done with a database,
 * like open/close a connection or selecion/updating queries.
 *
 * Warning! You should set a well defined dababase level privileges to use this
 * class or you could have a serious security hole in you application becasuse
 * the user can send any statement to the server. For example: DROP DATABASE
 */
var Connection = new Class ();
module.exports = Connection;

var Flag =
{
	 NOT_NULL : 1
	,PRI_KEY  : 2
	,AI       : 512 | 2 | 1
};

var Type =
{
	 BOOLEAN   : 1
	,INTEGER   : 3
	,DOUBLE    : 4
	,STRING    : 5
	,DATE      : 8
	,DATE_TIME : 9
};

Connection.extend
({
	 Flag: Flag
	,Type: Type
});

Connection.implement
({
	Extends: Vn.JsonConnection

	/**
	 * Runs a SQL query on the database.
	 *
	 * @param {string} sql The SQL statement
	 * @param {function} callback The function to call when operation is done
	 */
	,execSql: function (sql, callback)
	{
		if (enableDebug)
			console.log (sql);

		this.send ('core/query', {'sql': sql},
			this._onExec.bind (this, callback));
	}
	
	/**
	 * Runs a stmt on the database.
	 *
	 * @param {Sql.Stmt} stmt The statement
	 * @param {function} callback The function to call when operation is done
	 * @param {Object} params The statement parameters
	 */
	,execStmt: function (stmt, callback, params)
	{
		this.execSql (stmt.render (params), callback);
	}

	/**
	 * Runs a query on the database.
	 *
	 * @param {string} query The SQL statement
	 * @param {function} callback The function to call when operation is done
	 * @param {Object} params The statement parameters
	 */
	,execQuery: function (query, callback, params)
	{
		this.execSql (this.renderQuery (query, params), callback);
	}

	/**
	 * Renders a query.
	 *
	 * @param {string} query The SQL statement
	 * @param {Object} params The statement parameters
	 * @return {string} The rendered statement
	 */
	,renderQuery: function (query, params)
	{
		return new Sql.String ({query: query}).render (params);
	}

	/**
	 * Renders an SQL statement object.
	 * 
	 * @param {string} stmt The SQL statement
	 * @param {Object} params The statement parameters
	 * @return {string} The rendered statement
	 */
	,renderStmt: function (stmt, params)
	{
		return stmt.render (params);
	}

	/*
	 * Parses a value to date.
	 */
	,valueToDate: function (value)
	{
		return new Date (value * 1000);
	}

	/*
	 * Called when a query is executed.
	 */
	,_onExec: function (callback, json, error)
	{
		if (json)
		try {
			if (json && json instanceof Array)
			for (var i = 0; i < json.length; i++)
			if (json[i] !== true)
			{
				var data = json[i].data;
				var columns = json[i].columns;
				var nCols = columns.length;

				var result = [];
				var castFuncs = new Array (nCols);

				for (var j = 0; j < nCols; j++)
				{
					var castFunc = null;
			
					switch (columns[j].type)
					{
						case Type.DATE:
						case Type.DATE_TIME:
						case Type.TIMESTAMP:
							castFunc = this.valueToDate;
							break;
					}

					if (castFunc !== null)
					{
						castFuncs[j] = castFunc;

						if (columns[j].def != null)
							columns[j].def = castFunc (columns[j].def);
					}
				}

				for (var j = 0; j < data.length; j++)
				{
					var row = data[j];

					var object = {};
					result.push (object);

					for (var k = 0; k < nCols; k++)
					{
						var castFunc = castFuncs[k];
						object[columns[k].name] =
							castFunc ? castFunc (row[k]) : row[k];
					}
				}

				json[i].data = result;
			}
		}
		catch (e)
		{
			error = e;
		}

		if (callback)
			callback (new Db.ResultSet (json, error));
	}
});

