<?php
/**
 * Класс является "центром действия";
 * К данному классу нужно будет обращатся для произведениями операций с условиями в целом.
 * Тут будут осуществляться такие алгоритмы как:
 *  - Загрузка и обработка файла параметров условий;
 *  - Обработка основных операций: получение списка всех условий, получение конкретного условия по id и т.п.
 */
namespace common\modules\triggers\components\conditions;

use common\modules\triggers\controllers\TriggerAdminController;
use common\modules\triggers\models\TriggerCondition;
use common\modules\triggers\models\TriggerTrigger;
use yii\web\ServerErrorHttpException;

class Conditions {

    private static $config;

    /**
     * Все действия с условиями производятся через метод init
     * @return $this Conditions
     */
    public static function init(){
        // Переопределили переменную конфиг (чтобы в дальнейшем было проще обращатся)
        self::$config = require(__DIR__.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'main.php');
        // Сначала производится проверка всех классов условий на наличие необходимых констант и переменных
        self::createConditions();
        // Если до этого момента скрипт не остановился и не вернул ошибку, значит возвращаем экземпляр класса
        return new Conditions();
    }

    /**
     * Метод проверяет конфиг на наличие ошибок. Так же дополнительно производится проверка на обязательную константу ID
     * @return bool
     * @throws \yii\web\ServerErrorHttpException
     */
    public static function createConditions(){
        $uniqueId=array();
        // Перебираем все условия что указаны в конфиге
        foreach(self::$config as $condition) {
            // Если путь к классу указан, и файл по этому пути существует
            if (isset($condition['class']) && file_exists(\Yii::getAlias('@mainRoot').DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $condition['class']).'.php')) {
                // Занесли класс в переменную
                $class = $condition['class']::init();
                // Проверяем наличие обязательных констант
                // Проверили существование идентификатора
                if ($class::defined('CONDITION_ID')===true) {
                    // Если с идентификатором все в порядке, проверяем его на уникальность.
                    // В системе не должно быть два или более классов с условиями имеющие одинаковые идентификаторы
                    if (!in_array($class::CONDITION_ID, $uniqueId))
                        $uniqueId[] = $class::CONDITION_ID;
                    else
                        throw new ServerErrorHttpException('Condition ID have to be unique', 500);
                } else
                    throw new ServerErrorHttpException('Condition ID in class "'.get_class($class).'" has not found', 500);
            } else {
                throw new ServerErrorHttpException('Condition class file or path not found. Please check configuration file', 500);
            }
        }
        // Если все условия верны, возвращаем true
        return true;
    }

    /**
     * Возвращаем массив всех условий в системе
     * @return array
     */
    public function getConditions(){
        $data = array();
        foreach(self::$config as $key=>$condition) {
            $class = $condition['class']::init();
            $class->config = self::$config[$key];
            $data[$class::CONDITION_ID] = $class;
        }
        return $data;
    }

    /**
     * Возвращаем класс условия по ID
     * @param $id
     * @return null
     */
    public function getConditionById($id){
        foreach(self::$config as $key=>$condition) {
            $class = $condition['class']::init();
            $class->config = self::$config[$key];
            if ($class::CONDITION_ID==$id)
                return $class;
        }
        return null;
    }

    /**
     * @param $id
     * @param $key
     * @param array $values
     * @return string
     */
    public static function getConditionHtml($id, $key, $values=array()){
        $condition = Conditions::init()->getConditionById($id);
        $condition_key = rand(100,999);
        $result = '<div class="alert alert-info alert-dismissible" role="alert">';
        $result.= '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>';
        $result.= '<strong>Условие: '.$condition->getName().'</strong>';
        $result.= '<input type="hidden" name="Conditions['.$key.']['.$condition->getId().$condition_key.'][id]" value="'.$condition->getId().'"/>';
        foreach($condition->getParams($key, $condition_key, $values) as $param) {
            $result.=$param;
        }
        $result.= '</div>';
        return $result;
    }

    /**
     * @param array $condition_array
     * @param null $key
     * @return string
     */
    public static function getControlButtons($condition_array=array(), $key=null){
        // Сгенерировали случайный ключ (для массива в форме)
        $key = ($key!==null) ? $key : rand(100000,999999);
        // Сформировали и вернули новую строку в таблице
        return '<tr data-key='.$key.'><td>'.Conditions::init()->getControlArea($condition_array, $key).'</td></tr>';
    }

    /**
     * @param array $condition_array
     * @param $key
     * @return string
     */
    public function getControlArea($condition_array=array(), $key=null){
        $html  = '<div class="conditions-block">';
        if (count($condition_array)>0) {
            foreach($condition_array as $condition) {
                $html .= self::getConditionHtml($condition['id'], ($key!==null) ? $key : rand(100000,999999), (array_key_exists('params', $condition)) ? $condition['params'] : array());
            }
        }
        $html .= '</div>';
        $html .= '<div class="btn-group" role="group">';
        $html .= '<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="glyphicon glyphicon-plus"></i></button>';
        $html .= '<ul class="dropdown-menu">';
        foreach(Conditions::init()->getConditions() as $id=>$condition)
            $html .= '<li><a href="#" data-id="'.$id.'" class="add_condition">'.$condition->getName().'</a></li>';
        $html .= '<li><a href="#" class="delete_row" style="color: darkred;">Удалить условия</a></li>';
        $html .= '</ul>';
        $html .= '</div>';
        return $html;
    }

    /**
     * Получаем триггер и проверяем срабатывание всех условий в его рамках
     * @param \common\modules\triggers\models\TriggerSchedule $model
     * @param $trigger_id
     * @return bool
     */
    public function handleMessageByTriggerId($model, $trigger_id) {
        // Результирующий массив
        $resultArray=[];
        // Получили все условия в рамках триггера
        $conditions = TriggerCondition::find()->where(['trigger_id' => $trigger_id])->all();
        // Привели массив в понятный вид
        $conditionsArray=[];
        foreach($conditions as $i=>$condition) {
            $object=Conditions::init()->getConditionById($condition->condition_id);
            if ($object->recall===false)
                return false;
            $conditionsArray[$i][] = $condition;
        } // На выходе получаем массив необходимого формата
        // Обрабатываем отформатированный массив
        foreach($conditionsArray as $key=>$array) {
            $resultArray[] = self::checkConditionsArray($array, $model);
        }
        // Проверяем результат
        if (count($resultArray)>0) {
            foreach($resultArray as $result)
                if ($result===false)
                    return false;
            return true;
        }
        return false;
    }

    /**
     * Проверяем массив параметров на срабатывание хотябы одного условия
     * @param $array
     * @param $message
     * @return bool
     */
    public static function checkConditionsArray($array, $message){
        foreach($array as $item) {
            $object=Conditions::init()->getConditionById($item->condition_id);
            $object->model=$item;
            if ($object->check($message)==true)
                return true;
        }
        return false;
    }
}