<?php

class ActiveRecord extends CActiveRecord {

    /**
     *
     * @var string[]
     */
    protected $changedFields = array();

    const ATTR_GUI_TYPE_TEXT = 'text';
    const ATTR_GUI_TYPE_TEXTAREA = 'textarea';
    const ATTR_GUI_TYPE_RADIO = 'radio';
    const ATTR_GUI_TYPE_DROPDOWN = 'dropdown';
    const ATTR_GUI_TYPE_CHECK = 'checkbox';
    const ATTR_GUI_TYPE_HIDDEN = 'hidden';

    /**
     *
     * @var QueryCacheItem[]
     */
    static $cache = array();

    /**
     *
     * {@inheritDoc}
     *
     * @see CActiveRecord::query()
     */
    protected function query($criteria, $all = false) {
        $thisClass = get_class($this);
        if (paramdeep('db', 'cache', 'enabled') && array_value_exists(paramdeep('db', 'cache', 'types'), $thisClass)) {
            $id = md5($thisClass . json_encode($criteria) . json_encode($this->getDbCriteria()) . json_encode($all));
            if (!isset(self::$cache [$id])) {
                $results = parent::query($criteria, $all);

                $info = new QueryCacheItem ();
                $info->class = get_class($this);
                $info->count = 0;
                $info->criteria = $criteria;
                $info->all = $all;
                $info->data = $results;
                $info->id = $id;
                $info->datatype = getTypeEx($results);

                self::$cache [$id] = $info;

                return $results;
            } else {
                $info = self::$cache [$id];
                /** @var QueryCacheItem $info */
                $info->count ++;
                return $info->data;
            }
        } else {
            return parent::query($criteria, $all);
        }
    }

    /**
     *
     * @param string $attr          
     * @return string[]
     */
    public function getAttributeGuiControl($attr) {
        $html = "";
        if ($this->hasAttribute($attr)) {
            $htmlOptions = array();

            $htmlOptions ['class'] = 'span5';

            $type = $this->getAttributeGuiType($attr);
            switch ($type) {
                case self::ATTR_GUI_TYPE_CHECK :
                    $html = CHtml::activeCheckBox($this, $attr, $htmlOptions);
                    break;
                case self::ATTR_GUI_TYPE_DROPDOWN :
                    $html = CHtml::activeDropDownList($this, $attr, $this->getAttributeGuiValues($attr), $htmlOptions);
                    break;
                case self::ATTR_GUI_TYPE_HIDDEN :
                    $html = CHtml::activeHiddenField($this, $attr, $htmlOptions);
                    break;
                case self::ATTR_GUI_TYPE_RADIO :
                    $html = CHtml::activeRadioButtonList($this, $attr, $this->getAttributeGuiValues($attr), $htmlOptions);
                    break;
                case self::ATTR_GUI_TYPE_TEXT :
                    $html = CHtml::activeTextField($this, $attr, $htmlOptions);
                    break;
                case self::ATTR_GUI_TYPE_TEXTAREA :
                    $html = CHtml::activeTextArea($this, $attr, $htmlOptions);
                    break;
            }
        }
        return $html;
    }

    /**
     *
     * @param string $attr          
     * @return string[]
     */
    public function getAttributeGuiValues($attr) {
        return array();
    }

    /**
     * say for every model's field if it is text,radio,dropdown...
     *
     * @return string[]
     */
    public function getAttributesGuiType() {
        return array();
    }

    /**
     * say if a model's field is text,radio,dropdown...
     *
     * @param string $attr          
     * @return string
     */
    public function getAttributeGuiType($attr) {
        $types = $this->getAttributesGuiType();
        if (array_key_exists($attr, $types)) {
            return $types [$attr];
        }
        return self::ATTR_GUI_TYPE_TEXT;
    }

    public function doesConditionApply($condition) {
        $pks = $this->getPrimaryKey();
        $row = $this->findByPk($pks, $condition);
        /*
         * logObj(array('PKS'=>$pks, 'COND'=>$condition,'ATTR'=>$this->getAttributes(array('validation_result'
         * ,'data_check'
         * ,'Protocol'
         * ,'validation_result'
         * ,'ValidatedBy'
         * ,'ext_code'
         * )), 'RESULT'=>$row));
         */
        return $row != null;
    }

    /**
     *
     * @return string
     */
    public function getCreationDateField() {
        foreach ($this->attributeNames() as $name)
            if (strtolower($name) == 'epoch')
                return $name;
        return null;
    }

    protected function beforeSave() {
        $epochField = $this->getCreationDateField();

        if (!empty($epochField)) {
            if ($this->isNewRecord)
                $this->$epochField = new DateTimeEx ();
            // else
            // $this->modified = new CDbExpression('NOW()');
        }
        return parent::beforeSave();
    }

    protected function afterSave() {
        $userTracking = new CArray(paramdeep('security', 'user_tracking'));
        if ($userTracking->enabled && $this->tableName() != 'user_tracking_table') {
            if (!array_value_exists($userTracking->exclude_tables, $this->tableName())) {
                $uti = new UserTrackingTable ();
                $uti->action = $this->isNewRecord ? "INSERT" : "UPDATE";
                $uti->id_user = user()->id;
                $uti->table_name = $this->tableName();
                $uti->primary_key = _2str($this->primaryKey, true, true);
                $uti->epoch = DateTimeEx::nows();
                $uti->controller = (app()->controller->id . "/" . app()->controller->action->id);
                if ($userTracking->data_tracked_table !== false) {
                    if (array_value_exists($userTracking->data_tracked_table, $this->tableName()))
                        $uti->DataValue = $this->attributes;
                    else
                        $uti->DataValue = $this->getChangedFieldsValues();
                }
                $uti->insert();
            }
        }
        return parent::afterSave();
    }

    protected function afterDelete() {
        $userTracking = new CArray(paramdeep('security', 'user_tracking'));
        if ($userTracking->enabled && $this->tableName() != 'user_tracking_table') {
            if (!array_value_exists($userTracking->exclude_tables, $this->tableName())) {
                $uti = new UserTrackingTable ();
                $uti->action = "DELETE";
                $uti->id_user = user()->id;
                $uti->table_name = $this->tableName();
                $uti->primary_key = _2str($this->primaryKey, true, true);
                $uti->epoch = DateTimeEx::nows();
                $uti->controller = (app()->controller->id . "/" . app()->controller->action->id);
                if ($userTracking->data_tracked_table !== false) {
                    if (array_value_exists($userTracking->data_tracked_table, $this->tableName()))
                        $uti->DataValue = $this->attributes;
                    else
                        $uti->DataValue = $this->getChangedFieldsValues();
                }
                $uti->insert();
            }
        }
        return parent::afterDelete();
    }

    public function getRecordClassName() {
        return get_class($this);
    }

    public function getLabelsOverride() {
        $labels = paramdeep('modelLabelsOverride', $this->getRecordClassName());
        return $labels;
    }

    /**
     *
     * {@inheritDoc}
     *
     * @see CActiveRecord::get AttributeLabel()
     */
    public function getAttributeLabel($name) {
        $labels = $this->attributeLabels();
        return $labels [$name];
    }

    /**
     *
     * @return array customized attribute labels (name=>label)
     */
    public function attributeLabels() {
        $names = $this->attributeNames();

        $labels = array();
        foreach ($names as $name) {
            $labels [$name] = t($name);
        }

        if (method_exists($this, "myAttributeLabels")) {
            $myLabels = $this->myAttributeLabels();
            foreach ($myLabels as $myName => $myValue) {
                $labels [$myName] = $myValue;
            }
        }

        $customLabels = $this->getLabelsOverride();
        if ($customLabels != null) {
            foreach ($customLabels as $clk => $clv) {
                $labels [$clk] = $clv;
            }
        }

        return $labels;
    }

    public function getUserTrackingID() {
        return array(
            'table' => $this->tableName(),
            'pk' => $this->primaryKey
        );
    }

    public function getErrorsHtml() {
        $html = "";
        foreach ($this->errors as $e) {
            if (!empty($html))
                $html .= '<br>' . PHP_EOL;
            if (is_array($e))
                $html .= implode('<br>' . PHP_EOL, $e);
            else
                $html .= $e;
        }
        return $html;
    }

    public function __get($name) {
        $getter = 'get' . $name;
        if (method_exists($this, $getter))
            return $this->$getter();

        return parent::__get($name);
    }

    public function __set($name, $value) {
        if ($this->hasAttribute($name) && ($this->$name !== $value)) {
            if (!isset($this->changedFields [$name]))
                $this->changedFields [$name] = 0;
            $this->changedFields [$name] ++;
        }

        $setter = 'set' . $name;
        if (method_exists($this, $setter))
            return $this->$setter($value);
        return parent::__set($name, $value);
    }

    /**
     *
     * @return string[]
     */
    public function getChangedFields() {
        return array_keys($this->changedFields);
    }

    public function getChangedFieldsValues() {
        return $this->getAttributes($this->getChangedFields());
    }

    /**
     *
     * @return boolean
     */
    public function updateChanged() {
        return $this->update($this->getChangedFields());
    }

    /**
     *
     * @return ReflectionClass
     */
    public function getObjectType() {
        return new ReflectionClass($this);
    }

    /**
     *
     * @param boolean $exportRelations
     *          default false
     * @return array
     */
    public function exportByPk($exportRelations = false) {
        $exportParams = array();
        if ($exportRelations) {
            foreach ($this->relations() as $relName => $relData) {
                if (is_array_ex($relData, true)) {
                    list ( $rType, $rModel ) = $relData;
                    if (class_exists($rModel)) {
                        $tmpModel = new $rModel ();
                        if ($tmpModel instanceof ActiveRecord) {
                            $relatedObjects = $this->$relName;
                            if ($relatedObjects != null) {
                                if (!is_array_ex($relatedObjects))
                                    $relatedObjects = array(
                                        $relatedObjects
                                    );
                                $items = array_get($exportParams, $tableName, array());
                                foreach ($relatedObjects as $relObj)
                                    if ($relObj instanceof ActiveRecord)
                                        $items [] = $relObj->attributes;
                                $exportParams [$tableName] = $items;
                            }
                        }
                    }
                }
            }
        }
        return query_export_by_pk($exportParams);
    }

}
