<?php
// This file is part of mod_datalynx for Moodle - http://moodle.org/
//
// It is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// It is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 *
 * @package mod_datalynx
 * @copyright 2014 onwards by edulabs.org and associated programmers
 * @copyright based on the work by 2013 Itamar Tzadok
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace mod_datalynx\local\rule;

use coding_exception;
use dml_exception;
use mod_datalynx\datalynx;
use moodle_exception;
use moodle_url;
use stdClass;

/**
 * Base class for Datalynx Rule Types
 */
abstract class base {
    /**
     * Subclasses must override the type with their name.
     * @var string
     */
    public $type = 'unknown';

    /**
     * The datalynx object that this rule belongs to.
     *
     * @var ?datalynx
     */
    public $df = null;

    /**
     * The rule object itself, if we know it.
     * @var object
     */
    public $rule = null;

    /**
     * base constructor.
     *
     * @param int|datalynx $df
     * @param int|object $rule
     * @throws coding_exception
     * @throws moodle_exception
     */
    public function __construct($df = 0, $rule = 0) {
        if (empty($df)) {
            throw new coding_exception('Datalynx id or object must be passed to view constructor.');
        } else {
            if ($df instanceof datalynx) {
                $this->df = $df;
            } else { // Datalynx id/object.
                $this->df = new datalynx($df);
            }
        }

        if (!empty($rule)) {
            // Variable $rule is the rule record.
            if (is_object($rule)) {
                $this->rule = $rule; // Programmer knows what they are doing, we hope.

                // Variable $rule is a rule id.
            } else {
                if ($ruleobj = $this->df->get_rule_from_id($rule)) {
                    $this->rule = $ruleobj->rule;
                } else {
                    throw new moodle_exception('invalidrule', 'datalynx', null, null, $rule);
                }
            }
        }

        if (empty($this->rule)) { // We need to define some default values.
            $this->set_rule();
        }
    }

    /**
     *
     * @param \core\event\base $event
     * @return bool
     */
    abstract public function trigger(\core\event\base $event);

    /**
     * Checks if the rule triggers on the given event
     *
     * @param string $eventname full name of the event (with namespaces)
     * @return bool
     */
    public function is_triggered_by($eventname) {
        if (!is_string($eventname) || $eventname === '') {
            return false;
        }

        $eventname = explode('\\', trim($eventname, '\\'))[2];
        $triggers = array_map(
            function ($element) {
                return explode(':', $element)[0];
            },
            unserialize($this->rule->param1)
        );
        return array_search($eventname, $triggers) !== false;
    }

    /**
     * Returns the list of the triggers
     *
     * @return array
     */
    public function get_triggers() {
        static $triggers = [];
        if (empty($triggers)) {
            $triggers = array_map(
                function ($element) {
                    return explode(':', $element)[0];
                },
                unserialize($this->rule->param1)
            );
        }
        return $triggers;
    }

    /**
     * Sets up a rule object
     *
     * @param ?stdClass $forminput Form input data.
     */
    public function set_rule($forminput = null) {
        $this->rule = new stdClass();
        $this->rule->id = !empty($forminput->id) ? $forminput->id : 0;
        $this->rule->type = $this->type;
        $this->rule->dataid = $this->df->id();
        $this->rule->name = !empty($forminput->name) && is_scalar($forminput->name) ? trim((string) $forminput->name) : '';
        $this->rule->description = !empty($forminput->description) && is_scalar($forminput->description) ?
                trim((string) $forminput->description) : '';
        $this->rule->enabled = isset($forminput->enabled) ? $forminput->enabled : 1;
        for ($i = 1; $i <= 10; $i++) {
            $value = $forminput->{"param$i"} ?? null;
            if (is_string($value)) {
                $this->rule->{"param$i"} = $value !== '' ? trim($value) : null;
            } else if (is_scalar($value)) {
                $this->rule->{"param$i"} = (string) $value;
            } else {
                $this->rule->{"param$i"} = $value ?: null;
            }
        }
    }

    /**
     * Insert a new rule in the database
     * @param string $fromform
     * @return bool|int
     * @throws dml_exception
     */
    public function insert_rule($fromform = null) {
        global $DB, $OUTPUT;

        if (!empty($fromform)) {
            $this->set_rule($fromform);
        }
        $this->rule->id = $DB->insert_record('datalynx_rules', $this->rule);
        if (!$this->rule->id) {
            echo $OUTPUT->notification('Insertion of new rule failed!');
            return false;
        } else {
            return $this->rule->id;
        }
    }

    /**
     * Update a rule in the database
     *
     * @param null $fromform
     * @return bool
     * @throws dml_exception
     */
    public function update_rule($fromform = null) {
        global $DB, $OUTPUT;
        if (!empty($fromform)) {
            $this->set_rule($fromform);
        }

        if (!$DB->update_record('datalynx_rules', $this->rule)) {
            echo $OUTPUT->notification('updating of rule failed!');
            return false;
        }
        return true;
    }

    /**
     * Delete a rule completely
     *
     * @return bool
     * @throws dml_exception
     */
    public function delete_rule() {
        global $DB;

        if (!empty($this->rule->id)) {
            $DB->delete_records('datalynx_rules', ['id' => $this->rule->id]);
        }
        return true;
    }

    /**
     * Returns the rule id
     *
     * @return number
     */
    public function get_id() {
        return $this->rule->id;
    }

    /**
     * Is enabled
     *
     * @return boolean
     */
    public function is_enabled() {
        return $this->rule->enabled;
    }

    /**
     * Get type
     *
     * @return string
     */
    public function get_type() {
        return $this->type;
    }

    /**
     * Returns the name of the rule
     * @return string
     */
    public function get_name() {
        return $this->rule->name;
    }

    /**
     * Returns the type name of the rule
     *
     * @return string
     */
    public function typename() {
        return get_string('pluginname', "datalynxrule_{$this->type}");
    }

    /**
     * Get datalynx object
     *
     * @return ?datalynx
     */
    public function df() {
        return $this->df;
    }

    /**
     * Get form
     *
     * @return mixed
     * @throws moodle_exception
     */
    public function get_form() {
        $formclass = manager::get_rule_form_class_name($this->type);
        $actionurl = new moodle_url(
            '/mod/datalynx/rule/rule_edit.php',
            ['d' => $this->df->id(), 'rid' => $this->get_id(), 'type' => $this->type]
        );
        return new $formclass($this, $actionurl);
    }

    /**
     * To form
     *
     * @return object
     */
    public function to_form() {
        return $this->rule;
    }

    /**
     * Get SQL query
     *
     * @return string
     */
    public function get_select_sql() {
        if ($this->rule->id > 0) {
            $id = " c{$this->rule->id}.id AS c{$this->rule->id}_id ";
            $content = $this->get_sql_compare_text() . " AS c{$this->rule->id}_content";
            return " $id , $content ";
        } else {
            return '';
        }
    }

    /**
     * Get sort part for SQL query
     *
     * @param string $paramname
     * @param string $paramcount
     * @return ?array
     */
    public function get_sort_from_sql($paramname = 'sortie', $paramcount = '') {
        $ruleid = $this->rule->id;
        if ($ruleid > 0) {
            $sql = " LEFT JOIN {datalynx_contents} c$ruleid
            ON (c$ruleid.entryid = e.id AND c$ruleid.ruleid = :$paramname$paramcount) ";
            return [$sql, $ruleid];
        } else {
            return null;
        }
    }

    /**
     * Returngs empty string??
     * @return string
     */
    public function get_sort_sql() {
        return '';
    }
}
