<?php
	/**
	* SQL class. Used for handling SQL connections
	*
	* @module sql
	* @class SQL
	* @constructor
	*/
	class SQL {
		/**
		* This is the mysqli connection beneath everything
		* 
		* @property sql
		* @type {mysqli}
		* @private
		* @required
		*/
		private $sql;
		public function __construct($server,$user,$pass,$db){
			$this->sql = new mysqli($server,$user,$pass,$db) or die('Unable to connect to mysql');
		}
		public function __invoke(){
			return $this->sql;
		}
		public function __get($name){
			switch($name){
				case 'error':
					return $this->sql->error;
				break;
				case 'insert_id':
					return $this->sql->insert_id;
				break;
			}
		}
		/**
		* Returns a Query object based on inputs
		*
		* @method query
		* @param {String} sql The sql expression to run
		* @param {String=null} [types] A string containing all the types of arguments being passed
		* @param {Mixed} [bindings]* The bindings to use in the sql statement
		* @return {Query} Returns the query object
		*/
		public function query(...$args){
			return new Query(...array_merge([$this], $args));
		}
		public function escape($s){
			return $this->sql->escape_string($s);
		}
		public function charset($charset){
			return $this->sql->set_charset($charset);
		}
		public static function make_referenced(&$arr){
			$refs = [];
			foreach($arr as $key => $value){
				$refs[$key] = &$arr[$key];
			}
			return $refs;
		}
	}
	/**
	* Query class. Returned by SQL::query()
	*
	* @class Query
	* @constructor
	*/
	class Query {
		private $query;
		private $sql;
		public function __construct($sql, $source, $types=null, ...$args){
			$args = array_merge([$types], $args);
			$this->sql = $sql();
			$this->query = $this->sql->prepare($source);
			if(!is_null($types)){
				if(!$this->query->bind_param(...SQL::make_referenced($args))){
					throw new Exception("Unable to bind parameter {$this->query->error}");
				}
			}
		}
		public function __invoke(){
			return $this->query;
		}
		public function execute(){
			if($this->query){
				$r = $this->query->execute();
				$this->sql->commit();
				return $r;
			}else{
				return false;
			}
		}
		public function __get($name){
			switch($name){
				/**
				* Returns the mysqli::results object for the
				* query
				* 
				* @property results
				* @type {mysqli::results}
				* @public
				*/
				case 'results':
					if($this->query){
						$this->execute();
						$result = $this->query->get_result();
						$this->query->close();
						return $result;
					}else{
						return false;
					}
				break;
				/**
				* Returns an associative array of the query resulsts
				* 
				* @property assoc_results
				* @type {Array}
				* @public
				*/
				/**
				* Returns an associative array of the query resulsts
				* 
				* @property resulsts_assoc
				* @type {Array}
				* @public
				*/
				case 'assoc_results':case 'results_assoc':
					if($this->query){
						$a = [];
						$r = $this->results;
						while($row = $r->fetch_assoc()){
							array_push($a,$row);
						}
						return $a;
					}else{
						return false;
					}
				break;
				/**
				* Returns a numbered array of the query results
				* 
				* @property num_results
				* @type {Array}
				* @public
				*/
				/**
				* Returns a numbered array of the query results
				* 
				* @property resulsts_num
				* @type {Array}
				* @public
				*/
				case 'num_results':case 'results_num':
					if($this->query){
						$a = [];
						$r = $this->results;
						while($row = $r->fetch_num()){
							array_push($a,$row);
						}
						return $a;
					}else{
						return false;
					}
				break;
				case 'assoc_result':case 'result_assoc':
					if($this->query){
						$r = $this->results;
						return $r?$r->fetch_assoc():false;
					}else{
						return false;
					}
				break;
				case 'num_result':case 'result_num':
					if($this->query){
						$r = $this->results;
						return $r?$r->fetch_num():false;
					}else{
						return false;
					}
				break;
				case 'insert_id':
					return $this->sql->insert_id;
				break;
			}
		}
	}
?>