<?php

use Illuminate\Database\Eloquent\Collection;
use Eae\Database\Eloquent\Collection\BaseCollection;
use Illuminate\Support\Contracts\ArrayableInterface;

abstract class BaseModel extends Eloquent {
    
    protected $customRelations = array();
    
    public static function getTableName(){
        return with(new static)->getTable();    
    } 
    
    public static function getPrimaryKeyName(){
        return with(new static)->getKeyName();   
    }
    
    public function keyExists($key){
        $camelKey = camel_case($key);
        return (array_key_exists($key, $this->attributes) || array_key_exists($key, $this->relations) || method_exists($this, $camelKey));
    }
    
    /*****************************************
     * Act like toArray but generate stdClass objects on the current object.
     *****************************************/
    public function toObject()
    {
        return $this->convertToObject($this);
    }

    /*****************************************
     * Act like toArray but generate stdClass objects on the given object.
     *****************************************/
    public function convertToObject($value)
    {

        if ($value instanceof Eloquent)
        {
            $attributes = $value->toArray();
            $relations  = $value->relationsToArray();

            $object = new stdClass();
            
            foreach($attributes AS $key => $attribute)
            {
                if (array_key_exists($key, $relations)) 
                {
                    $key = camel_case($key);
                    $object->$key = $this->convertToObject($value->$key);
                }
                else 
                {
                    $object->$key = $attribute;
                }
            }

            return $object;
        }

        if ($value instanceof Collection)
        {
            $array = array();
            
            foreach($value AS $key => $element)
            {
                $array[$key] = $this->convertToObject($element);
            }
            
            return $array;
        }

        return $value;
    }
    
    public function newCollection(array $models = array()){
        return new BaseCollection($models);        
    }
    
    public function isRelationLoaded($relationName){
        return array_key_exists($relationName, $this->relations);
    }
    
    public function isCustomRelationLoaded($relationName){
        return array_key_exists($relationName, $this->customRelations);    
    }
    
    public function customRelation($relationName){
        if($this->isCustomRelationLoaded($relationName)){
            return $this->customRelations[$relationName];
        }
        $this->setCustomRelation($relationName, $this->_loadCustomRelation($relationName));
        return $this->customRelations[$relationName]; 
    }
    
    public function setCustomRelation($relationName, $value){
        $this->customRelations[$relationName] = $value;            
    }
    
    protected function _loadCustomRelation($relationName){
        $method = $this->getCustomRelationQueryMethodName($relationName);
        $query = $this->$method(); 
        return $query->get();   
    }
    
    public function getCustomRelationQueryMethodName($relationName){
        return "customRelation{$relationName}Query";        
    }
    
    public function toArray(){
        $attributes = parent::toArray();

        return array_merge($attributes, $this->customRelationsToArray());
        
    }
    
    public function customRelationsToArray()
    {
        $attributes = array();
        
        foreach ($this->customRelations as $key => $value)
        {
            if (in_array($key, $this->hidden)) continue;
            // If the values implements the Arrayable interface we can just call this
            // toArray method on the instances which will convert both models and
            // collections to their proper array form and we'll set the values.
            if ($value instanceof ArrayableInterface)
            {
                $relation = $value->toArray();
            }

            // If the value is null, we'll still go ahead and set it in this list of
            // attributes since null is used to represent empty relationships if
            // if it a has one or belongs to type relationships on the models.
            elseif (is_null($value))
            {
                $relation = $value;
            }

            // If the relationships snake-casing is enabled, we will snake case this
            // key so that the relation attribute is snake cased in this returned
            // array to the developers, making this consistent with attributes.
            if (static::$snakeAttributes)
            {
                $key = snake_case($key);
            }

            // If the relation value has been set, we will set it on this attributes
            // list for returning. If it was not arrayable or null, we'll not set
            // the value on the array because it is some type of invalid value.
            if (isset($relation) || is_null($value))
            {
                $attributes[$key] = $relation;
            }

            unset($relation);
        }

        return $attributes;
    } 
}

