// This Database Adapter depends on MySqliDatabase	
	if (!class_exists('QMySqli5Database'))
		require(__QCODO_CORE__ . '/database/QMySqli5Database.class.php');

	// New MySQL 5 constanst not yet in PHP (as of PHP 5.1.2)
	if (!defined('MYSQLI_TYPE_NEWDECIMAL'))
		define('MYSQLI_TYPE_NEWDECIMAL', 246);
	if (!defined('MYSQLI_TYPE_BIT'))
		define('MYSQLI_TYPE_BIT', 16);

	class QMySqli5MasterSlaveDatabase extends QMySqli5Database {
		const Adapter = 'MySql Improved Database Adapter for MySQL 5  (Master/Slave)';
		
		protected $objMysqliMaster;
		

		public function IsInMasterSlaveMode ()
		{
			if ($this->objMysqliMaster) return true;
			
			if (($this->master_server))  return true;
			else return false; 
		}

		public function GetTables() {
			// Connect if Applicable
			if (!$this->blnConnectedFlag) $this->Connect();

			// Use the MySQL5 Information Schema to get a list of all the tables in this database
			// (excluding views, etc.)
			$strDatabaseName = $this->Database;

			$objResult = $this->Query("
				SELECT
					table_name
				FROM
					information_schema.tables
				WHERE
					table_type <> 'VIEW' AND
					table_schema = '$strDatabaseName';
			");

			$strToReturn = array();
			while ($strRowArray = $objResult->FetchRow())
				array_push($strToReturn, $strRowArray[0]);
			return $strToReturn;
		}

		/**
		 * to query master...
		 *
		 * @param string $strQuery
		 */
		public function QueryMaster($strQuery) {
			if (!$this->IsInMasterSlaveMode())
				throw new QMySqliDatabaseException("queryMaster: no master/slave setup", -1, $strQuery);
			
			// Connect if Applicable
			if (!$this->blnConnectedFlag) $this->Connect();

			// Log Query (for Profiling, if applicable)
			$this->LogQuery($strQuery);

			// Perform the Query
			$objResult = $this->objMysqliMaster->query($strQuery);
			if ($this->objMysqliMaster->error)
				throw new QMySqliDatabaseException($this->objMysqliMaster->error, $this->objMysqliMaster->errno, $strQuery);

			// Return the Result
			$objMySqliDatabaseResult = new QMySqli5DatabaseResult($objResult, $this);
			return $objMySqliDatabaseResult;
		}
		
		
		public function Query($strQuery) {
			
			
			
			// Connect if Applicable
			if (!$this->blnConnectedFlag) $this->Connect();

			// Log Query (for Profiling, if applicable)
			$this->LogQuery($strQuery);

			// Perform the Query
			// sometimes we want to query master (i.e. for highly interactive cases, like job postings)
			// this switch is normally OFF, turned ON by say jobs.uloop.com
			if (QApplication::$MysqlForceMaster)
//			if (false)
			{
//				ULogger::log("force query master :\n $strQuery");
				$objResult = $this->objMysqliMaster->query($strQuery);
				if ($this->objMysqliMaster->error)
					throw new QMySqliDatabaseException($this->objMysqliMaster->error, $this->objMysqliMaster->errno, $strQuery);
				
			}
			else
			{
//				ULogger::log("regular query :\n $strQuery");
				$objResult = $this->objMySqli->query($strQuery);
				if ($this->objMySqli->error)
					throw new QMySqliDatabaseException($this->objMySqli->error, $this->objMySqli->errno, $strQuery);
			}
			// Return the Result
			$objMySqliDatabaseResult = new QMySqli5DatabaseResult($objResult, $this);
			return $objMySqliDatabaseResult;
		}

		/**
		 * Performs a Multi Result-Set Query, which is available with Stored Procs in MySQL 5
		 * Written by Mike Hostetler
		 *
		 * @param string $strQuery
		 * @return QMySqli5DatabaseResult[] array of results
		 */
		public function MultiQuery($strQuery) {
			// Connect if Applicable
			if (!$this->blnConnectedFlag) $this->Connect();

			// Log Query (for Profiling, if applicable)
			$this->LogQuery($strQuery);

			// Perform the Query
			$this->objMySqli->multi_query($strQuery);
			if ($this->objMySqli->error)
				throw new QMySqliDatabaseException($this->objMySqli->error, $this->objMySqli->errno, $strNonQuery);

			$objResultSets = array();
			do {
				if ($objResult = $this->objMySqli->store_result()) {
					array_push($objResultSets,new QMySqli5DatabaseResult($objResult, $this));
				}
			} while ($this->objMySqli->next_result());

			return $objResultSets;
		}
		
		public function NonQuery($strNonQuery) {
			if ($this->IsInMasterSlaveMode())
				$this->NonQueryMaster($strNonQuery);
			else
				$this->NonQuerySlave($strNonQuery);
			

		}
		
		protected function NonQuerySlave($strNonQuery)
		{
			// Connect if Applicable
			if (!$this->blnConnectedFlag) $this->Connect();

			// Log Query (for Profiling, if applicable)
			$this->LogQuery($strNonQuery);

			// Perform the Query
			$this->objMySqli->query($strNonQuery);
			if ($this->objMySqli->error)
				throw new QMySqliDatabaseException($this->objMySqli->error, $this->objMySqli->errno, $strNonQuery);
		}
		
		protected function NonQueryMaster($strNonQuery)
		{
			if (!$this->blnConnectedFlag) $this->Connect();
			
			$this->LogQuery($strNonQuery);
			
			$this->objMysqliMaster->query($strNonQuery);
			
			if ($this->objMysqliMaster->error)
				throw new QMySqliDatabaseException($this->objMysqliMaster->error, $this->objMysqliMaster->errno, $strNonQuery);
		}
		
		public function Connect() {
			$this->ConnectToSlave();
			// check if master is defined
			if ($this->IsInMasterSlaveMode())
			{
				$this->ConnectToMaster();
			}

			// Update "Connected" Flag
			$this->blnConnectedFlag = true;
		}
		
		protected function ConnectToSlave()
		{
				// Connect to the Database Server
			$this->objMySqli = new MySqli($this->Server, $this->Username, $this->Password, $this->Database, $this->Port);

			if (!$this->objMySqli)
				throw new QMySqliDatabaseException("Unable to connect to Database", -1, null);
			
			if ($this->objMySqli->error)
				throw new QMySqliDatabaseException($this->objMySqli->error, $this->objMySqli->errno, null);
				

			// Set to AutoCommit
//			$this->NonQuery('SET AUTOCOMMIT=1;');
			$this->RunQueryOnDb($this->objMySqli, 'SET AUTOCOMMIT=1;');

			// Set NAMES (if applicable)
			if (array_key_exists('encoding', $this->objConfigArray))
			{
//				$this->NonQuery('SET NAMES ' . $this->objConfigArray['encoding'] . ';');
				$this->RunQueryOnDb( $this->objMySqli, 'SET NAMES ' . $this->objConfigArray['encoding'] . ';');
			}
		}
		
		protected function RunQueryOnDb($objSqli , $str)
		{
			$objSqli->query($str);
			if ($objSqli->error)
				throw new QMySqliDatabaseException($objSqli->error, $objSqli->errno, $str);
		}
		
		protected function ConnectToMaster()
		{
			$this->objMysqliMaster = new mysqli($this->master_server, $this->master_username, $this->master_password, $this->master_database, $this->master_port);
			if (!$this->objMysqliMaster)
				throw new QMySqliDatabaseException("Unable to connect to master Database", -1, null);
		
			if ($this->objMysqliMaster->error)
				throw new QMySqliDatabaseException($this->objMysqliMaster->error, $this->objMysqliMaster->errno, null);
				
		
			// Set to AutoCommit
			$this->RunQueryOnDb($this->objMysqliMaster, 'SET AUTOCOMMIT=1;');

			// Set NAMES (if applicable)
			// FIXME : use the encoding for slave server?
			if (array_key_exists('encoding', $this->objConfigArray))
			{
//				$this->NonQuery('SET NAMES ' . $this->objConfigArray['encoding'] . ';');
				$this->RunQueryOnDb( $this->objMysqliMaster, 'SET NAMES ' . $this->objConfigArray['encoding'] . ';');
			}
		}
		
		public function Close() {
			if ($this->objMysqliMaster)
				$this->objMysqliMaster->Close();
		
			parent::Close();
			
		}
		
		public function InsertId($strTableName = null, $strColumnName = null) {
			if ($this->IsInMasterSlaveMode())
				return $this->objMysqliMaster->insert_id;
			else
				return $this->objMySqli->insert_id;
		}

		
		
			public function __get($strName) {
				
				switch ($strName)
				{
					case 'master_server':
					case 'master_port':
					case 'master_database':
					case 'master_username':
					case 'master_password':
						return $this->objConfigArray[$strName];
						
					case 'AffectedRows':
						if ($this->IsInMasterSlaveMode())
							return $this->objMysqliMaster->affected_rows;
						else
							return $this->objMySqli->affected_rows;
						
					default:
						try {
							return parent::__get($strName);
						} catch (QCallerException $objExc) {
							$objExc->IncrementOffset();
							throw $objExc;
						}
				}
			}
		
	}