<?php 

	if (ENABLE_CONNECTION) {
		class Model{
			private static $last_query;

			private static function beginTransaction(){
				try{
					Connection::beginTransaction();
					return true;
				}
				catch (PDOException $e) {
					return false;
				}
			}

			private static function filter($value){
				return preg_replace("/[\.\,\#\?\*\@\!\$\&\+\(\)\[\]\{\}\´\`\^\~\'\"\=\;\ç\º\ª\á\à\ã\â\ä\ñ\é\è\ë\ê\í\ì\î\ï\ó\ò\ô\õ\ö\ú\ù\û\ü\Á\À\Ã\Â\Ä\Ñ\É\È\Ë\Ê\Í\Ì\Î\Ï\Ó\Ò\Ô\Õ\Ö\Ú\Ù\Û\Ü\Ç\ \¨\¬\£\¢\§\°\%\-]/", "", $value);
			}

			private static function getSQl($sql, $values){
				$query = $sql;
				foreach ($values as $key => $value) {
					$key = (strpos($key, ":") === false) ? ":".$key : $key;
					if (gettype($value) == "integer") {
						$query = str_replace(self::filter($key), $value, $query);
					}
					elseif (gettype($valeu) == "array") {
						$j = 0;
						foreach ($value as $v) {
							if (gettype($v) == "integer") {
								$query = str_replace(self::filter($key.$j), $v, $query);
							}
							else{
								$query = str_replace(self::filter($key.$j), Connection::quote($v), $query);
							}						
							$j++;
						}
					}
					else{
						$query = str_replace(self::filter($key), Connection::quote($value), $query);
					}
				}
				return $query;
			}

			public static function insert($table, $values){
				if (gettype($table) == "string" and gettype($values) == "array"){
					try{
						self::beginTransaction();

						$part1 = "INSERT INTO ".self::filter($table)." (";
						$part2 = ") VALUES (";
						$i = 0;

						foreach ($values as $key => $value){
							$k = self::filter($key);
							if ($i == 0) {
								$part1 .= $k;
								$part2 .= ":".$k;
							}
							else{
								$part1 .= ", ".$k;
								$part2 .= ", :".$k;
							}
							$i++;
						}

						$insert = Connection::prepare($part1.$part2.")");

						foreach ($values as $key => $value){
							if (gettype($value) == "integer"){
								$insert->bindValue(":".self::filter($key), $value, PDO::PARAM_INT);
							}
							elseif (gettype($value) == "array") {
								throw new Exception("ERROR: The value of the $key key is invalid, it does not allow array.", 500);
								return;
							}
							else{
								$insert->bindValue(":".self::filter($key), $value);
							}
						}

						$insert->execute();

						self::$last_query = self::getSQl($insert->queryString, $values);

						return Connection::lastInsertId();
					}
					catch (PDOException $e) {
						Connection::rollBack();
						throw new Exception("ERROR: There was an internal error in the Model class. (".$e->getMessage().")", 500);
					}
				}
				else{
					throw new Exception("ERROR: Invalid parameter value type.", 500);
				}
			}

			public static function update($table, $values, $conditions = []){
				if (gettype($table) == "string" and gettype($values) == "array" and gettype($conditions) == "array"){
					try{
						self::beginTransaction();

						$sql = "UPDATE ".self::filter($table)." SET ";

						$i = 0;

						foreach ($values as $key => $value){
							$k = self::filter($key);
							if ($i == 0) {
								$sql .= $k." = :".$k;
							}
							else{
								$sql .= ", ".$k." = :".$k;
							}
							$i++;
						}

						$i = 0;

						if (count($conditions) > 0) {
							$sql .= " WHERE ";
							foreach ($conditions as $key => $value) {
								$k = self::filter($key);
								if ($i == 0) {
									if (gettype($value) == "array"){
										$j = 0;
										$sql .= "(";
										foreach ($value as $v) {
											if ($j == 0) {
												$sql .= $k." = :".$k.$j;
											}
											else{
												$sql .= " OR ".$k." = :".$k.$j;
											}
											$j++;
										}
										$sql .= ")";
									}
									else{
										$sql .= $k." = :".$k;
									}
								}
								else{
									if (gettype($value) == "array"){
										$j = 0;
										$sql .= " AND (";
										foreach ($value as $v) {
											if ($j == 0) {
												$sql .= $k." = :".$k.$j;
											}
											else{
												$sql .= " OR ".$k." = :".$k.$j;
											}
											$j++;
										}
										$sql .= ")";
									}
									else{
										$sql .= " AND ".$k." = :".$k;
									}
								}
								$i++;
							}
						}                       

						$update = Connection::prepare($sql);

						foreach ($values as $key => $value){
							if (gettype($value) == "integer"){
								$update->bindValue(":".self::filter($key), $value, PDO::PARAM_INT);
							}
							elseif (gettype($value) == "array") {
								throw new Exception("ERROR: The value of the $key key is invalid, it does not allow array.", 500);
								return;
							}
							else{
								$update->bindValue(":".self::filter($key), $value);
							}
						}

						if (count($conditions) > 0) {
							foreach ($conditions as $key => $value){
								if (gettype($value) == "integer"){
									$update->bindValue(":".self::filter($key), $value, PDO::PARAM_INT);
								}
								elseif (gettype($value) == "array") {
									$j = 0;
									foreach ($value as $v) {
										if (gettype($v) == "integer") {                             
											$update->bindValue(":".self::filter($key.$j), $v, PDO::PARAM_INT);
										}
										elseif (gettype($v) == "array" or gettype($v) == "object" or gettype($v) == "resource" or gettype($v) == "boolean") {
											throw new Exception("ERROR: The value of the $key key is invalid, it does not allow array, object, resource and boolean.", 500);
											return;
										}
										else{
											$update->bindValue(":".self::filter($key.$j), $v);  
										}
										$j++;
									}   
								}
								elseif (gettype($value) == "object" or gettype($value) == "resource" or gettype($value) == "boolean") {
									throw new Exception("ERROR: The value of the $key key is invalid, it does not allow object, resource and boolean.", 500);
									return;
								}
								else{
									$update->bindValue(":".self::filter($key), $value);
								}
							}
						}

						$status = $update->execute();

						self::$last_query = self::getSQl($update->queryString, array_merge($values, $conditions));

						return ($status) ? 1 : 0;
					}
					catch (PDOException $e) {
						Connection::rollBack();
						throw new Exception("ERROR: There was an internal error in the Model class. (".$e->getMessage().")", 500);
					}
				}
				else{
					throw new Exception("ERROR: Invalid parameter value type.", 500);
				}
			}

			public static function delete($table, $conditions = []){
				if (gettype($table) == "string" and gettype($conditions) == "array"){
					try{
						self::beginTransaction();

						$sql = "DELETE FROM ".self::filter($table);

						$i = 0;

						if (count($conditions) > 0) {
							$sql .= " WHERE ";
							foreach ($conditions as $key => $value) {
								$k = self::filter($key);
								if ($i == 0) {
									if (gettype($value) == "array"){
										$j = 0;
										$sql .= "(";
										foreach ($value as $v) {
											if ($j == 0) {
												$sql .= $k." = :".$k.$j;
											}
											else{
												$sql .= " OR ".$k." = :".$k.$j;
											}
											$j++;
										}
										$sql .= ")";
									}
									else{
										$sql .= $k." = :".$k;
									}
								}
								else{
									if (gettype($value) == "array"){
										$j = 0;
										$sql .= " AND (";
										foreach ($value as $v) {
											if ($j == 0) {
												$sql .= $k." = :".$k.$j;
											}
											else{
												$sql .= " OR ".$k." = :".$k.$j;
											}
											$j++;
										}
										$sql .= ")";
									}
									else{
										$sql .= " AND ".$k." = :".$k;
									}
								}
								$i++;
							}
						}

						$delete = Connection::prepare($sql);

						if (count($conditions) > 0) {
							foreach ($conditions as $key => $value){
								if (gettype($value) == "integer"){
									$delete->bindValue(":".self::filter($key), $value, PDO::PARAM_INT);
								}
								elseif (gettype($value) == "array") {
									$j = 0;
									foreach ($value as $v) {
										if (gettype($v) == "integer") {
											$delete->bindValue(":".self::filter($key).$j, $v, PDO::PARAM_INT);
										}
										elseif (gettype($v) == "array" or gettype($v) == "object" or gettype($v) == "resource" or gettype($v) == "boolean") {
											throw new Exception("ERROR: The value of the $key key is invalid, it does not allow array, object, resource and boolean.", 500);
											return;
										}
										else{
											$delete->bindValue(":".self::filter($key).$j, $v);  
										}
										$j++;
									}   
								}
								elseif (gettype($value) == "object" or gettype($value) == "resource" or gettype($value) == "boolean") {
									throw new Exception("ERROR: The value of the $key key is invalid, it does not allow object, resource and boolean.", 500);
									return;
								}
								else{
									$delete->bindValue(":".self::filter($key), $value);
								}
							}
						}

						$status = $delete->execute();

						self::$last_query = self::getSQl($delete->queryString, $conditions);

						return ($status) ? 1 : 0;
					}
					catch (PDOException $e) {
						Connection::rollBack();
						throw new Exception("ERROR: There was an internal error in the Model class. (".$e->getMessage().")", 500);
					}
				}
				else{
					throw new Exception("ERROR: Invalid parameter value type.", 500);
				}
			}

			public static function query($sql, $values = []){
				if (gettype($sql) == "string" and gettype($values) == "array") {
					try{
						self::beginTransaction();

						$query = Connection::prepare($sql);

						if (count($values) > 0) {
							foreach ($values as $key => $value){
								if (gettype($value) == "integer"){
									$query->bindValue(self::filter($key), $value, PDO::PARAM_INT);
								}
								elseif (gettype($value) == "array" or gettype($value) == "object" or gettype($value) == "resource" or gettype($value) == "boolean") {
									throw new Exception("ERROR: The value of the $key key is invalid, it does not allow array, object, resource and boolean.", 500);
									return;
								}
								else{
									$query->bindValue(self::filter($key), $value);
								}
							}
						}

						$query->execute();
						
						self::$last_query = self::getSQl($query->queryString, $values);

						return $query;
					}
					catch (PDOException $e) {
						Connection::rollBack();
						throw new Exception("ERROR: There was an internal error in the Model class. (".$e->getMessage().")", 500);
					}
				}
				else{
					throw new Exception("ERROR: Invalid parameter value type.", 500);
				}           
			}

			public static function commit(){
				Connection::commit();
			}

			public static function rollBack(){
				Connection::rollBack();
			}

			public static function lastQuery(){
				return self::$last_query;
			}

			public static function issetElementInTable($table, $element, $exception = []){
				$sql = "SELECT * FROM ".self::filter($table)." WHERE ";
				$fields = [];
				$lenght = count($element);
				$count = 1;

				foreach ($element as $key => $value){
					$k = self::filter($key);
					$sql .= $k." = :".$k;
					if ($lenght > $count) {
						$sql .= " AND ";
					}
					$fields[":".$k] = $value;
					$count ++;
				}

				if (gettype($exception) == "array" and count($exception) > 0) {
					foreach ($exception as $key => $value){
						$k = self::filter($key);
						$sql .= " AND ".$k." <> :".$k;
						$fields[":".$k] = $value;
					}
				}

				$result = self::query($sql, $fields);

				if ($result->rowCount() > 0) {
					return true;
				}
				else{
					return false;
				}
			}
		}
	}
	else{
		throw new Exception("ERROR: Connection disabled.", 500);
	}

?>