<?php

use system\entity\filter\baseFilter;

class ModelBase extends Model
{

    /**
     * @var Entity
     */
    private $entityClass;

    /**
     * @var Entity
     */
    private $_entityClass;

    /**
     * @var array
     */
    private $_entityArray;

    /**
     * @var Entity BaseFilter
     */
    public $baseFilter;

    public function __construct($registry, $entityClass)
    {
        parent::__construct($registry);

        $this->load->helper('Ftr_sql');

        $this->entityClass = $entityClass;

        $this->_entityClass = new $entityClass();

        $this->baseFilter = new BaseFilter();
    }

    /**
     * @return new Entity
     */
    public function getNewEntityClass()
    {
        $this->_entityClass = new $this->entityClass();
    }

    /**
     * @return Entity
     */
    public function getEntityClass()
    {
        return $this->_entityClass;
    }

    /**
     * pega todas as informações da entity que são necessarias para fazer update/insert nas tabelas
     */
    public function setEntityArray()
    {
        $this->_entityArray = $this->_entityClass->__toArray();
    }

    /**
     * Seta o valor encontrado na entity desde que encontre o methodName para esse valor
     *
     * @param $name
     * @param $value
     * @throws exception
     */
    public function setClassValues($name, $value)
    {
        $methodName = 'set' . str_replace('_', '', $name);

        if (method_exists($this->_entityClass, $methodName)) {
            call_user_func(array($this->_entityClass, $methodName), $value);
        } else {
            throw new exception('Metodo set não encontrado ' . $methodName . ' para a classe ' . $this->_entityName);
        }
    }

    /**
     * @param $name
     * @throws exception
     */
    public function getClassValues($name)
    {
        $methodName = 'get' . str_replace('_', '', $name);

        if (method_exists($this->_entityClass, $methodName)) {
            call_user_func(array($this->_entityClass, $methodName));
        } else {
            throw new exception('Metodo get não encontrado ' . $methodName . ' para a classe ' . $this->_entityName);
        }
    }

    /**
     * Retorna todos os valores setando na entity
     *
     * @return array de entity.
     */
    public function findAll()
    {
        return $this->findBy();
    }

    /**
     * Retorna os valores baseados nos filtros
     *
     * @param array|null $filter
     * @param array|null $orderBy
     * @param int|null $limit
     * @param int|null $offset
     *
     * @return array de entity.
     */
    public function findBy(BaseFilter $filter = null, array $orderBy = null, $limit = null, $offset = null)
    {
        $sqlSelect = Ftr_sql::generateSelect($this->_entityClass::tableName, $filter, $orderBy, $limit, $offset);
        $results = $this->db->query($sqlSelect);

        if ($results->rows) {

            $arrayClass = array();

            foreach ($results->rows as $results) {
                $this->getNewEntityClass();
                foreach ($results as $name => $value) {
                    $this->setClassValues($name, $value);
                }
                $arrayClass[] = $this->_entityClass;
            }

            return $arrayClass;
        }

        return false;
    }

    /**
     * @param baseFilter|null $filter
     * @param array|null $orderBy
     * @param null $limit
     * @param null $offset
     * @return bool|Entity
     * @throws exception
     */
    public function findByOne(BaseFilter $filter = null, array $orderBy = null, $limit = null, $offset = null)
    {
        $sqlSelect = Ftr_sql::generateSelect($this->_entityClass::tableName, $filter, $orderBy, $limit, $offset);
        $results = $this->db->query($sqlSelect);

        if ($results->rows) {

            foreach ($results->rows as $results) {
                $this->getNewEntityClass();
                foreach ($results as $name => $value) {
                    $this->setClassValues($name, $value);
                }
            }

            return $this->_entityClass;
        }

        return false;
    }

    /**
     * @param $id
     * @return array|bool
     * @throws exception
     */
    public function find($id)
    {
        $this->setEntityArray();
        $this->baseFilter->setWhere($this->_entityArray['entityIndex'], Type_OperationsComparation::Equals, $id);

        $sqlSelect = Ftr_sql::generateSelect($this->_entityClass::tableName, $this->baseFilter);
        $results = $this->db->query($sqlSelect);

        if ($results->rows) {
            foreach ($results->rows as $results) {
                $this->getNewEntityClass();
                foreach ($results as $name => $value) {
                    $this->setClassValues($name, $value);
                }
            }
            return $this->_entityClass;
        }

        return false;

    }

    /**
     * @return Entity
     * @throws exception
     */
    public function insert()
    {
        $this->setEntityArray();

        //monta a sql de insert
        $sqlInsert = Ftr_sql::generateInsert(
            $this->_entityClass::tableName,
            $this->_entityArray['entityFields']
        );

        $this->db->executeSql($sqlInsert, $this->_entityArray['entityValues']);

        return $this->find($this->db->getLastId());
    }

    /**
     * @return Entity
     * @throws exception
     */
    public function update()
    {
        $this->setEntityArray();

        //monta a sql de update
        $sqlUpdate = Ftr_sql::generateUpdate(
            $this->_entityClass::tableName,
            $this->_entityArray['entityFields'],
            $this->_entityArray['entityIndex'] . ' = ' . $this->_entityClass->getTableId()
        );

        $this->db->executeSql($sqlUpdate, $this->_entityArray['entityValues']);

        return $this->find($this->_entityClass->getTableId());

    }

    /**
     * @param baseFilter|null $filter
     * @return mixed
     */
    public function delete(BaseFilter $filter = null)
    {
        return $this->db->query(Ftr_sql::generateDelete($this->_entityClass::tableName, $filter));
    }

//ajustar para fazer save dinamico com classes pai e filho
//    public function save()
//    {
//
//       $oReflectionClass = new ReflectionClass('system\entity\marketplace\marketplacePrice');
//       $teste = new ReflectionMethod(MarketplacePrice::class, 'setMarketplaceAdjustment');
//
//        foreach ($teste->getParameters() as $i => $param) {
//            echo $param->getType()->getName();
//        }
//        exit;
//    }

}