<?php

namespace onespace\tools\widgets\ui;

use Closure;
use onespace\tools\widgets\ui\base\Align;
use Yii;
use yii\grid\GridView;
use yii\widgets\Pjax;

/**
 * Widget for generating Onespace specificly styled GridViews.
 * 
 * PJAX is a built in option, on by default.
 * 
 * @author  Gareth Palmer <gareth@one-space.co.za>
 */

class OneSpaceGridview extends GridView {

    const ROW_OUTLINE_WHITESPACE = 'ui-outline-whitespace';
    const ROW_OUTLINE_MAIN       = 'ui-outline-main';
    const ROW_OUTLINE_ACCENT     = 'ui-outline-accent';
    const ROW_OUTLINE_SMOKEYGREY = 'ui-outline-smokeygrey';
    const ROW_OUTLINE_COOLGREY   = 'ui-outline-coolgrey';
    const ROW_OUTLINE_SNIPR      = 'ui-outline-snipr';

    /**
     * The contents of the header area as desired. Can be any ordinary string, or parsed using a widget, ideally the `ItemHeader` widget.
     * 
     * @var string  $header_area
     * 
     * @access  public
     */

    public string $header_area;

    /**
     * The contents of the header area as desired. Can be any ordinary string, or parsed using widget.
     * 
     * @var string|Closure  $footer_area
     * 
     * @access  public
     */

    public string|Closure $footer_area;

    /**
     * The id of the filter row. If this is set, the filter row is hidden by default, and the id should
     * be passed to the filter button to show / hide the filter.
     * 
     * @var string  $filter_id
     * 
     * @access  public
     */

    public string $filter_id;

    /**
     * @var string|Closure the CSS Class for setting the row outline. This can be either an string (See constants below)
     * specifying the common outline for all body rows, or an anonymous function that returns the specified outline 
     * based on your own parameters. The anonymous function will be called once for every data model returned by 
     * [[dataProvider]]. It should have the following signature:
     *
     * ```php
     * function ($model, $key, $index, $grid)
     * ```
     */

    public string|Closure $row_outline;

    /**
     * The id of the PJAX element if you need to specify one. Otherwise it'll randomly generate.
     * 
     * @var string  $pjax_id
     * 
     * @access  public
     */

    public string $pjax_id;

    /**
     * Whether or not to use PJAX automatically. Set to false to set your own PJAX options.
     * 
     * @var bool    $use_pjax   Default: true
     * 
     * @access  public
     */

    public bool $use_pjax = true;

    /**
     * If desired, the title to be placed into the header. This is just a plain text / html header.
     * 
     * @var string  $title
     * 
     * @access  public
     */

    public string $title;

    /**
     * The desired alignment of the title on the header.
     * 
     * @var Align   $title_position
     * 
     * @access  public
     */

    public Align $title_position = Align::LEFT;

    /**
     * If desired, the button to be placed into the header. This value is specifically the text of the button.
     * 
     * @var array   $button
     * 
     * @access  public
     */

    public array $button;

    /**
     * The desired alignment of the button on the header.
     * 
     * @var Align   $button_position
     * 
     * @access  public
     */

    public Align $button_position = Align::CENTER;

    /**
     * If desired, the filter to be placed into the header. This value is specifically the id of the filter row, which should be hidden / unhidden.
     * 
     * @var string  $filter
     * 
     * @access  public
     */

    public string $filter;

    /**
     * The desired alignment of the filter on the header.
     * 
     * @var Align   $filter_position
     * 
     * @access  public
     */

    public Align $filter_position = Align::RIGHT;

    /**
     * If the alignment does not sufficently lay out the elements as desired, you can parse desired CSS here.
     * 
     * Specifically the value to be placed as a `grid-template-columns` property.
     * 
     * ## Examples
     * 
     * ```php
     *  ... 
     *  'grid_template_columns' => 'repeat(3, 1fr)', // 3 equal elements.
     *  'grid_template_columns' => 'max-content 1fr', // 1 Elements that is max content size, then 1 which fits the rest of the space.
     *  'grid_template_columns' => '200px 300px 200px', // An element of 200px, then one of 300px, then one of 200px again.
     *  ...
     * ```
     * 
     * @var string  $grid_template_columns
     * 
     * @access  public
     */

    public string $grid_template_columns;


    /**
     * {@inheritdoc}
     */

    public function init(): void {
        parent::init();
        if ($this->use_pjax) {
            $this->pjax_id ??= 'pj' . Yii::$app->security->generateRandomString(8);
        }
        $this->layout = "{items} {pager}";
        $this->tableOptions['class'] = 'table ui-table';
        $hold_params = $this->rowOptions ?? [];
        $this->rowOptions = function ($model, $key, $index, $grid) use ($hold_params) {
            $params = ['class' => 'ui-table-row'];
            if (isset($this->row_outline)) {
                if ($this->row_outline instanceof Closure) {
                    $params['class'] .= ' ' . call_user_func($this->row_outline, $model, $key, $index, $this);
                } else {
                    $params['class'] .= " {$this->row_outline}";
                }
            }
            if ($hold_params instanceof Closure) {
                $hold_params = call_user_func($hold_params, $model, $key, $index, $this);
            }
            return array_merge($params, $hold_params);
        };
        if (isset($this->filter)) {
            $this->filter_id ??= $this->filter;
        }
        if (isset($this->filter_id)) {
            if (isset($this->filterModel)) {
                $searchClass = explode('\\', $this->filterModel::class);
                $searchName = $searchClass[array_key_last($searchClass)];
            } else {
                $searchName = '';
            }
            $filterOpen = ($_GET[$this->filter_id . '--filter'] ?? false) && $_GET[$this->filter_id . '--filter'] == 1 || isset($_GET[$searchName]);
            $this->filterRowOptions['id'] = $this->filter_id;
            $this->filterRowOptions['class'] = $filterOpen ? 'filter' : 'filter hidden';
        }
    }


    /**
     * {@inheritdoc}
     */

    public function run() {
        $this->draw_header();
        if ($this->use_pjax) {
            Pjax::begin([
                'id' => $this->pjax_id,
                'linkSelector' => "#{$this->pjax_id} ul.pagination a, th a",
            ]);
        }
        parent::run();
        if ($this->use_pjax) {
            Pjax::end();
        }
        $this->draw_footer();
    }


    /**
     * Daw out the custom header area of the Gridview as desired, followed by a `<hr>`.
     * 
     * @access  private
     */

    private function draw_header(): void {
        $use_header = isset($this->header_area) || isset($this->title) || isset($this->button) || isset($this->filter);
        if ($use_header) {
            if (isset($this->header_area)) {
                echo $this->header_area;
            } else {
                $options = [
                    'title_position' => $this->title_position,
                    'button_position' => $this->button_position,
                    'filter_position' => $this->filter_position,
                ];
                if (isset($this->title)) {
                    $options['title'] = $this->title;
                }
                if (isset($this->button)) {
                    $options['button'] = $this->button;
                }
                if (isset($this->filter)) {
                    $options['filter'] = $this->filter;
                }
                if (isset($this->grid_template_columns)) {
                    $options['grid_template_columns'] = $this->grid_template_columns;
                }
                echo ItemHeader::widget($options);
            }
            echo "<hr class='ui-item-hr'>";
        }
    }


    /**
     * Draw out the custom footer area of the Gridview as desired, preceeded by a `<hr>`.
     * 
     * @access  private
     */

    private function draw_footer(): void {
        if (isset($this->footer_area)) {
            echo "<hr class='ui-item-hr'>";
            if ($this->footer_area instanceof Closure) {
                echo call_user_func($this->footer_area);
            } else {
                echo $this->footer_area;
            }
        }
    }
}
