<?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 . serialize ( $criteria ) . serialize ( $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 );
  }
}
