<?php
class DbWrapper {
	private $dbtype = null;
	private $serverName = null;
	private $userName = null;
	private $passWord = null;
	private $dbName = null;
	private $integrator;
	public function __construct(DokuWiki_Syntax_Plugin $integrator, $dbtype, $serverName, $userName, $passWord, $dbName) {
		$this->dbtype = $dbtype;
		$this->serverName = $serverName;
		$this->userName = $userName;
		$this->passWord = $passWord;
		$this->dbName = $dbName;
		$this->integrator = $integrator;
	}
	public function executeQuery($query) {
		if ($query !== '') {
			
			echo <<<EOT
<div style="padding-top: 1em; margin-top: 1em; border-top: 1px solid #888888;">
EOT;
			
			switch ($this->dbtype) {
				case "mysql" :
					$this->_mysql ( $this->serverName, $this->userName, $this->passWord, $this->dbName, $query );
					break;
				case "oracle" :
					$this->_oracle ( $this->serverName, $this->userName, $this->passWord, $this->dbName, $query );
					break;
				case "postgresql" :
					$this->_postgresql ( $this->serverName, $this->userName, $this->passWord, $this->dbName, $query );
					break;
				default :
					break;
			}
		}
		
		echo '</div>';
	}
	private function _oracle($Server, $User, $Pass, $Database, $Query) {
		if (! $connection = oci_connect ( $User, $Pass, $Server ) or false)
			throw new Exception ( oci_error () );
			
			// execute query
		$rs = oci_parse ( $connection, $Query );
		oci_execute ( $rs );
		$dbArray = array ();
		
		if ($rs === true)
			$dbArray [] = array (
					$this->aString ["affected"] => oci_affected_rows ( $connection ) 
			);
		else
			while ( $row = oci_fetch_assoc ( $rs ) )
				$dbArray [] = $row;
		
		oci_free_statement ( $rs );
		oci_close ( $connection );
		return $dbArray;
	}
	private function _mysql($serverName, $userName, $passWord, $dbName, $query) {
		try {
			
			$db = new mysqli ( $serverName, $userName, $passWord, $dbName );
			mysqli_autocommit ( $db, TRUE );
			mysqli_set_charset ( $db, 'utf8' ); // muss auf UTF8 umgesetzt werden
			mysqli_select_db ( $db, $dbName ); // DB-Auswahl
			
			if (! $db) {
				echo '<div class="error">' . $this->getLang ( 'errordbconnection' ) . '</div>';
			}
			if ($db->multi_query ( $query )) {
				echo '<u>' . $this->getLang ( 'firstresult' ) . '</u><br>';
				do {
					$result = $db->store_result ();
					if ($result instanceof mysqli_result) {
						try {
							$rows = $this->mysql_fetch_all ( $result );
							if (sizeof ( $rows ) <= 0) {
								echo $this->getLang ( 'emptyresult' );
							}
							$cols = empty ( $first ) ? array () : array_keys ( $first );
							
							echo $this->__renderTable ( $cols, $rows );
						} catch ( Exception $e ) {
							echo '<div class="error">' . $e->getMessage () . '</div>';
						} finally {
							$result->free ();
						}
					} else if (is_bool ( $result )) {
						$err = mysqli_errno ( $db );
						echo $this->getLang ( 'queryexecuted' ) . ($err != 0 ? $this->getLang ( 'no' ) : $this->getLang ( 'yes' ));
					} else
						var_dump ( $result );
					
					if ($db->more_results ()) {
						echo '<br><u>' . $this->getLang ( 'nextresult' ) . '</u><br>';
					}
				} while ( $db->next_result () );
			} else {
				echo $this->getLang ( 'queriesnotexecuted' );
			}
		} catch ( Exception $e ) {
			echo '<div class="error">' . $e->getMessage () . '</div>';
		} finally {
			if ($db) {
				$db->close ();
			}
		}
	}
	private function _postgresql($serverName, $userName, $passWord, $dbName, $query) {
		$hostAndPort [] = explode ( ":", $serverName );
		
		if (count ( $hostAndPort [0] ) == 2)
			$url = "host=" . $hostAndPort [0] [0] . " port=" . $hostAndPort [0] [1] . " dbname=" . $dbName . " user=" . $userName . " password=" . $passWord;
		else
			$url = "host=" . $serverName . " dbname=" . $dbName . " user=" . $userName . " password=" . $passWord;
			
			// $rs = pg_exec ( $query );
			// $dbArray = pg_fetch_array ( $result, NULL, PGSQL_ASSOC );
			
		// pg_close ( $connection );
		try {
			
			$db = pg_connect ( $url );
			
			pg_setclientencoding ( $db, 'utf8' );
			// mysqli_autocommit ( $db, TRUE );
			
			if (! $db) {
				echo '<div class="error">' . $this->getLang ( 'errordbconnection' ) . '</div>';
			}
			$result = pg_query ( $db, $query );
			if ($result) {
				echo '<u>' . $this->getLang ( 'firstresult' ) . '</u><br>';
				// if ($result instanceof mysqli_result) {
				
				$rows = pg_fetch_all ( $result );
				
				if ($rows == false || sizeof ( $rows ) <= 0) {
					echo $this->getLang ( 'emptyresult' );
				} else {
					$cols = empty ( $first ) ? array () : array_keys ( $first );
					
					echo $this->__renderTable ( $cols, $rows );
				}
				
				// } else if (is_bool ( $result )) {
				// $err = mysqli_errno ( $db );
				// echo $this->getLang ( 'queryexecuted' ) . ($err != 0 ? $this->getLang ( 'no' ) : $this->getLang ( 'yes' ));
				// } else
				// var_dump ( $result );
				
				// if ($db->more_results ()) {
				// echo '<br><u>' . $this->getLang ( 'nextresult' ) . '</u><br>';
				// }
			} else {
				echo $this->getLang ( 'queriesnotexecuted' );
			}
		} catch ( Exception $e ) {
			echo '<div class="error">' . $e->getMessage () . '</div>';
		} finally {
			if ($db) {
				pg_close ( $db );
			}
		}
	}
	private function mysql_fetch_all($result) {
		if (method_exists ( 'mysqli_result', 'fetch_all' )) // Compatibility layer with PHP < 5.3
			$res = mysqli_fetch_all ( $result, MYSQLI_ASSOC );
		else
			for($res = array (); $tmp = mysqli_fetch_array ( $result, MYSQLI_ASSOC );)
				$res [] = $tmp;
		
		return $res;
	}
	private function pg_fetch_all($result) {
		for($res = array (); $tmp = pg_fetch_array ( $result, NULL, PGSQL_ASSOC );)
			$res [] = $tmp;
		
		return $res;
	}
	private function __renderTable($cols, $rows) {
		
		/*
		 * - collect header row according to listed rows
		 * - transform all listed rows to properly defined HTML table cells
		 */
		$headers = array ();
		$meta = array ();
		$counter = 0;
		
		foreach ( $rows as $nr => $row ) {
			
			// convert all values in current row to table cells
			$i = 0;
			
			foreach ( $row as $column => $value ) {
				
				$def = $meta [$column];
				
				$headers [$column] = $def ['label'] ? $def ['label'] : $column;
				$class = $def ['format'] ? $def ['format'] : 'na';
				$class .= ' col' . ++ $i;
				
				$value = $this->valueFromDB ( $column, $value, $def );
				
				$cell = $this->renderValue ( $column, $value, $def, false, false );
				
				$row [$column] = "<td class=\"$class\">" . $cell . "</td>\n";
			}
			
			// prepend cell for counter
			array_unshift ( $row, '<td class="counter col0 rightalign">' . ++ $counter . "</td>\n" );
			
			// convert set of values into HTML table row
			$classes = array ();
			
			if ($nr == 0)
				$classes [] = 'first';
			if ($nr == count ( $rows ) - 1)
				$classes [] = 'last';
			
			$classes [] = ($nr % 2) ? 'even' : 'odd';
			$classes [] = 'row' . ($nr + 1);
			
			$classes = implode ( ' ', $classes );
			
			$rows [$nr] = '<tr class="' . $classes . '">' . implode ( '', $row ) . "</tr>\n";
		}
		
		// finally convert all HTML table rows into single HTML table body
		$rows = implode ( '', $rows );
		
		/*
		 * compile header row
		 */
		
		// ensure to have row of headers (missing on an empty list of rows)
		if (empty ( $headers ))
			foreach ( $cols as $column ) {
				
				unset ( $def );
				
				if (is_array ( $meta [$column] ))
					$def = $meta [$column];
				else {
					// missing meta information on current "column name"
					// --> might be an alias definition
					// --> extract originally selected column name from that
					
					$pos = strripos ( $column, ' AS ' );
					if ($pos !== false) {
						
						$temp = substr ( $column, $pos + 4 );
						if ($meta [$temp]) {
							// found definition on extracted column name
							
							$def = $meta [$temp];
							$column = $temp;
						}
					}
				}
				
				$headers [$column] = $def ['label'] ? $def ['label'] : $column;
			}
		
		foreach ( $headers as $column => $label ) {
			
			$headers [$column] = "<th class=\"label\">$label</th>\n";
		}
		
		// compile row of header cells
		$headers = implode ( '', $headers );
		
		/*
		 * render list of rows as HTML table
		 */
		
		$width = intval ( $this->options ['width'] ) ? ' width="' . $this->options ['width'] . '"' : '';
		
		$trClass = $this->options ['wikistyle'] ? '' : ' class="data-list"';
		$tableClass = $this->options ['wikistyle'] ? ' class="inline"' : '';
		
		$table = <<<EOT
<div style="overflow-x: scroll;">
   <table width="100%"$tableClass>
    <thead>
     <tr class="row0">
      <th class="counter"></th>
      $headers
     </tr>
    </thead>
    <tbody>
     $rows
    </tbody>
   </table>
</div>
EOT;
		
		return $table;
	}
	protected function valueFromDB($column, $value, $def) {
		switch ($def ['format']) {
			
			case 'image' :
			case 'file' :
				if (is_null ( $value ))
					return null;
				
				if ($value === '||')
					return null;
					
					// parse file for internally used structure
				$a = strpos ( $value, '|' );
				if (! $a)
					// externally provided file --> don't touch
					return (strlen ( $value ) > 0);
				
				$b = strpos ( $value, '|', $a + 1 );
				if (! $b)
					// externally provided file --> don't touch
					return true;
				
				$temp = array (
						'mime' => substr ( $value, 0, $a ),
						'name' => substr ( $value, $a + 1, $b - $a - 1 ),
						'file' => substr ( $value, $b + 1 ) 
				);
				
				if (! preg_match ( '#^[a-z0-9-]+/[+a-z0-9-]+$#i', $temp ['mime'] ))
					// externally provided file --> don't touch
					return true;
				
				if (trim ( $temp ['name'] ) === '')
					// externally provided file --> don't touch
					return true;
				
				return $temp;
			
			case 'date' :
				if ($def ['options'] ['unixts'])
					return $value;
				
				if ((trim ( $value ) === '') || ($value == '0000-00-00'))
					return 0;
				
				return self::parseDBDateTime ( $value, true );
			
			case 'time' :
				return $value;
			
			case 'datetime' :
				if ($def ['options'] ['unixts'])
					return $value;
				
				$value = substr ( $value, 0, 19 );
				if ((trim ( $value ) === '') || ($value == '0000-00-00T00:00:00') || ($value == '0000-00-00 00:00:00'))
					return 0;
				
				return self::parseDBDateTime ( $value, false );
			
			case 'bool' :
				$value = trim ( $value );
				switch ($def ['options'] ['booltype']) {
					
					case 'int' :
						return (intval ( $value ) != 0);
					
					case 'xmark' :
						return (strtolower ( $value [0] ) == 'x');
					
					case 'yesno' :
					default :
						return (strtolower ( $value [0] ) == 'y');
				}
			
			case 'enum' :
				$value = trim ( $value );
				$value = array_search ( $value, $def ['options'] ['selectables'] );
				if ($value === false)
					$value = null;
				else
					$value = intval ( $value );
				
				break;
			
			case 'related' :
				if (is_numeric ( $value ))
					$value = intval ( $value );
				
				break;
			
			case 'monetary' :
			case 'real' :
			/**
			 *
			 * @todo manage decimal point conversions
			 */
			
			case 'url' :
			case 'email' :
			case 'phone' :
			case 'fax' :
			case 'text' :
			case 'integer' :
			// keep value as is ...
		}
		
		return $value;
	}
	protected function renderValue($column, $value, $def, $mayBeSkipped = false, $inEditor = false) {
		if (is_null ( $value ) && ($def ['type'] != 'data'))
			return $mayBeSkipped ? null : '';
		
		switch ($def ['format']) {
			
			case 'text' :
				return trim ( $value );
			
			case 'image' :
			case 'file' :
				if ($value === true)
					return '<span class="info">' . $this->getLang ( 'fileexternalfound' ) . '</span>';
				else if ($value === false)
					return '<span class="info">' . $this->getLang ( 'fileexternalnotfound' ) . '</span>';
				else {
					
					$temp = is_array ( $value ) ? trim ( implode ( '', $value ) ) : '';
					
					if (! $inEditor)
						$url .= '&thumb=150';
					
					if ($def ['format'] === 'image')
						return "<img src=\"$url\" alt=\"" . sprintf ( $this->getLang ( 'fileimagealt' ), $column, $value ['mime'] ) . "\" />";
					
					return "<a href=\"$url\" title=\"" . $this->getLang ( 'filedlhint' ) . '">' . $this->getLang ( 'cmddl' ) . '</a>';
				}
				break;
			
			case 'email' :
				if ((trim ( $value ) === '') && $mayBeSkipped)
					return null;
				
				return DokuWiki_Plugin::email ( $value, $email );
			
			case 'url' :
				if ((trim ( $value ) === '') && $mayBeSkipped)
					return null;
				
				return DokuWiki_Plugin::external_link ( $value );
			
			case 'phone' :
			case 'fax' :
				if ((trim ( $value ) === ''))
					return null;
				
				return $value;
			
			case 'bool' :
				if (! $value)
					return null;
				return $value ? '&#10007;' : '&ndash;';
			
			case 'date' :
				if (! $value)
					return $mayBeSkipped ? null : '';
				
				return strftime ( strtok ( trim ( $this->getConf ( 'dformat' ) ), ' ' ), $value );
			
			case 'datetime' :
				if (! $value)
					return $mayBeSkipped ? null : '';
				
				return strftime ( $this->getConf ( 'dformat' ), $value );
			
			case 'time' :
				if ($value && (substr ( $value, - 3 ) == ':00') && (strlen ( $value ) > 5))
					$value = substr ( $value, 0, - 3 );
				
				return $value;
			
			case 'integer' :
			case 'monetary' :
			case 'real' :
				if (! $value && $mayBeSkipped)
					return null;
				
				return $value;
			
			case 'enum' :
			case 'related' :
				if (is_integer ( $value ))
					$value = $def ['options'] ['selectables'] [$value];
				
				return $value;
			
			default :
				return $value;
		}
	}
	
	/**
	 * Retrieves localized string.
	 *
	 * @param string $name
	 *        	name of localized string
	 * @return mixed retrieved localized string
	 */
	public function getLang($name) {
		return $this->integrator->getLang ( $name );
	}
}
?>