<?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 blobt\widgets;

use Yii;
use yii\base\Widget;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Url;

/**
 * 参照yii\widgets\Menu,根据AdminLTE样式从写的一个小物件
 * @author Blobt
 * @email 380255922@qq.com
 * 使用例子 
 * <?php
 *   echo Menu::widget([
 *       'items' => [
 *           ['label' => 'MAIN NAVIGATION', 'is_header' => true],
 *           ['label' => 'Documentation', 'url' => 'https:www.baidu.com', 'icon' => 'fa-book'],
 *           ['label' => 'Products', 'url' => ['product/index'], 'items' => [
 *                   ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']],
 *                   ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']],
 *               ]],
 *           ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],
 *       ]
 *   ]);
 *   ?>
 * 
 * 
 */
class Menu extends Widget {

    /**
     * @var array 菜单的item数组。
     * 菜单的item同时也是一个数组,结构如下所述:
     * lable: string, optional,指定选项的label
     * encode:boolean, optional,label是否需要转义
     * url: string or array, optional, 生产菜单的路径
     * 产生结果和yii的Url::to()方法一致,并且会套用[[linkTemplate]]模板
     * visible: boolean, optional, 选项是否可见,默认是true(可见)
     * active: boolean or Closure, optional, 是否被选中
     * 如果为true,则会在css中添加[[activeCssClass]]
     * 当使用Closure时候,匿名函数必须是`function ($item, $hasActiveChild, $isItemActive, $widget)`
     * 匿名函数必须返回true或者false
     * 如果不是使用匿名函数,就会使用[[isItemActive]]来判断item是否被选中
     * items: array, optional, 指定子菜单选项,格式和父菜单是一样的
     * template: string, optional,选项的渲染模板
     * submenuTemplate: string, optional, 子菜单渲染模板
     * options: array, optional, 指定html属性
     */
    public $items = [];

    /**
     * @var array html属性,这些指定的属性会加到所有的item中
     */
    public $itemOptions = [];

    /**
     * @var string 链接的渲染模板
     */
    public $linkTemplateWithSub = "<a href=\"{url}\"><i class=\"fa {icon}\"></i><span>{label}</span><span class=\"pull-right-container\"><i class=\"fa fa-angle-left pull-right\"></i></span></a>";
    public $linkTemplateNoSub = "<a href=\"{url}\"><i class=\"fa {icon}\"></i><span>{label}</span></a>";

    /**
     * @var string label的渲染模板
     */
    public $labelTemplate = '{label}';

    /**
     * @var string 子菜单的渲染模板
     */
    public $submenuTemplate = "\n<ul class=\"treeview-menu\">\n{items}\n</ul>\n";

    /**
     * @var boolean label是否要进行html转义
     */
    public $encodeLabels = true;

    /**
     * @var string 被选中的菜单的CSS类
     */
    public $activeCssClass = 'active';

    /**
     * @var string 控制二级菜单开合
     */
    public $menuOpenClass = 'menu-open';

    /**
     * @var boolean 是否根据路由去判断菜单项是否被选中
     */
    public $activateItems = true;

    /**
     * @var boolean 当某一个子菜单的选中时候是否也关联选中父菜单
     */
    public $activateParents = true;

    /**
     * @var boolean 如果item的url没有设置时,是否不显示该item
     */
    public $hideEmptyItems = true;

    /**
     * @var array 菜单容器标签的属性
     */
    public $options = [
        'class' => 'sidebar-menu',
        'data-widget' => 'tree'
    ];

    /**
     * @var string 第一个菜单item的css类
     */
    public $firstItemCssClass;

    /**
     * @var string 最后一个菜单item的css类
     */
    public $lastItemCssClass;

    /**
     * @var string 路由 ,run的时候会自动获取当前路由
     */
    public $route;

    /**
     * @var string $_GET参数
     */
    public $params;

    /**
     * @var string 菜单项默认Icon
     */
    public $defaultIcon = 'fa-circle-o';

    /**
     * 渲染菜单
     */
    public function run() {
        if ($this->route === null && Yii::$app->controller !== null) {
            $this->route = Yii::$app->controller->getRoute();
        }
        if ($this->params === null) {
            $this->params = Yii::$app->request->getQueryParams();
        }

        $items = $this->normalizeItems($this->items, $hasActiveChild);

        if (!empty($items)) {
            $options = $this->options;
            $tag = ArrayHelper::remove($options, 'tag', 'ul');
            return Html::tag($tag, $this->renderItems($items), $options);
        }
    }

    /**
     * 渲染菜单
     * @param array $items
     * @return string 渲染结果
     */
    protected function renderItems($items) {
        $lines = [];
        $n = count($items);
        foreach ($items as $i => $item) {
            /* 获取菜单项的自定义属性 */
            $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', []));
            $tag = ArrayHelper::remove($options, 'tag', 'li');
            $class = [];

            if (isset($item['active'])) {
                $class[] = $this->activeCssClass;
                if (isset($item['items'])) {
                    $class[] = $this->menuOpenClass;
                }
            }
            if ($i === 0 && $this->firstItemCssClass !== null) {
                $class[] = $this->firstItemCssClass;
            }
            if ($i === $n - 1 && $this->lastItemCssClass !== null) {
                $class[] = $this->lastItemCssClass;
            }
            if (isset($item['items'])) {
                $class[] = 'treeview';
            }
            if (isset($item['is_header']) && $item['is_header']) {
                $class[] = "header";
            }

            Html::addCssClass($options, $class);

            $menu = $this->renderItem($item);
            if (!empty($item['items'])) {
                $submenuTemplate = ArrayHelper::getValue($item, 'submenuTemplate', $this->submenuTemplate);
                $menu .= strtr($submenuTemplate, [
                    '{items}' => $this->renderItems($item['items']),
                ]);
            }

            $lines[] = Html::tag($tag, $menu, $options);
        }
        return implode("\n", $lines);
    }

    /**
     * 渲染菜单项
     * @param array $item
     * @return string 渲染结果
     */
    protected function renderItem($item) {
        if (isset($item['url'])) {
            if (isset($item['template'])) {
                $template = $item['template'];
            } else {
                $template = isset($item['items']) ? $this->linkTemplateWithSub : $this->linkTemplateNoSub;
            }
            return strtr($template, [
                '{url}' => Html::encode(Url::to($item['url'])),
                '{label}' => Html::encode($item['label']),
                '{icon}' => Html::encode($item['icon'])
            ]);
        }

        $template = ArrayHelper::getValue($item, 'template', $this->labelTemplate);

        return strtr($template, [
            '{label}' => $item['label'],
        ]);
    }

    /**
     * 判断菜单项是否被选中
     * @param $item array
     * @return boolean $item
     */
    protected function isItemActive($item) {
        if (isset($item['url']) && is_array($item['url']) && isset($item['url'][0])) {
            $route = Yii::getAlias($item['url'][0]);

            if ($route[0] !== '/' && Yii::$app->controller) {
                $route = Yii::$app->controller->module->getUniqueId() . '/' . $route;
            }

            if (ltrim($route, '/') !== $this->route) {
                return false;
            }

            return true;
        }

        return false;
    }

    /**
     * 格式化菜单item
     * @param string $item
     * @param bool $active
     */
    protected function normalizeItems($items, &$active) {
        foreach ($items as $i => $item) {
            /* 去除visible 为 false的item */
            if (isset($item['visible']) && !$item['visible']) {
                unset($items[$i]);
                continue;
            }

            /* 添加默认icon */
            if (!isset($item['icon'])) {
                if (!empty($this->defaultIcon)) {
                    $items[$i]['icon'] = $this->defaultIcon;
                }
            }

            /* 添加label */
            if (!isset($item['label'])) {
                $item['label'] = '';
            }

            /* 转义HTML */
            $encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
            if ($encodeLabel) {
                $items[$i]['label'] = Html::encode($item['label']);
            }

            /* 格式化子菜单 item */
            $hasActiveChild = false;
            if (isset($item['items'])) {
                $items[$i]['items'] = $this->normalizeItems($item['items'], $hasActiveChild);
                if (empty($items[$i]['items']) && $this->hideEmptyItems) {
                    unset($items[$i]['items']);
                }
            }

            /* 处理菜单是否被选中 */
            if (!isset($item['active'])) {
                if ($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item)) {
                    $active = $items[$i]['active'] = true;
                }
            } elseif ($item['active'] instanceof Closure) {
                $active = $items[$i]['active'] = call_user_func($item['active'], $item, $hasActiveChild, $this->isItemActive($item), $this);
            } elseif ($item['active']) {
                $active = true;
            }
        }
        return array_values($items);
    }

}