<?php

require_once 'Conexao.class.php';

/**
 * @author Eticons
 */
class DAOGenerico {

    private $conexao = NULL;

    function __construct() {
        $this->conexao = new Conexao();
    }

    public function save($object) {
        try {
            $reflect = new ReflectionClass($object);
            $table = $this->getAnnotationTableName($reflect);
            $insert = "INSERT INTO $table (";
            $values = "";
            foreach ($reflect->getProperties(ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_PROTECTED) as $filed) {
                $filed->setAccessible(true);
                $value = $filed->getValue($object);
                if (!is_null($value) && $value != "") {
                    $insert .= $this->getAnnotationColumnName($filed) . ",";
                    if (is_string($value) || substr_count($value, "/") > 0) {//se é string ou data
                        $values .= "'" . $value . "',";
                    } else {
                        $values .= $value . ",";
                    }
                }
            }
            $insert.=") VALUES (";
            $insert .= $values . ")";
            $insert = str_replace(",)", ")", $insert);
            //echo $insert;
            $result = $this->executeUpdate($insert);
            return $result;
        } catch (Exception $ex) {
            die($ex->getMessage());
        }
    }

    public function delete($object) {
        try {
            $reflect = new ReflectionClass($object);
            $table = $this->getAnnotationTableName($reflect);
            $delete = "DELETE FROM $table WHERE ";
            $id = $this->getAnnotationColumnId($reflect);
            $pk = $this->getPK($id, $object);
            $delete .= $pk;
            $con = $this->conexao->getConexao();
            $stmt = $con->exec($delete);
            $this->conexao->desconectar();
            return $stmt;
        } catch (PDOException $ex) {
            return NULL;
        }
    }

    public function update($object) {
        try {
            $reflect = new ReflectionClass($object);
            $table = $this->getAnnotationTableName($reflect);
            $id = $this->getAnnotationColumnId($reflect);
            $pk = $this->getPK($id, $object);
            $fields = array();
            foreach ($reflect->getProperties(ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_PROTECTED) as $field) {
                $field->setAccessible(true);
                $value = $field->getValue($object);
                if (!is_null($value) && $value != "") {
                    $column = $this->getAnnotationColumnName($field);
                    if (!stripos($field->getDocComment(), '@id') && !stripos($field->getDocComment(), '@transient')) {
                        if (is_string($value) || substr_count($value, "/") > 0) {
                            $fields[$column] = $column . " = '" . $value . "'";
                        } else {
                            $fields[$column] = $column . " = " . $value;
                        }
                    }
                }
            }
            $update = "UPDATE $table SET " . implode(", ", array_values($fields)) . " WHERE " . $pk;
            $result = $this->executeUpdate($update);
            return $result;
        } catch (Exception $ex) {
            die($ex->getMessage());
        }
    }

    private function executeUpdate($query) {
        try {
            $con = $this->conexao->getConexao();
            $stmt = $con->prepare($query);
            $stmt->execute();
            $retorno = $con->lastInsertId();
            $this->conexao->desconectar();
            return $retorno;
        } catch (PDOException $ex) {
            die($ex->getMessage());
            return NULL;
        }
    }

    public function find($entityClass, $primaryKey) {
        try {
            $reflect = new ReflectionClass($entityClass);
            $table = $this->getAnnotationTableName($reflect);
            $id = $this->getAnnotationColumnId($reflect);
            $pk = $this->getPK($id, $reflect) . $primaryKey;
            $campos = array();
            foreach ($reflect->getProperties() as $i => $field) {
                if (!stripos($field->getDocComment(), '@transient')) {
                    $campos[$i] = $this->getFieldName($field);
                }
            }
            $select = "SELECT " . implode($campos, ",") . " FROM $table WHERE " . $pk;
            $con = $this->conexao->getConexao();
            $stmt = $con->query($select);
            $this->conexao->desconectar();
            if (!$result = $stmt->fetchObject(get_class($entityClass))) {
                return new $entityClass;
            } else {
                return $result;
            }
        } catch (Exception $ex) {
            die($ex->getMessage());
        }
    }

    public function findAll($entityClass) {
        try {
            $reflectClass = new ReflectionClass($entityClass);
            $table = $this->getAnnotationTableName($reflectClass);
            $campos = array();
            foreach ($reflectClass->getProperties() as $i => $field) {
                $campos[$i] = $this->getFieldName($field);
            }
            $select = "SELECT " . implode($campos, ",") . " FROM " . $table;
            $con = $this->conexao->getConexao();
            $stmt = $con->query($select);
            $this->conexao->desconectar();
            $i = 0;
            $lista = array();
            if ($stmt) {
                while ($row = $stmt->fetchObject(get_class($entityClass))) {
                    $lista[$i] = $row;
                    $i++;
                }
                return $lista;
            } else {
                return NULL;
            }
        } catch (Exception $ex) {
            die($ex->getMessage());
        }
    }

    public function buscaGenerica($tipo, $valor, $objeto) {
        try {
            $reflect = new ReflectionClass($objeto);
            $table = $this->getAnnotationTableName($reflect);
            $con = $this->conexao->getConexao();
            $stmt = $con->query("SELECT * FROM $table WHERE $tipo LIKE '%$valor%' ORDER BY $tipo");
            $this->conexao->desconectar();
            $i = 0;
            $lista = array();
            if ($stmt) {
                while ($row = $stmt->fetchObject(get_class($objeto))) {
                    $lista[$i] = $row;
                    $i++;
                }
                return $lista;
            } else {
                return NULL;
            }
        } catch (PDOException $ex) {
            die($ex->getMessage());
        }
    }

    public function executeNativeQuery($query, $returnClass) {
        try {
            $con = $this->conexao->getConexao();
            $stmt = $con->query($query);
            $this->conexao->desconectar();
            $i = 0;
            $lista = array();
            if ($stmt) {
                while ($row = $stmt->fetchObject(get_class($returnClass))) {
                    $lista[$i] = $row;
                    $i++;
                }
                return $lista;
            } else {
                return array();
            }
        } catch (PDOException $ex) {
            die($ex->getMessage());
        }
    }

    public function getAnnotationColumnId($reflectClass) {
        $ids = array();
        foreach ($reflectClass->getProperties(ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_PROTECTED) as $i => $field) {
            if (stripos($field->getDocComment(), '@id')) {
                $ids[$i] = $field;
            }
        }
        return (count($ids) > 1 ? $ids : $ids[0]); //verifica se existe mais de um id
    }

    private function getAnnotationColumnName($field) {
        $annotations = $field->getDocComment();
        return $this->getNameColumn($annotations, '@column');
    }

    private function getAnnotationTableName($reflectObject) {
        $annotations = $reflectObject->getDocComment();
        return $this->getNameColumn($annotations, '@table');
    }

    private function getNameColumn($annotations, $annotationType) {
        if (stripos($annotations, $annotationType)) {//verifica se existe a annotation
            if (stripos($annotations, ',')) {
                $carac = ',';
            } else {
                $carac = ')';
            }// verifica se contem type
            $length = (stripos($annotations, $carac) - stripos($annotations, 'name=') - 5); //tamanho do valor da propriedade
            $name = (substr($annotations, stripos($annotations, 'name=') + 5, $length)); //o valor da propriedade
            return $name;
        }
        return NULL;
    }

    private function getTypeColumn($annotations) {
        if (stripos($annotations, 'type=')) {//verifica se existe a annotation
            $length = (stripos($annotations, ')') - stripos($annotations, 'type=') - 5); //tamanho do valor da propriedade
            $type = (substr($annotations, stripos($annotations, 'type=') + 5, $length));
            return $type;
        }
        return NULL;
    }

    private function getPK($id, $object) {
        $pk = "";
        if (is_array($id)) {
            foreach ($id as $i) {
                $i->setAccessible(true);
                $value = $i->getValue($object);
                if (!is_null($value)) {
                    if ($pk != "") {
                        $pk .= ' AND ' . $i->getName() . ' = ' . $value;
                    } else {
                        $pk = $i->getName() . ' = ' . $value;
                    }
                }
            }
        } else {
            $id->setAccessible(true);
            $pk = $id->getName() . ' = ' . $id->getValue($object);
        }
        return $pk;
    }

    public function getFieldName($field) {
        $tipo = $this->getTypeColumn($field->getDocComment());
        $campo = $field->getName();
        if ($tipo == 'dateTime') {
            $campo = "DATE_FORMAT(" . $field->getName() . ", '%d/%m/%Y %H:%i:%s') AS " . $field->getName();
        } else if ($tipo == 'date') {
            $campo = "DATE_FORMAT(" . $field->getName() . ", '%d/%m/%Y') AS " . $field->getName();
        }
        return $campo;
    }

}

?>