<?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 REGISTER_STATUS_UNREGISTERED = "unregistered";
    const REGISTER_STATUS_TO_REGISTER = "to_register";
    const REGISTER_STATUS_REGISTERED = "registered";
    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
                )
            ),
            'validable' => array(
                'condition' => 'status = :readyToEvaluateStatus and (in_charge_to = 0 or in_charge_to is null)',
                'params' => array(
                    ':readyToEvaluateStatus' => self::STATUS_READY_TO_EVALUATE
                )
            ),
            'incomplete' => array(
                'condition' => "status in ('" . implode("','", $this->incompleteStatuses) . "')"
            ),
            'unregistered' => array(
                'condition' => 'registration_status = :result',
                'params' => array(
                    ':result' => self::REGISTER_STATUS_UNREGISTERED
                )
            ),
            'to_register' => array(
                'condition' => 'registration_status = :result',
                'params' => array(
                    ':result' => self::REGISTER_STATUS_TO_REGISTER
                )
            ),
            'registered' => array(
                'condition' => 'registration_status = :result',
                'params' => array(
                    ':result' => self::REGISTER_STATUS_REGISTERED
                )
            ),
        );
    }

    /**
     *
     * @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'
            ),
            'registerOperator' => array(
                self::BELONGS_TO,
                'User',
                'report_user'
            ),
            'validatingOperator' => array(
                self::BELONGS_TO,
                'User',
                'id_user_validating'
            ),
            '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
            ),
            'toBeConfirmed' => array(
                self::STAT,
                'Event',
                'id_run',
                'condition' => Event::SQL_VALIDATED_NOT_CONFIRMED
            )
        );
    }

    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 isValidatingBy($userId) {
        if (!isset($this->id_user_validating))
            return false;

        return $this->id_user_validating == $userId;
    }

    public function isAssigned() {
        if (!isset($this->in_charge_to) || $this->in_charge_to <= 0)
            return false;

        return true;
    }

    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 isRegistered() {
        return ($this->report_user > 0);
    }

    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;
    }

    public function areAllEventsValidatedAndNotSuspended() {
        return ($this->eventsCount > 0 && $this->eventsCount == $this->eventsValidatedCount && $this->eventsPendingCount <= 0);
    }

    /**
     *
     * {@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;
    }

    public function activateRegistration() {
        $this->registration_status = self::REGISTER_STATUS_TO_REGISTER;
    }

    public function confirmRegistration() {
        $this->registration_status = self::REGISTER_STATUS_REGISTERED;
    }

}
