<?php
/**
 * Class ManageListBuilder
 * Generates the manage.php type lists used in module builder built module admin panels
 * @copyright MTC media Ltd.
 * @author Rihards Silins
 * @version 2 14/07/2016
 */

namespace Mtc\Modules\ModuleBuilder\Classes\Builders;

use Illuminate\Database\Capsule\Manager as Capsule;
use Mtc\Modules\ModuleBuilder\Classes\ModuleHelper;

class ManageListBuilder extends PanelBuilder
{
    /**
     * whether or not ManageListBuilder::start() has been called and ManageListBuilder::end() yet hasn't.
     *
     * @var bool
     */
    protected $list_open = true;
    /**
     * path to templates for generating lists
     *
     * @var string
     */
    protected $template_path = "/admin/module_builder/builders/manage_list_builder";

    /**
     * ManageListBuilder class constructor.
     * @param Twig Object $twig
     */
    public function __construct($twig)
    {
        $this->twig = $twig;
    }

    /**
     * ManageListBuilder::start()
     * Starts list.
     *
     * @param Object model
     * @param mixed[] params
     *
     * @return void
     */
    public function start($model = null, $params = array())
    {
        // init
        $this->start_microtime = microtime(true);
        $this->model = $model;

        if (!empty($this->model)) {
            $this->model_name = get_class($this->model);
        }

        $this->list_open = true;

        $this->input_default_params = array(
            'id'                        => null,
            'tag'                       => 'input', // only used on undefined/custom types
            'type'                      => 'text',
            'button_type'               => 'button',
            'class'                     => '',
            'value'                     => null,
            'default_value'             => null,
            'attr_name'                 => null,
            'input_name'                => null,
            'scoped_name'               => true,
            'datetime_db_format'        => 'Y-m-d H:i:s',
            'datetime_frontend_format'  => 'd/m/Y H:i',
            'preview_image_path'        => 'uploads/images/cms/small',
            'file_path'                 => 'uploads/files',
            'container'                 => array(),
            'label'                     => null,
            'info'                      => '',
            'dynamic_options_select'    => array(
                'name',
            ),
            'dynamic_options_filter'    => array(
                array('active', '=', 1),
            ),
            'dynamic_options_order'     => array(
                array('name','ASC'),
            ),
        );

        $check_simple_isset_parameters = array(
            "scoped_name",
            "preview_image_path",
            "file_path",
            "dynamic_options_select",
            "dynamic_options_filter",
            "dynamic_options_order"
        );
        foreach ($check_simple_isset_parameters as $check_simple_isset_parameter) {
            if (isset($params[$check_simple_isset_parameter])) {
                $this->input_default_params[$check_simple_isset_parameter] = $params[$check_simple_isset_parameter];
                unset($params[$check_simple_isset_parameter]);
            }
        }

        if (isset($params['input_default_params'])) {
            $this->input_default_params = array_merge(
                $this->input_default_params,
                $params['input_default_params']
            );
            unset($params['input_default_params']);
        }
        return;
    }

    /**
     * ManageListBuilder::end()
     * Ends form.
     * @param mixed[] params
     * @return float $duration
     */
    public function end($params = array())
    {
        $this->model = null;
        $this->list_open = false;
        $duration = microtime(true) - $this->start_microtime;
        return $duration;
    }

    /**
     * ManageListBuilder::header()
     * Creates & returns a html header.
     * @param mixed[] $params
     * @return string $html
     */
    public function header($params)
    {
        $attributes = $this->input_default_params;

        // process params

        if (!is_array($params)) {
            $attributes['attr_name'] = $params;
            $params = array();
        } else {
            $attributes['attr_name'] = key($params);
            $params = $params[$attributes['attr_name']];
        }

        $attributes['scoped_name'] = $this->input_default_params['scoped_name'];
        if (isset($params['scoped_name'])) {
            $attributes['scoped_name'] = $params['scoped_name'];
            unset($params['scoped_name']);
        } elseif (!empty($params['type']) && $params['type'] === 'submit') {
            $attributes['scoped_name'] = false;
        }

        // if there is no model - it can't be scoped
        if (empty($this->model)) {
            $attributes['scoped_name'] = false;
        }

        if ($attributes['scoped_name']) {
            $attributes['input_name'] = $this->model_name.'['.$attributes['attr_name'].']';
        } else {
            $attributes['input_name'] = $attributes['attr_name'];
        }

        $attributes['label'] = $this->labelFromAttrName($attributes['attr_name']);
        $attributes['id'] = $attributes['input_name'];

        $check_simple_isset_parameters = array("label", "type", "info", "class", "id");
        foreach ($check_simple_isset_parameters as $check_simple_isset_parameter) {
            if (isset($params[$check_simple_isset_parameter])) {
                $attributes[$check_simple_isset_parameter] = $params[$check_simple_isset_parameter];
                unset($params[$check_simple_isset_parameter]);
            }
        }

        if (isset($params['container'])) {
            $attributes['container'] = $params['container'];
            unset($params['container']);
        } else if ($attributes['type'] == "action") {
            $attributes['container'] = array(
                "th" => array(
                    "class" => "center header"
                )
            );
        } else {
            $attributes['container'] = array(
                "th" => array(
                    "class" => "default header"
                )
            );
        }

        // additional attributes
        $additional_attributes = $this->filterAdditionalAttributes($params);

        // usability adjust
        if ($attributes['type'] == 'coordinates') {
            $attributes['type'] = 'text';
            $attributes['class'] = 'coordinates';
        }

        $html = '';
        $html .= $this->containerStart($attributes['container']);
        $html .= $this->twig->render(
            $this->template_path."/header_element.twig",
            array(
                'attributes'            => $attributes,
                'additional_attributes' => $additional_attributes
            )
        );
        $html .= $this->containerEnd($attributes['container']);

        return $html;
    }

    /**
     * FormBuilder::cell()
     * Creates & returns a html cell.
     * @param mixed[] $params
     * @return string $html
     */
    public function cell($params, $model)
    {
        $this->model = $model;
        $attributes = $this->input_default_params;

        // process params
        if (!is_array($params)) {
            $attributes['attr_name'] = $params;
            $params = array();
        } else {
            $attributes['attr_name'] = key($params);
            $params = $params[$attributes['attr_name']];
        }

        if (!empty($params['type'])) {
            $attributes['type'] = $params['type'];
            unset($params['type']);
        }

        if (isset($params['container'])) {
            $attributes['container'] = $params['container'];
            unset($params['container']);
        } else if ($attributes['type'] == "action") {
            $attributes['container'] = array(
                "td" => array(
                    "class" => "center cell"
                )
            );
        } else {
            $attributes['container'] = array(
                "td" => array(
                    "class" => "default cell"
                )
            );
        }

        // ACTION ADJUST
        if ($attributes['type'] == 'action') {
            if ($attributes['attr_name'] == "edit") {
                $attributes['class'] = "editBtn";
                $attributes['value'] = "<i class=\"fa fa-pencil\"></i>";
                $attributes['href'] = ModuleHelper::generateNondestructiveUrl(
                    [
                        "id"=>"{{id}}"
                    ],
                    [],
                    "edit.php"
                );
            } elseif ($attributes['attr_name'] == "delete") {
                $attributes['class'] = "deleteBtn alert";
                $attributes['value'] = "<i class=\"fa fa-trash\"></i>";
                $attributes['href'] = ModuleHelper::generateNondestructiveUrl(
                    [
                        "action" => "delete",
                        "action_id" => "{{id}}"
                    ],
                    [],
                    "manage.php"
                );
                $attributes['title'] = 'Are you sure you want to delete this ' . MODEL_SINGULAR . '?';
            } elseif ($attributes['attr_name'] == "order") {
                $attributes['class'] = "orderHandle";
                $attributes['value'] = "<i class=\"fa fa-arrows\"></i>";
                $attributes['href'] = "#";
            } else if ($attributes['attr_name'] == "edit_relation") {
                $attributes['class'] = "editBtn";
                $attributes['value'] = "<i class=\"fa fa-pencil\"></i>";
                $attributes['href'] = ModuleHelper::generateNondestructiveUrl(
                    [
                        "id"=>"{{id}}"
                    ],
                    [],
                    "edit_relation.php"
                );
            } elseif ($attributes['attr_name'] == "delete_relation") {
                $attributes['class'] = "deleteBtn alert";
                $attributes['value'] = "<i class=\"fa fa-trash\"></i>";
                $attributes['href'] = ModuleHelper::generateNondestructiveUrl(
                    [
                        "action"=>"delete",
                        "action_id"=>"{{id}}"
                    ],
                    [],
                    "manage_relation.php"
                );
                $attributes['title'] = 'Are you sure you want to delete this ' . MODEL_SINGULAR . ' relation?';
            }
        }

        $attributes['scoped_name'] = $this->input_default_params['scoped_name'];
        if (isset($params['scoped_name'])) {
            $attributes['scoped_name'] = $params['scoped_name'];
            unset($params['scoped_name']);
        } elseif (!empty($params['type']) && $params['type'] === 'submit') {
            $attributes['scoped_name'] = false;
        }

        // if there is no model - it can't be scoped
        if (empty($this->model)) {
            $attributes['scoped_name'] = false;
        }

        if ($attributes['scoped_name']) {
            $attributes['input_name'] = $this->model_name.'['.$attributes['attr_name'].']';
        } else {
            $attributes['input_name'] = $attributes['attr_name'];
        }

        $attributes['label'] = $this->labelFromAttrName($attributes['attr_name']);

        $attributes['id'] = $attributes['input_name'];

        $check_simple_isset_parameters = array(
            "label",
            "value",
            "default_value",
            "class",
            "id",
            "preview_image_path",
            "file_path",
            "dynamic_options_select",
            "dynamic_options_filter",
            "dynamic_options_order"
        );
        foreach ($check_simple_isset_parameters as $check_simple_isset_parameter) {
            if (isset($params[$check_simple_isset_parameter])) {
                $attributes[$check_simple_isset_parameter] = $params[$check_simple_isset_parameter];
                unset($params[$check_simple_isset_parameter]);
            }
        }

        $check_simple_not_empty_parameters = array(
            "datetime_db_format",
            "datetime_frontend_format",
            "href",
            "pass_value_through"
        );
        foreach ($check_simple_not_empty_parameters as $check_simple_not_empty_parameter) {
            if (!empty($params[$check_simple_not_empty_parameter])) {
                $attributes[$check_simple_not_empty_parameter] = $params[$check_simple_not_empty_parameter];
                unset($params[$check_simple_not_empty_parameter]);
            }
        }

        // Transfer message to label, where this will be used as the title tag (delete confirmation message)
        // in the template
        if (!empty($attributes['title'])) {
            if (isset($params['title'])) {
                $attributes['label'] = $params['title'];
                unset($params['title']);
            } else {
                $attributes['label'] = $attributes['title'];
            }
            unset($attributes['title']);
        }

        if (isset($params['options']) && !is_array($params['options'])) {
            $attributes['options'] = array();
            $options_object_name = $params['options'];

            $option_lister = $options_object_name::where('id', '>', 0);

            foreach ($attributes['dynamic_options_filter'] as $filter) {
                $option_lister = $option_lister->where($filter[0], $filter[1], $filter[2]);
            }

            foreach ($attributes['dynamic_options_order'] as $order) {
                $option_lister = $option_lister->orderBy($order[0], $order[1]);
            }

            $options_object_raw_list = $option_lister->get();
            foreach ($options_object_raw_list as $key => $options_object_raw_item) {
                $attributes['options'][$options_object_raw_item->id] = '';
                foreach ($attributes['dynamic_options_select'] as $select) {
                    $attributes['options'][$options_object_raw_item->id] .= $options_object_raw_item->{$select}.' ';
                }
            }
            unset($params['options']);
        } elseif (isset($params['options']) && is_array($params['options'])) {
            $attributes['options'] = $params['options'];
            unset($params['options']);
        }

        // additional attributes
        $additional_attributes = $this->filterAdditionalAttributes($params);

        // usability adjust
        if ($attributes['type'] == 'coordinates') {
            $attributes['type'] = 'text';
            $attributes['class'] = 'coordinates';
        }

        // Figure out the value of the input based on all the attributes/params
        if (isset($attributes['value'])) {
            // If 'value' param is passed into the input then display it as it has top priority
            $input_value = $attributes['value'];
        } elseif (!empty($this->model) && isset($this->model->{$attributes['attr_name']})) {
            // Then maybe the model already has a value for this property preset
            $input_value = $this->model->{$attributes['attr_name']};
        } elseif (!empty($this->model) && strpos($attributes['attr_name'], '.') !== false) {
            // Check if we maybe need to look for a relationship
            $attr_name_parts = explode(".", $attributes['attr_name']);
            $input_value = $this->model->{$attr_name_parts[0]}->{$attr_name_parts[1]};
        } elseif (isset($attributes['default_value'])) {
            // Then maybe theres a default value a new model should be using
            $input_value = $attributes['default_value'];
        } else {
            // If none of the above the value is empty
            $input_value = '';
        }

        if (!empty($attributes['pass_value_through'])) {
            if (str_contains($attributes['pass_value_through'], '::')) {
                $pass_value_through = explode("::", $attributes['pass_value_through']);
                $class = $pass_value_through[0];
                $method = $pass_value_through[1];
                $input_value = $class::{$method}($input_value);
            } else {
                $input_value = ($attributes['pass_value_through'])($input_value);
            }
        }

        $html = '';
        $html .= $this->containerStart($attributes['container']);
        if (in_array($attributes['type'], array("select", "checkbox", "datalist"))) {
            if (!isset($attributes['options'])) {
                $attributes['options'] = array('No','Yes');
            }
            $html .= clean_page($attributes['options'][$input_value]);
        } elseif ($attributes['type'] == 'datetime') {
            if (!empty($input_value)) {
                $datetime = date_create_from_format($attributes['datetime_db_format'], $input_value);
                if ($datetime !== false) {
                    $input_value = $datetime->format($attributes['datetime_frontend_format']);
                }
            }

            $html .= clean_page($input_value);
        } elseif ($attributes['type'] == 'image' || $attributes['type'] == 'file' || $attributes['type'] == 'action') {
            $html .= $this->twig->render(
                $this->template_path."/".$attributes['type'].".twig",
                array(
                    'attributes'            => $attributes,
                    'additional_attributes' => $additional_attributes,
                    'input_value'           => $input_value,
                )
            );
        } else {
            $html .= clean_page($input_value);
        }
        $html .= $this->containerEnd($attributes['container']);

        $column_listing = Capsule::schema()->getColumnListing($this->model->getTable());
        foreach ($this->model as $key => $param_value) {
            $html = str_replace("%7B%7B".$key."%7D%7D", $param_value, $html);// url encoded equivalent
            $html = str_replace("{{".$key."}}", $param_value, $html);
        }
        foreach ($column_listing as $key => $column) {
            $html = str_replace("%7B%7B".$column."%7D%7D", $this->model->{$column}, $html);// url encoded equivalent
            $html = str_replace("{{".$column."}}", $this->model->{$column}, $html);
        }
        return $html;
    }
}
