<?php

namespace OfficeBrain\Bundle\CoreBundle\Decorator;

use Doctrine\ORM\EntityManager as BaseEntityManager;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\ORMException;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use OfficeBrain\Bundle\ActivityBundle\Listener\PostEventListener;
use Doctrine\ORM\Events;

class OwnCustomEntityManager extends BaseEntityManager
{
    public $objQueryBuilder;
    public $moduleParameters;
    
    public $persistEntity;
    
    public function __construct(Connection $conn, Configuration $config, EventManager $eventManager)
    {
        parent::__construct($conn,$config,$eventManager);
        $this->moduleParameters['entities']=array('Doctrine\ORM\Query\AST\DeleteStatement','Doctrine\ORM\Query\AST\UpdateStatement');
        $this->persistEntity=null;
    }
    
    public static function create($conn, Configuration $config, EventManager $eventManager = null) {
        if (!$config->getMetadataDriverImpl()) {
            throw ORMException::missingMappingDriverImpl();
        }

        if (is_array($conn)) {
            $conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ? : new EventManager()));
        } else if ($conn instanceof Connection) {
            if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
                throw ORMException::mismatchedEventManager();
            }
        } else {
            throw new \InvalidArgumentException("Invalid argument: " . $conn);
        }

        // This is where you return an instance of your custom class!
        return new self($conn, $config, $conn->getEventManager());
    }
    
    
    public function createQueryBuilder()
    {
        //print_r(get_class_methods(get_class(parent::createQueryBuilder())));
        $this->objQueryBuilder=parent::createQueryBuilder();
        return $this->objQueryBuilder;//->setCacheable(true)->setCacheMode(1);
    }
    
    /*
    public function find($entityName, $id, $lockMode = null, $lockVersion = null)
        {
            $obj=parent::find($entityName, $id, $lockMode, $lockVersion);
            return $obj;
        }*/
    
    
    
    public function flush($entity = NULL)
    {
        parent::flush();
        if(!empty($this->persistEntity))
        {
          foreach ($this->persistEntity as $key => $value) {
            $value=$this->getEntityName($value);
            $this->removeCacheOfFlush($value);
          }
        }
        $this->persistEntity=null;
    }
    
      public function persist($entity)
      {
          parent::persist($entity);
          $className=get_class($entity);
          $this->persistEntity[$className]=$className;
          $metadata = $this->getClassMetadata($className);
          $associationMappings=$metadata->getAssociationMappings();
          if(count($associationMappings)>0)
          {
              foreach($metadata->getAssociationMappings() as $key=>$value)
              {
                  if(isset($value['targetEntity'])){
                      $this->persistEntity[$value['targetEntity']]=$value['targetEntity'];
                  }
              }
          }
      }

    /*
    private function removeCache($entity)
        {
            $cache=$this->getConfiguration()->getResultCacheImpl();
            echo get_class($cache);exit;
        }*/
    
    public function remove($obj)
    {
        parent::remove($obj);
        $this->removeCacheOfFlush(get_class($obj));
    }
    
    public function createQuery($dql='')
    {
        $objQuery=parent::createQuery($dql);
        //echo "======$dql======";
        //print_r(get_class_methods(get_class($objQuery->getEntityManager())));exit;
        //print_r(get_class_methods(get_class($objQuery->getAST())));
        $this->checkDeleteQuery($objQuery);
        
        //echo "==============".$objQuery->getType()."==========";
        list($nameSpace,$cacheId)=$this->createCacheKey($objQuery->getParameters(),$dql);
        
        $objQuery->getQueryCacheDriver()->setNamespace("QueryCache:".$nameSpace.":");
        $objQuery->getResultCacheDriver()->setNamespace("ResultCache:".$nameSpace.":");
        //echo "=============getNamespace===========".$objQuery->getQueryCacheDriver()->getNamespace($nameSpace);
        $objQuery->useQueryCache(true);
        $objQuery->useResultCache(true, 4000,$cacheId);
        return $objQuery;
    }

    private function checkDeleteQuery(&$objQuery)
    {
        $sqlStatement=$objQuery->getAST();
        if(in_array(get_class($sqlStatement), $this->moduleParameters['entities']))
        {
            if(strpos(get_class($sqlStatement),'UpdateStatement')>-1 && strpos(get_class($sqlStatement->updateClause),'UpdateClause')>-1)
            {
                $this->removeCacheOfFlush($sqlStatement->updateClause->abstractSchemaName);
            }
            elseif(strpos(get_class($sqlStatement),'DeleteStatement')>-1 && strpos(get_class($sqlStatement->deleteClause),'DeleteClause')>-1)
            {
                $this->removeCacheOfFlush($sqlStatement->deleteClause->abstractSchemaName);
            }
        }
    }

    private function removeCacheOfFlush($EntityName)
    {
        $EntityName=$this->getEntityName($EntityName);
        $moduleName['entities']=array('OfficeBrain\Bundle\ActivityBundle\Listener\PostEventListener');
        $getListeners=($this->getEventManager()->getListeners(Events::postRemove));
        $objPostListener=null;
        foreach($getListeners as $key=>$objListeners)
        {
            if(in_array(get_class($objListeners), $moduleName['entities']))
            {
                $objPostListener=$objListeners;
                $objPostListener->removeCachebyEntityName("ResultCache:".$EntityName);
                $EntityName=str_replace('Entity:','', $EntityName);
                $objPostListener->removeCachebyEntityNamePrefix($EntityName);
                break;
            }
        }
    }
    
    private function createCacheKey($parameters,$dql)
    {
        /*
        $matches = array();
        $s = preg_match('/Entity(.)[a-zA-Z]*[\s]/', $dql, $matches);
        $entity=trim(!empty($matches[0])?$matches[0]:'Query');
        $entity=str_replace('\\', ':', $entity);
        */
        $entity=$this->getEntityName($dql);
        $joinsEntities='';
        $joinsEntities=$this->getEntityJoinsName($dql);
        $entity=$entity.(!empty($joinsEntities)?":".$joinsEntities:'');
        
        $cacheId = $entity.':'.md5($dql);
        
        //print_r(get_class_methods(get_class()));exit;
        /** @var Parameter $parameter */
        $cacheParam='';
        foreach ($parameters as $parameter) {
            $cacheParam =$cacheParam.':'.serialize($parameter);
        }
        $cacheId.=$cacheParam;
        return array($entity,($cacheId));
    }
    
    private function getEntityName($dql)
    {
        $matches = array();
        $s = preg_match('/Entity(.)[a-zA-Z]*/', $dql, $matches);
        if(!empty($matches[0])){
            $entity=trim($matches[0]);
        }
        else {
            $s = preg_match('/FROM([\sa-zA-Z.])*:[a-zA-Z]*/', $dql, $matches);
            $entity=trim(!empty($matches[0])?$matches[0]:'Query');
            $entity='Entity\\'.substr($entity, strpos($entity, ":")+1);
        }
        return str_replace('\\', ':', $entity);
    }
    
    private function getEntityJoinsName($dql)
    {
        $matches = array();
        $s = preg_match_all('/JOIN[\sa-zA-Z]*(:)[a-zA-Z]*/', $dql, $matches);
        $joinKeys='';
        if(is_array($matches[0]))
        {
            foreach ($matches[0] as $value) {
                $joinKeys.=substr($value, stripos($value,":")+1).":";
            }
            $joinKeys=trim(trim($joinKeys),":");
        }
        return $joinKeys;
    }
}
?>