<?php

/*
 * The MIT License
 *
 * Copyright 2019 Blobt.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

namespace iron\grid;

use Closure;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\helpers\Html;
use yii\helpers\Json;
use yii\helpers\Url;
use yii\helpers\ArrayHelper;
use yii\i18n\Formatter;
use yii\widgets\BaseListView;
use iron\web\GridViewAsset;
use blobt\grid\DataColumn;

/**
 * @author Blobt
 * @email 380255922@qq.com
 * @created Aug 13, 2019
 */
class GridView extends BaseListView
{

    /**
     * @var string 渲染列数据的类,默认是'yii\grid\DataColumn'
     */
    public $dataColumnClass;

    /**
     * @var array 表格说明的html属性
     */
    public $captionOptions = [];

    /**
     * @var array 表格外层div的属性
     */
    public $options = ['class' => 'card'];

    /**
     * @var array table的html属性
     */
    public $tableOptions = ['class' => 'table table-bordered table-striped dataTable'];

    /**
     * @var array 表格头部html属性
     */
    public $headerRowOptions = [];

    /**
     * @var array 表格脚部html属性
     */
    public $footerRowOptions = [];

    /**
     * @var array|Cloure 表格每一行的html属性
     * 这个参数除了可以是一个options数组外,还可以是一个匿名函数,该函数必须返回一个options数组,
     * 渲染每一行都会调用该函数
     * 该函数必须遵循以下声明规则
     * ```php
     * function ($model, $key, $index, $grid)
     * ```
     *
     * - `$model`: 每行的模型
     * - `$key`: id值
     * - `$index`: [[dataProvider]]提供的索引号
     * - `$grid`: GridView 对象
     */
    public $rowOptions = [];

    /**
     * @var Closure an 一个匿名函数(结构和[[rowOptions]]一样),每行渲染前后都会被调用
     */
    public $beforeRow;
    public $afterRow;

    /**
     * @var bool 是否显示表格头
     */
    public $showHeader = true;

    /**
     * @var bool 是否显示表格脚
     */
    public $showFooter = false;

    /**
     * @var bool 没有数据情况下是否显示
     */
    public $showOnEmpty = true;

    /**
     * @var array|Formatter 用来格式化输出属性值
     */
    public $formatter;

    /**
     * @var string 摘要的显示样式
     *
     *
     * - `{begin}`: 开始条数
     * - `{end}`: 结束条数
     * - `{count}`: 显示条数
     * - `{totalCount}`: 总记录条数
     * - `{page}`: 显示分页
     * - `{pageCount}`: 总分页数
     * - `{select}`: 显示页数
     */
    public $summary = "{select} 显示{begin}~{end}条 共{totalCount}条";

    /**
     * @var array 摘要的html属性
     */
    public $summaryOptions = ['class' => 'summary'];

    /**
     * @var array 列配置数组. 数组每一项代表一个列,列数组可以包括class、attribute、format、label等。
     * 例子:
     * ```php
     * [
     *     ['class' => SerialColumn::className()],
     *     [
     *         'class' => DataColumn::className(), //渲染用到的类,没一列都默认使用[[DataColumn]]渲染,所以这里可以忽略
     *         'attribute' => 'name', //代表每一行的数据原
     *         'format' => 'text', //输出的格式
     *         'label' => 'Name', //label
     *         '' => ''
     *     ],
     *     ['class' => CheckboxColumn::className()],
     * ]
     * ```
     *
     * 当然,也支持简写成这样:[[DataColumn::attribute|attribute]], [[DataColumn::format|format]],
     * 或 [[DataColumn::label|label]] options: `"attribute:format:label"`.
     * 所以上面例子的 "name" 列能简写成这样 : `"name:text:Name"`.
     * 甚至"format"和"label"都是可以不制定的,因为它们都有默认值。
     *
     * 其实大多数情况下都可以使用简写:
     *
     * ```php
     * [
     *     'id',
     *     'amount:currency:Total Amount',
     *     'created_at:datetime',
     * ]
     * ```
     *
     * 当[[dataProvider]]提供active records, 且active record又和其它 active record建立了关联关系的,
     * 例如 the `name` 属性是 和 `author` 关联,那么你可以这样制定数据:
     *
     * ```php
     * // shortcut syntax
     * 'author.name',
     * // full syntax
     * [
     *     'attribute' => 'author.name',
     *     // ...
     * ]
     * ```
     */
    public $columns = [];

    /**
     * @var string 当单元格数据为空时候显示的内容。
     */
    public $emptyCell = '&nbsp;';

    /**
     * @var string TODO:这个目前用来做页数选择,具体原理没有研究清楚
     */
    public $filterSelector = 'select[name="per-page"]';

    /**
     * @var type
     */
    public $filter;

    /**
     * @var array 批量操作的选项
     */
    public $batch;

    /**
     * @var string 表格的layout:
     *
     * - `{summary}`: 摘要.
     * - `{items}`: 表格项.
     * - `{pager}`: 分页.
     * - `{batch}`: 批量处理
     */
    public $layout = <<< HTML
    <div class="card-body">
        <div id="example2_wrapper" class="dataTables_wrapper  dt-bootstrap4">
            <div class="row">
                <div class="col-sm-12 col-md-6">
                    {batch}

                    {create}
<!--                    <a href="#"  data-url='export' class="export btn btn-default"><i class="fa fa-file-excel-o"></i>导出</a>-->
                    {export}
<!--                    <button type="button" id="export" class="btn btn-default"><i class="fa fa-file-excel-o"></i>导出</button>-->
                    {content}
                </div>
                <div class="col-sm-12 col-md-6">
                            {filter}
                </div>
            </div>
            <div class="row">
                <div class="col-sm-12">
                    {items}
                </div>
            </div>
            <div class="row">
                <div class="col-sm-12 col-md-5">
                    <div class="dataTables_length" id="example2_info" role="status" aria-live="polite">
                        {summary}
                    </div>                                     
                </div>
                <div class="col-sm-12 col-md-7">
                    <div class="dataTables_paginate paging_simple_numbers">
                        {pager}
                    </div>
                </div>
            </div>
        </div>
    </div>
HTML;
    public $batchTemplate = <<< HTML
            <div class="btn-group">
                <button type="button" class="btn btn-default btn checkbox-toggle"><i class="far fa-square"></i></button>
                <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">批量操作</button>
                <ul class="dropdown-menu" role="menu">
                    {items}
                </ul>
            </div>
HTML;
    public $export =<<<HTML
           <div class="btn-group">
                   <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="fas fa-file-upload mr-2"></i>导出</button>
                    <ul class="dropdown-menu" role="menu">
                        <li> <a class="dropdown-item export-page" href="#" data-url="{url}/export">本页</a></li>
                        <li> <a class="dropdown-item export-all" href="#" data-url="{url}/export">全部</a></li>
                    </ul>
                </div>
HTML;
    public $create =<<<HTML
                            <a href="create" class="btn btn-default"><i class="fas fa-plus-square mr-2"></i>添加</a>

HTML;
    /**
     * @var 
     * 表单头部内容
     */
    public $content;



    /**
     * 初始化 grid view.
     * 初始化必须的属性和每个列对象
     * @return
     */
    public function init()
    {
        parent::init();
        if ($this->formatter === null) {
            $this->formatter = Yii::$app->getFormatter();
        } elseif (is_array($this->formatter)) {
            $this->formatter = Yii::createObject($this->formatter);
        }
        if (!$this->formatter instanceof Formatter) {
            throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.');
        }
        $this->pager = [
            'options' => ['class' => ['justify-content-end', 'pagination']],
            'linkOptions' => ['class' => 'page-link'],
            'pageCssClass' => 'paginate_button page-item',
            'disabledPageCssClass' => 'page-link disabled',
            'firstPageLabel' => '&laquo;',
            'prevPageLabel' => '&lsaquo;',
            'nextPageLabel' => '&rsaquo;',
            'lastPageLabel' => '&raquo;',];
        $this->initColumns();
    }

    public function run()
    {
        $view = $this->getView();
        GridViewAsset::register($view);
        $this->registerGridJs();
        $this->registerIcheckJs();
        $this->registerConfirmJs();
        $this->registerExportJs();
        parent::run();
    }

    /**
     * 注册GridView Js
     */
    protected function registerGridJs()
    {
        $options = Json::htmlEncode(['filterUrl' => Url::to(Yii::$app->request->url),
            'filterSelector' => $this->filterSelector]);
        $id = $this->options['id'];
        $this->getView()->registerJs("jQuery('#$id').yiiGridView($options);");
    }

    /**
     * 注册icheck Js
     */
    protected function registerIcheckJs()
    {
        $js = <<<SCRIPT
            $('.dataTable input[type="checkbox"]').iCheck({
                checkboxClass: 'icheckbox_flat-blue',
                radioClass: 'iradio_flat-blue'
            });
            $(".checkbox-toggle").click(function () {
                var clicks = $(this).data('clicks');
                if (clicks) {
                  //Uncheck all checkboxes
                  $(".dataTable input[type='checkbox']").iCheck("uncheck");
                  $(".far", this).removeClass("fa-check-square").addClass('fa-square');
                } else {
                  //Check all checkboxes
                  $(".dataTable input[type='checkbox']").iCheck("check");
                  $(".far", this).removeClass("fa-square").addClass('fa-check-square');
                }
                $(this).data("clicks", !clicks);
            });
SCRIPT;
        $this->getView()->registerJs($js);
    }

    /**
     * 注册批量操作js
     */
    protected function registerBatchJs()
    {
        $js = <<<SCRIPT
            $("a.batch_item").click(function(){
                var url = $(this).data("url");
                var act = $(this).text();
                var selected = [];
                
                $(".checked input").each(function(){
                    selected.push($(this).val());
                });
                
                if(selected.length > 0){
                    alertify.confirm('系统提示', "确定执行批量 '"+act+"' 操作?", function(){
                        $.ajax({
                                type: "POST",
                                url: url,
                                traditional:true,
                                data:{ 'ids[]':selected},
                                dataType: "json",
                                async:false
                        });
                        window.location.reload();
                    },function(){
                    
                    });                    
                }
                return false;
            })
SCRIPT;
        $this->getView()->registerJs($js);
    }

    protected function registerConfirmJs()
    {
        $js = <<<SCRIPT
            $("a[alertify-confirm]").click(function(){
                var message = $(this).attr('alertify-confirm');
                var url = $(this).attr('href');
                var id = $(this).data('id');
                alertify.confirm('系统提示', message,function(){
                    $.ajax({
                        type: "POST",
                        url: url,
                        traditional:true,
                        data:{ id:id },
                        dataType: "json",
                        async:false
                    });
                    window.location.reload();
                },function(){
                
                });
                return false;
            });
SCRIPT;
        $this->getView()->registerJs($js);
    }

    protected function registerExportJs()
    {
        $js = <<<SCRIPT
            $("a.export-all").click(function(url){
                var url = $(this).data("url");
            if(!location.search){
                window.location.replace(url+"?page-type=all");
            }else{
                    window.location.replace(url+location.search+"&page-type=all");
                }
            });
            $("a.export-page").click(function(url){
            var url = $(this).data("url")+location.search;
            if(!location.search){
                window.location.replace(url+"?page-type=page");
            }else{
                window.location.replace(url+location.search+"&page-type=page");
                }
            });
SCRIPT;
        $this->getView()->registerJs($js);
    }

    /**
     * 渲染局部
     * @return string|bool
     */
    public function renderSection($name)
    {
        switch ($name) {
            case '{summary}':
                return $this->renderSummary();
            case '{items}':
                return $this->renderItems();
            case '{pager}':
                return $this->renderPager();
            case '{sorter}':
                return $this->renderSorter();
            case '{filter}':
                return $this->renderFilter();
            case '{batch}':
                return $this->renderBatch();
            case '{url}':
                return Yii::$app->request->url;
            case '{export}':
                return $this->renderExport();
            case '{create}':
                return $this->renderCreate();
            case '{content}':
                return $this->renderContent();
            default:
                return false;
        }
    }

    /**
     * 渲染表格的html真实table
     * @return string
     */
    public function renderItems()
    {
        $tableHeader = $this->showHeader ? $this->renderTableHeader() : false;
        $tableBody = $this->renderTableBody();

        $content = array_filter([
            $tableHeader,
            $tableBody
        ]);

        return Html::tag('table', implode("\n", $content), $this->tableOptions);
    }

    /**
     * 初始化每列
     * @throws InvalidConfigException
     */
    protected function initColumns()
    {
        if (empty($this->columns)) {
            throw new InvalidConfigException('The "columns" property must be set.');
        }

        foreach ($this->columns as $i => $column) {
            if (is_string($column)) {
                $column = $this->createDataColumn($column);
            } else {
                $column = Yii::createObject(array_merge([
                    'class' => $this->dataColumnClass ?: DataColumn::className(),
                    'grid' => $this,
                ], $column));
            }
            if (!$column->visible) {
                unset($this->columns[$i]);
                continue;
            }

            $this->columns[$i] = $column;
        }
    }

    /**
     * 渲染表头
     * @return string
     */
    public function renderTableHeader()
    {
        $cells = [];
        foreach ($this->columns as $column) {
            /* @var $column Column */
            $cells[] = $column->renderHeaderCell();
        }

        $content = Html::tag('tr', implode('', $cells), $this->headerRowOptions);
        return "<thead>\n" . $content . "\n</thead>";
    }

    /**
     * 渲染表格体
     * @return string
     */
    public function renderTableBody()
    {
        $models = $this->dataProvider->getModels();
        $keys = $this->dataProvider->getKeys();
        $rows = [];

        foreach ($models as $index => $model) {
            $key = $keys[$index];

            if ($this->beforeRow !== null) {
                $row = call_user_func($this->beforeRow, $model, $key, $index, $this);
                if (!empty($row)) {
                    $rows[] = $row;
                }
            }

            $rows[] = $this->renderTableRow($model, $key, $index);

            if ($this->afterRow !== null) {
                $row = call_user_func($this->afterRow, $model, $key, $index, $this);
                if (!empty($row)) {
                    $rows[] = $row;
                }
            }
        }

        if (empty($rows) && $this->emptyText !== false) {
            $colspan = count($this->columns);

            return "<tbody>\n<tr><td colspan=\"$colspan\">" . $this->renderEmpty() . "</td></tr>\n</tbody>";
        }

        return "<tbody>\n" . implode("\n", $rows) . "\n</tbody>";
    }

    /**
     * 渲染表格的每行
     * @param Objetc $model
     * @param int $key
     * @param int $index
     * @return string
     */
    public function renderTableRow($model, $key, $index)
    {
        $cells = [];
        foreach ($this->columns as $column) {
            $cells[] = $column->renderDataCell($model, $key, $index);
        }
        if ($this->rowOptions instanceof Closure) {
            $options = call_user_func($this->rowOptions, $model, $key, $index, $this);
        } else {
            $options = $this->rowOptions;
        }
        $options['data-key'] = is_array($key) ? json_encode($key) : (string)$key;

        //TODO 各行变色放到这里不合理
        if ($index % 2 == 0) {
            $oddEven = 'odd';
        } else {
            $oddEven = 'even';
        }

        if (isset($options['class'])) {
            $options['class'] += " " . $oddEven;
        } else {
            $options['class'] = $oddEven;
        }

        return Html::tag('tr', implode('', $cells), $options);
    }

    /**
     * 渲染摘要显示
     * @return string
     */
    public function renderSummary()
    {
        $count = $this->dataProvider->getCount();
        if ($count <= 0) {
            return '';
        }
        $summaryOptions = $this->summaryOptions;
        $tag = ArrayHelper::remove($summaryOptions, 'tag', 'div');
        if (($pagination = $this->dataProvider->getPagination()) !== false) {
            $totalCount = $this->dataProvider->getTotalCount();
            $begin = $pagination->getPage() * $pagination->pageSize + 1;
            $end = $begin + $count - 1;
            if ($begin > $end) {
                $begin = $end;
            }
            $page = $pagination->getPage() + 1;
            $pageCount = $pagination->pageCount;
        }

        return Yii::$app->getI18n()->format($this->summary, [
            'begin' => $begin,
            'end' => $end,
            'count' => $count,
            'totalCount' => $totalCount,
            'page' => $page,
            'pageCount' => $pageCount,
            'select' => $this->renderCountSelect()
        ], Yii::$app->language);
    }

    /**
     * 渲染批量操作
     */
    public function renderBatch()
    {
        if (empty($this->batch) && !is_array($this->batch)) {
            return "";
        }

        $this->registerBatchJs();

        $items = "";
        foreach ($this->batch as $item) {
            $items .= Html::tag('li', Html::a(Html::encode($item['label']), '#', ["data-url" => Html::encode($item['url']), "class" => "batch_item dropdown-item"]));
        }

        return strtr($this->batchTemplate, [
            "{items}" => $items
        ]);
    }

    /**
     * 渲染表格的页数select
     * @return string
     */
    protected function renderCountSelect()
    {
        $items = [
            "20" => 20,
            "50" => 50,
            "100" => 100
        ];

        $per = "条/页";

        $options = [];
        foreach ($items as $key => $val) {
            $options[$val] = "{$key}{$per}";
        }

        $perPage = !empty($_GET['per-page']) ? $_GET['per-page'] : 20;
        return Html::dropDownList('per-page', $perPage, $options, ["class" => "custom-select"]);
    }

    /**
     * 渲染表格的筛选部分
     * @return string
     */
    protected function renderFilter()
    {
        return $this->filter;
    }

    /**
     * 根据给定格式,创建一个 [[DataColumn]] 对象
     * @param string $text DataColumn 格式
     * @return DataColumn 实例
     * @throws InvalidConfigException
     */
    protected function createDataColumn($text)
    {
        if (!preg_match('/^([^:]+)(:(\w*))?(:(.*))?$/', $text, $matches)) {
            throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"');
        }

        return Yii::createObject([
            'class' => $this->dataColumnClass ?: DataColumn::className(),
            'grid' => $this,
            'attribute' => $matches[1],
            'format' => isset($matches[3]) ? $matches[3] : 'text',
            'label' => isset($matches[5]) ? $matches[5] : null,
        ]);
    }

    /**
     * 渲染导出部分
     * @return string
     */
    protected function renderExport()
    {
        return $this->export;
    }

    /**
     * 渲染创建部分
     * @return string
     */
    protected function renderCreate()
    {
        return $this->create;
    }

    /**
     * 渲染表单头部内容
     * @return string
     */
    protected function renderContent()
    {
        return Html::tag('div', $this->content, ['class' => 'btn-group']);
    }

}