<?php

include_once("server/config/config.php");

class dbObject
{

    public $tableName;
    public $metaData;
    public $connection;
    public $recurrentAttribute;
    public $recursiveKey;
    public $debugLevel;
    public $auto;

    public function __construct($tableName = null, $dao = null)
    {
        // Designates specific connector according to DBENGINE specification
        // Loads metadata from designated TABLENAME.
        $this->metaData = null;
        $this->tableName = $tableName;
        $this->getConnection(null, null, null, null);
        $this->auto = array();
        return $this->connection;
    }

    function setId($idArray)
    {
        if (is_array($idArray) && count($idArray) > 0) {
            foreach ($idArray as $attributeName => $value) {
                $this->$attributeName = $value;
            }
        }
    }

    function close()
    {
        $this->connection->close();
    }

    function getAffectedRows()
    {
        return $this->connection->getAffectedRows();
    }

    function getConnection($host = null, $user = null, $pass = null, $db = null)
    {

        switch (DBENGINE) {
            case 'MYSQL':
                $this->connection = new MySqlConnector($host, $user, $pass, $db);
                //$this->metaData = (!is_null($this->tableName)) ? $this->getMetaData() : null;
                break;

            default:
                $this->connection = false;
                break;
        }
    }

    function query($query)
    {
        return $this->connection->queryDB($query);
    }

    function fetch($result)
    {
        return $this->connection->fetchOne($result);
    }

    function execute($q)
    {
        return $this->connection->executeQuery($q);
    }

    function fetchQuery($q)
    {

        $fetchedRows = array();
        $queryResult = $this->query($q);
        if ($queryResult->num_rows > 0) {
            $row = $this->fetch($queryResult);
            while (!is_null($row)) {
                if (!($row === false)) $fetchedRows[] = $row;
                $row = $this->fetch($queryResult);
            }
        }
        return $fetchedRows;
    }

    function fetchAll($result)
    {
        $fetchAll = false;
        while ($fetch = $this->fetch($result)) {
            $fetchAll[] = $fetch;
        }
        return $fetchAll;
    }

    function getRecordCount($result)
    {
        return $this->connection->getRecordCount($result);
    }

    function openTransaction()
    {
        return $this->connection->openTransaction();
    }

    function commit()
    {
        $this->connection->commit();
    }

    function rollback()
    {
        $this->connection->rollback();
    }

    function store($debug = false)
    {
        // Stores object into database, no matter wether is necessary to INSERT or
        // update, the function makes the decission.
        $identCond = $this->getIdentCondition();
        $mustInsert = true;
        //echo $identCond;
        if ($result = $this->query(sprintf("select * from %s where %s", $this->tableName, $identCond))) {
            $mustInsert = ($this->getRecordCount($result) == 0);
            //echo "ON DB: ".$this->getRecordCount($result);
        }
        $store = $mustInsert ? $this->insert($debug) : $this->update($debug);
        return $store;
    }

    function retrieve($debug = false)
    {
        // Loads object attributes from those on database (uses primary key to locate).
        $retrieveString = sprintf('select * from %s where %s', $this->tableName, $this->getIdentCondition());
        if ($debug)
            echo $retrieveString;
        $retrieve = false;
        if ($result = $this->query($retrieveString)) {
            $retrieve = $this->fetch($result);
        }
        if ($retrieve) {
            foreach ($retrieve as $attributeName => $value) {
                $this->$attributeName = $value;
            }
        }
        return $retrieve;
    }

    function getAutoInc()
    {
        return $this->connection->getAutoIncValue();
    }

    function insert($debug = false)
    {
        // inserts a new row into table.
        // Status: Final Check Passed.
        // Author: Am�rico Patetta.
        // Last revised: jun-6-06
        $insert = false;
        foreach ($this->metaData['types'] as $fieldName => $fieldType) {
            if (isset($this->$fieldName) && (!isset($this->metaData['auto'][$fieldName]) || !$this->metaData['auto'][$fieldName])) {
                $attsArray[] = $fieldName;
                $valsArray[] = $this->$fieldName;
            } elseif (!isset($this->$fieldName) && (!isset($this->metaData['auto'][$fieldName]) || !$this->metaData['auto'][$fieldName])) {
                $attsArray[] = $fieldName;
                $valsArray[] = null;
            }
        }
        if ($queryString = $this->makeInsertQuery($attsArray, $valsArray)) {
            if ($debug)
                echo $queryString;
            $insert = $this->query($queryString);
            if (is_array($this->metaData['auto']) && count($this->metaData['auto'])) {
                foreach ($this->metaData['auto'] as $autoIncFieldName => $value) {
                    $this->$autoIncFieldName = $this->getAutoInc();
                }
            }
        }
        return $insert;
    }

    function makeInsertQuery($attsArray, $valsArray)
    {
        // Constructs SQL INSERT query according to passed attributes and values.
        // Status: Final Check Passed
        // Author: Am�rico Patetta.
        // Last revised: jun-6-06	

        $atts = $this->sqlizeAtts($attsArray);
        $vals = $this->sqlizeVals($attsArray, $valsArray);
        $insertQuery = sprintf("insert into %s %s values %s", $this->tableName, $atts, $vals);
        //echo $insertQuery;
        return $insertQuery;
    }

    function sqlizeAtts($tableAttArray)
    {
        // Formats a set of attributes as for an INSERT query.
        // Status: Final Check Passed
        // Author: Am�rico Patetta.
        // Last revised: jun-6-06	
        $sqlizeAtts = false;
        if (is_array($tableAttArray) && (count($tableAttArray) > 0)) {
            $sqlizeAtts = '';
            for ($i = 0; $i < sizeof($tableAttArray); $i++) {
                $thisAttribute = $tableAttArray[$i];
                $sqlizeAtts .= strlen($sqlizeAtts) != 0 ? ', ' : '';
                $sqlizeAtts .= $thisAttribute;
            }
            $sqlizeAtts = '(' . $sqlizeAtts . ')';
        }
        return $sqlizeAtts;
    }

    function sqlizeVals($attsArray, $valsArray)
    {
        // Formats a set of attribute valuse as for an INSERT query.
        // Status: Final Check Passed
        // Author: Am�rico Patetta.
        // Last revised: jun-6-06	
        $i = 0;
        $sqlizedVals = '';
        foreach ($attsArray as $attribute) {
            $metaData = $this->getMetaData();
            if ($abstracType = $metaData['types'][$attribute]) {
                $thisVal = $this->sqlizeVal($valsArray[$i], $abstracType);
            }
            $i++;
            $sqlizedVals .= ($sqlizedVals != '') ? ', ' : '';
            $sqlizedVals .= $thisVal;
        }
        return '(' . $sqlizedVals . ')';
    }

    function sqlizeVal($value, $abstracType)
    {
        // Formats an attributes according to "abstract SQL" type.
        // Status: Final Check Passed
        // Author: Am�rico Patetta.
        // Last revised: jun-6-06
        switch ($abstracType) {
            case 'NUM':
                $sqlizedValue = (is_null($value)) ? 'null' : (string) $value;
                break;
            default:
                $sqlizedValue = (is_null($value)) ? 'null' : "'" . $value . "'";
                break;
        }
        return $sqlizedValue;
    }

    function getMetaData($forceReload = false)
    {
        // Gets table metadata (field types & primary key).
        // Status: Final Check Passed
        // Author: Am�rico Patetta.
        // Last revised: jun-6-06
        if ((!isset($this->metaData) || is_null($this->metaData)) || $forceReload) {
            // We'll have to get the metadata 
            $this->metaData = $this->connection->getMetaData($this->tableName);
        }
        return $this->metaData;
    }

    function update($debug = false)
    {
        $updateList = '';
        $metaData = $this->getMetaData();
        foreach ($metaData['types'] as $fieldName => $fieldType) {
            //echo $fieldName.'>'.$fieldType.'<br>';
            if (!isset($this->$fieldName)) {
                $this->$fieldName = null;
            }
            $updateList .= ($updateList == '') ? '' : ', ';
            $updateList .= $fieldName . '=' . $this->sqlizeVal($this->$fieldName, $fieldType);
        }
        $condition = $this->getIdentCondition();
        $updateString = sprintf('update %s SET %s WHERE %s', $this->tableName, $updateList, $condition);
        if ($debug)
            echo $updateString;
        return $this->query($updateString);
    }

    function getIdentCondition()
    {
        $metaData = $this->getMetaData();
        $identCondition = false;
        if (is_array($metaData['keys']) && count($metaData['keys']) != 0) {
            $identCondition = '';
            foreach ($metaData['keys'] as $fieldName => $bogus) {
                $identCondition .= ($identCondition != '') ? ' and ' : '';
                $identCondition .= $fieldName . '=' . $this->sqlizeVal($this->$fieldName, $metaData['types'][$fieldName]);
            }
        }
        return $identCondition;
    }

    function kill()
    {
        $delete = $this->query(sprintf('delete from %s where %s', $this->tableName, $this->getIdentCondition()));
        return $delete;
    }

    function getDirectSons($parent)
    {

        $sons = false;
        $d = new dbObject($this->tableName);
        $resultSet = $d->query(sprintf('SELECT %s FROM %s WHERE %s ="%s"', $this->recursiveKey, $this->tableName, $this->recurrentAttribute, $parent));
        while ($row = $d->fetch($resultSet)) {
            $sons[] = $row[$this->recursiveKey];
        }
        return $sons;
    }

    function getSons($parent)
    {

        $directSons = $this->getDirectSons($parent);
        if (is_array($directSons)) {
            for ($i = 0; $i < count($directSons); $i++) {
                $grandSons = $this->getDirectSons($directSons[$i]);
                if (is_array($grandSons)) {
                    $directSons = array_merge($directSons, $grandSons);
                }
            }
        }
        return $directSons;
    }

    function getParent($son)
    {

        //die("gp");
        $d = new dbObject($this->tableName);
        $resultSet = $d->query(sprintf('SELECT %s FROM %s WHERE %s="%s"', $this->recurrentAttribute, $this->tableName, $this->recursiveKey, $son));
        $row = $d->fetch($resultSet);
        return $row[$this->recurrentAttribute];
    }

    function makeParentsArray($path)
    {
        //echo"<pre>";print_r($path);
        $parent = $this->getParent($path[count($path) - 1]);
        if (is_null($parent)) {
            return $path;
        } else {
            $path[] = $parent;
            return $this->makeParentsArray($path);
        }
    }

    function getParents($son)
    {
        return $this->makeParentsArray(array($son));
    }
}

/*
  class objectCollection extends dbObject{

  var $criteriaSqlStatement;

  function objectCollection($tableName){
  parent::dbObject($tableName);
  }

  function setCriteria($criteriaArray){
  foreach($criteriaArray as $attName => $attValue){

  }
  }

  function setCriteriaStatement($statement){
  $this->criteriaSqlStatement = $statement;
  }

  function fetchRows(){
  $sqlStatement = sprintf('select * from %s %s',$this->tableName,$this->criteriaSqlStatement);
  echo "<br>".$sqlStatement.'<br>';
  $q = $this->query($sqlStatement);
  while($row = $this->fetch($q)){
  echo "<pre>";
  print_r($row);
  }
  }
  }
 */

class MySqlConnector extends mysqli
{

    var $connection;

    function __construct($host = null, $user = null, $pass = null, $database = null)
    {

        $theHost = is_null($host) ? DB_HOST : $host;
        $theUser = is_null($user) ? DB_USER : $user;
        $thePass = is_null($pass) ? DB_PASS : $pass;
        $theDB = is_null($database) ? DB_NAME : $database;
        parent::__construct($theHost, $theUser, $thePass, $theDB);
        //echo $theHost.'/'.$theUser.'/'.$theDB;
        //$this->connection = new mysqli();
    }

    function getMetaData($tableName)
    {
        // Get table metadata (field names, types & primary key)
        // Status: OK.
        // Last check: 6-jun-06	
        $fields = array();
        $types = array();
        $keys = array();
        $auto = array();
        $sqlQuery = sprintf("select * from %s", $tableName);
        $stmt = $this->prepare($sqlQuery);
        $stmt->execute();
        $result = $stmt->result_metadata();
        for ($i = 0; $i < $this->field_count; $i++) {
            $field = $result->fetch_field();
            $types[$field->name] = $this->abstracTypize($field->type);
            if ($field->flags & MYSQLI_PRI_KEY_FLAG) {
                $keys[$field->name] = true;
            }
            if ($field->flags & MYSQLI_AUTO_INCREMENT_FLAG) {
                $auto[$field->name] = true;
            }
        }
        $result->close();
        $metaData['types'] = $types;
        @$metaData['keys'] = $keys;
        @$metaData['auto'] = $auto;
        return $metaData;
    }

    function getAutoIncValue()
    {
        return $this->insert_id;
    }

    function getAffectedRows()
    {
        $this->affected_rows;
    }

    function openTransaction()
    {
        $ok = false;
        if ($this->queryDB("SET AUTOCOMMIT=0")) {
            $ok = $this->queryDB("START TRANSACTION");
        }
        return $ok;
    }
    /*
    function commit($f = 0, $name = null) {
        return $this->queryDB("commit");
    }

    function rollback($f = 0, $name = null) {
        return $this->queryDB("rollback");
    }
    */
    function abstracTypize($concreteType)
    {
        switch ($concreteType) {
            case 10:
                $abstracType = 'DATE';
                break;
            case 11:
                $abstracType = 'TIME';
                break;
            case 12:
                $abstracType = 'DATETIME';
                break;
            case 252:
                $abstracType = 'BIN';
                break;
            case 253:
            case 254:
                $abstracType = 'STR';
                break;
            default:
                $abstracType = 'NUM';
                break;
        }
        return $abstracType;
    }

    function queryDB($query)
    {
        $result = $this->query($query);
        return $result;
    }

    function executeQuery($query)
    {
        $this->query($query);
        return $this->affected_rows;
    }

    function fetchOne($result)
    {
        $fetchOne = $result->fetch_array(MYSQLI_ASSOC);
        //print_r($fetchOne);
        return $fetchOne;
    }

    function getRecordCount($result)
    {
        $getRecordCount = $result->num_rows;
        return $getRecordCount;
    }
}
