<?php

/**
 * This is the model class for table "run" - NOT "run_schedule"!!!! CODE DOWNSTAIRS UNTOUCHED FOR NOW... 
 *
 * The followings are the available columns in table 'run_schedule':
 * @property string $ID
 * @property string $id_gategroup
 * @property string $status
 * @property string $origin_type
 * @property integer $confirmation_required
 * @property string $expected_start_time
 * @property string $expected_end_time
 * @property string $confirmed_by
 * @property string $confirmation_time
 * @property string $start_time
 * @property string $in_charge_to
 * @property string $in_charge_to_time
 * @property string $terminated_by
 * @property string $termination_type
 * @property string $termination_time
 * @property string $termination_confirmed_time
 * @property string $epoch
 * @property string $name
 * 
 * @property User $assignedOperator 
 * @property GateGroup gateGroup
 * @property string Description
 */
class Service extends ActiveRecord implements ISortable
{

  const STATUS_IBERNATED = 'IBERNATED';

  const STATUS_WAITING_CONFIRMATION = 'WAITING_CONFIRMATION';

  const STATUS_WAITING_ACTIVATION = 'WAITING_ACTIVATION';

  const STATUS_ACTIVE = 'ACTIVE';

  const STATUS_ABORTED = 'ABORTED';

  const STATUS_WAITING_TERMINATION = 'WAITING_TERMINATION';

  const STATUS_TERMINATED = 'TERMINATION_CONFIRMED';

  const STATUS_READY_TO_EVALUATE = 'READY_TO_EVALUATE';

  const STATUS_TERMINATION_CONFIRMED = 'TERMINATION_CONFIRMED';

  const MANUAL_SERVICE = 'MANUAL';

  const SCHEDULED_SERVICE = 'AUTOMATIC';

  private $incompleteStatuses = array (
      self::STATUS_ACTIVE,
      self::STATUS_WAITING_ACTIVATION,
      self::STATUS_WAITING_TERMINATION,
      self::STATUS_WAITING_CONFIRMATION 
  );

  private $validStatuses = array (
      self::STATUS_TERMINATED,
      self::STATUS_ACTIVE,
      self::STATUS_WAITING_ACTIVATION,
      self::STATUS_WAITING_TERMINATION,
      self::STATUS_WAITING_CONFIRMATION 
  );

  public static function model($className = __CLASS__)
  {
    return parent::model ( $className );
  }

  public function tableName()
  {
    return 'run';
  }

  public function scopes()
  {
    return array (
        'active' => array (
            'condition' => 'status = :status',
            'params' => array (
                ':status' => self::STATUS_ACTIVE 
            ) 
        ),
        'waitingActivation' => array (
            'condition' => 'status = :status',
            'params' => array (
                ':status' => self::STATUS_WAITING_ACTIVATION 
            ) 
        ),
        'waitingTermination' => array (
            'condition' => 'status = :status',
            'params' => array (
                ':status' => self::STATUS_WAITING_TERMINATION 
            ) 
        ),
        'waitingConfirmation' => array (
            'condition' => 'status = :status',
            'params' => array (
                ':status' => self::STATUS_WAITING_CONFIRMATION 
            ) 
        ),
        'terminated' => array (
            'condition' => 'status = :status',
            'params' => array (
                ':status' => self::STATUS_TERMINATED 
            ) 
        ),
        'ibernated' => array (
            'condition' => 'status = :status',
            'params' => array (
                ':status' => self::STATUS_IBERNATED 
            ) 
        ),
        'readyToEvaluate' => array (
            'condition' => 'status = :status',
            'params' => array (
                ':status' => self::STATUS_READY_TO_EVALUATE 
            ) 
        ),
        'valid' => array (
            'condition' => 'status != :status1 AND status != :status2',
            'params' => array (
                ':status1' => self::STATUS_ABORTED,
                ':status2' => self::STATUS_IBERNATED 
            ) 
        ),
        
        'incomplete' => array (
            'condition' => "status in ('" . implode ( "','", $this->incompleteStatuses ) . "')" 
        ) 
    );
  }

  /**
   *
   * @param number $serviceId          
   * @param bool $throwOnFail
   *          default=true
   * @throws CHttpException
   * @return Service
   */
  public static function findById($serviceId, $throwOnFail = true)
  {
    $serviceObj = Service::model ()->findByPk ( $serviceId );
    if ($serviceObj === null && $throwOnFail)
      throw new CHttpException ( 404, t ( "The requested service '{0}' does not exist.", array (
          '{0}' => $serviceId 
      ) ) );
    return $serviceObj;
  }

  /**
   *
   * @param string $condition
   *          default=empty
   * @param array $params
   *          default=empty
   * @param bool $sort
   *          default=false
   * @return Service[]
   */
  public static function getAll($condition = '', array $params = array(), $sort = false)
  {
    $services = self::model ()->findAll ( $condition, $params );
    if ($sort)
      sort_sortable ( $services );
    return $services;
  }

  /**
   *
   * @return Service[]
   */
  public static function getLastN($count)
  {
    $ids = query ( "SELECT id FROM run ORDER BY start_time DESC, termination_time DESC, id_gategroup LIMIT $count" );
    $services = array ();
    foreach ( $ids as $s )
      $services [] = self::findById ( $s->id );
    return $services;
  }

  public function byAssignedOperator($userId)
  {
    $this->getDbCriteria ()
      ->mergeWith ( array (
        'condition' => 'in_charge_to = :user_id',
        'params' => array (
            ':user_id' => $userId 
        ) 
    ) );
    return $this;
  }

  public function byGateGroup($gateGroupId)
  {
    $this->getDbCriteria ()
      ->mergeWith ( array (
        'condition' => 'id_gategroup = :gategroup_id',
        'params' => array (
            ':gategroup_id' => $gateGroupId 
        ) 
    ) );
    return $this;
  }

  public function rules()
  {
    return array (
        array (
            'id_gategroup, status, origin_type, expected_start_time, expected_end_time',
            'required' 
        ),
        array (
            'status',
            'in',
            'range' => array (
                self::STATUS_IBERNATED,
                self::STATUS_WAITING_CONFIRMATION,
                self::STATUS_WAITING_ACTIVATION,
                self::STATUS_ACTIVE,
                self::STATUS_ABORTED,
                self::STATUS_TERMINATED,
                self::STATUS_WAITING_TERMINATION,
                self::STATUS_READY_TO_EVALUATE 
            ) 
        ),
        array (
            'origin_type, termination_type',
            'in',
            'range' => array (
                self::MANUAL_SERVICE,
                self::SCHEDULED_SERVICE 
            ) 
        ) 
    );
  }

  public function relations()
  {
    return array (
        'events' => array (
            self::HAS_MANY,
            'Event',
            'id_run' 
        ),
        'gateGroup' => array (
            self::BELONGS_TO,
            'GateGroup',
            'id_gategroup' 
        ),
        'startOperator' => array (
            self::BELONGS_TO,
            'User',
            'confirmed_by' 
        ),
        'stopOperator' => array (
            self::BELONGS_TO,
            'User',
            'terminated_by' 
        ),
        'assignedOperator' => array (
            self::BELONGS_TO,
            'User',
            'in_charge_to' 
        ),
        
        'newEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'new' 
        ),        
        'voidEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'void' 
        ),
        'examinedEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'examined' 
        ),
        'pendingEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'pending' 
        ),
        
        'notClosedEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'notClosedYet' 
        ),
        'examinedNotClosedEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'examinedNotClosedYet' 
        ),
        'pendingNotClosedEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'pendingNotClosedYet' 
        ),
        
        'notClosedNotPendingEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'notClosedYetNotPending' 
        ),
        'examinedNotClosedNotPendingEvents' => array (
            self::HAS_MANY,
            'Event',
            'id_run',
            'scopes' => 'examinedNotClosedYetNotPending' 
        ),
        
        'eventsCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_TO_BE_MANAGED 
        ),
        'eventsNewCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_NEW_CONDITION 
        ),
        'eventsVoidCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_VOID_CONDITION   
        ),
        'eventsExaminedCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_EXAMINED_CONDITION 
        ),
        'eventsValidatedCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_VALIDATED_CONDITION 
        ),
        'eventsApprovedCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_APPROVED_CONDITION 
        ),
        'eventsInvalidCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_INVALID_CONDITION 
        ),
        'eventsPendingCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_PENDING_CONDITION 
        ),
        'eventsQueuedForExportCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_QUEUED_FOR_EXPORT_CONDITION 
        ),
        'eventsExportedCount' => array (
            self::STAT,
            'Event',
            'id_run',
            'condition' => Event::SQL_EXPORTED_CONDITION 
        ) 
    );
  }

  public function behaviors()
  {
    return array (
        'edatetimebehavior' => array (
            'class' => 'ext.EDateTimeBehavior' 
        ) 
    );
  }

  public function isActive()
  {
    return $this->status == self::STATUS_ACTIVE;
  }

  public function isWaitingActivation()
  {
    return $this->status == self::STATUS_WAITING_ACTIVATION;
  }

  public function isWaitingConfirmation()
  {
    return $this->status == self::STATUS_WAITING_CONFIRMATION;
  }

  public function isWaitingTermination()
  {
    return $this->status == self::STATUS_WAITING_TERMINATION;
  }

  public function setStart()
  {
    $this->status = self::STATUS_WAITING_ACTIVATION;
    $this->start_time = new DateTime ();
  }

  public function setManualStart($startDateTime, $endDateTime, $gateGroupId, $operator)
  {
    $ibernatedInGateGroup = Service::model ()->byGateGroup ( $gateGroupId )
      ->ibernated ()
      ->findAll ();
    $this->abortAll ( $ibernatedInGateGroup );
    
    $this->expected_start_time = $startDateTime;
    $this->expected_end_time = $endDateTime;
    $this->origin_type = self::MANUAL_SERVICE;
    $this->id_gategroup = $gateGroupId;
    $this->epoch = $startDateTime;
    $this->name = 'Run';
    $this->confirmation_required = 0;
    $this->confirmed_by = $operator->id;
    $this->confirmation_time = new DateTime ();
    $this->in_charge_to = 0;
    $this->terminated_by = 0;
    
    $this->setStart ();
  }

  public function setStop($operator)
  {
    $this->status = self::STATUS_WAITING_TERMINATION;
    $this->termination_type = self::MANUAL_SERVICE;
    $this->terminated_by = $operator->id;
    $this->termination_time = new DateTime ();
  }

  public function takeCharge($operator)
  {
    $this->in_charge_to = $operator->id;
    return $this->save ();
  }

  public function releaseCharge()
  {
    $this->in_charge_to = null;
    return $this->save ();
  }

  public function isAssignedTo($userId)
  {
    if (! isset ( $this->assignedOperator ))
      return false;
    
    return $this->assignedOperator->id == $userId;
  }

  public function abortAll(array $services)
  {
    if (count ( $services ) > 0)
      $this->changeStatus ( $services, self::STATUS_ABORTED );
  }

  public function isManageable()
  {
    return ($this->status == self::STATUS_READY_TO_EVALUATE);
  }

  public function setReadyToEvaluate()
  {
    $this->status = self::STATUS_READY_TO_EVALUATE;
    return $this->save ();
  }
  
  // PRIVATE
  private function changeStatus(array $services, $status)
  {
    foreach ( $services as $service )
    {
      $service->status = $status;
      if (! $service->save ())
        throw new CExpection ( Yii::t ( 'app', 'Cannot set status "{status}" to service #{service_id}', array (
            '{status}' => $status,
            '{service_id}' => $service->ID 
        ) ) );
    }
  }

  public static function parseTime($time)
  {
    if (isEmpty ( $time ) || $time == '1970-01-01 01:00:00')
      return null;
    else
      return new DateTimeEx ( $time );
  }

  /**
   *
   * @return DateTimeEx
   */
  public function getStartTime()
  {
    if ($this->origin_type == self::MANUAL_SERVICE)
      return array_first_not_null ( array (
          self::parseTime ( $this->start_time ),
          self::parseTime ( $this->expected_start_time ) 
      ) );
    else
      return array_first_not_null ( array (
          self::parseTime ( $this->expected_start_time ),
          self::parseTime ( $this->start_time ) 
      ) );
  }

  /**
   *
   * @return DateTimeEx
   */
  public function getEndTime()
  {
    if ($this->termination_type == self::MANUAL_SERVICE)
      return array_first_not_null ( array (
          self::parseTime ( $this->termination_time ),
          self::parseTime ( $this->expected_end_time ) 
      ) );
    else
      return array_first_not_null ( array (
          self::parseTime ( $this->expected_end_time ),
          self::parseTime ( $this->termination_time ) 
      ) );
  }

  /**
   *
   * @return string
   */
  public function getDescription()
  {
    $startTime = $this->getStartTime ();
    if ($startTime == null)
    {
      $interval = t ( "Invalid" );
    }
    else
    {
      $endTime = $this->getEndTime ();
      if ($this->isActive ())
      {
        $interval = $startTime->toSmartString () . " (" . t ( "Running" ) . ")";
      }
      else
      {
        if ($startTime->toSmartString () == $endTime->toSmartString ())
          $interval = $startTime->toSmartString ();
        else
        {
          if (containsStr ( $endTime->toSmartString (), ' 23:59:59' ))
            $interval = $startTime->toSmartString () . " -> " . $endTime->addFromString ( '1 second' )
              ->toSmartString ();
          else
            $interval = $startTime->toSmartString () . " -> " . $endTime->toSmartString ();
        }
      }
    }
    
    /** @var GateGroup $group */
    $group = $this->gateGroup;
    
    return "[{$this->ID} / {$group->name}] {$interval}";
  }

  public function isTerminated()
  {
    if ($this->status == self::STATUS_TERMINATED)
      return true;
    
    if ($this->status == self::STATUS_READY_TO_EVALUATE)
      return true;
    
    return false;
  }

  public function isReadyForEvaluation()
  {
    return $this->status == self::STATUS_READY_TO_EVALUATE;
  }

  /**
   *
   * {@inheritDoc}
   *
   * @see ISortable::compare()
   * @param Service $other          
   */
  public function compare($other)
  {
    $compare = compare ( new DateTimeEx ( $other->start_time ), new DateTimeEx ( $this->start_time ) );
    if ($compare == 0)
      $compare = compare ( $other->id_gategroup, $this->id_gategroup );
    return $compare;
  }
}
