<?php
namespace Solwin\Verticalmenu\Block\Adminhtml\Menu;

/**
 * @method Tree setUseAjax($useAjax)
 * @method bool getUseAjax()
 */
class Tree extends \Solwin\Verticalmenu\Block\Adminhtml\Menu\AbstractMenu
{
    /**
     * Tree template
     * 
     * @var string
     */
    protected $_template = 'menu/tree.phtml';

    /**
     * JSON Encoder instance
     * 
     * @var \Magento\Framework\Json\EncoderInterface
     */
    protected $jsonEncoder;

    /**
     * Backend Session
     * 
     * @var \Magento\Backend\Model\Auth\Session
     */
    protected $backendSession;

    /**
     * Resource Helper
     * 
     * @var \Magento\Framework\DB\Helper
     */
    protected $resourceHelper;

    /**
     * constructor
     * 
     * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder
     * @param \Magento\Backend\Model\Auth\Session $backendSession
     * @param \Magento\Framework\DB\Helper $resourceHelper
     * @param \Magento\Framework\Registry $registry
     * @param \Solwin\Verticalmenu\Model\ResourceModel\Menu\Tree $menuTree
     * @param \Solwin\Verticalmenu\Model\MenuFactory $menuFactory
     * @param \Solwin\Verticalmenu\Model\ResourceModel\Menu\CollectionFactory $menuCollectionFactory
     * @param \Magento\Backend\Block\Widget\Context $context
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\Json\EncoderInterface $jsonEncoder,
        \Magento\Backend\Model\Auth\Session $backendSession,
        \Magento\Framework\DB\Helper $resourceHelper,
        \Magento\Framework\Registry $registry,
        \Solwin\Verticalmenu\Model\ResourceModel\Menu\Tree $menuTree,
        \Solwin\Verticalmenu\Model\MenuFactory $menuFactory,
        \Solwin\Verticalmenu\Model\ResourceModel\Menu\CollectionFactory $menuCollectionFactory,
        \Magento\Backend\Block\Widget\Context $context,
        array $data = []
    )
    {
        $this->jsonEncoder    = $jsonEncoder;
        $this->backendSession = $backendSession;
        $this->resourceHelper = $resourceHelper;
        parent::__construct($registry, $menuTree, $menuFactory, $menuCollectionFactory, $context, $data);
    }

    /**
     * @return void
     */
    protected function _construct()
    {
        parent::_construct();
        $this->setUseAjax(0);
    }

    /**
     * Add buttons
     *
     * @return $this
     */
    protected function _prepareLayout()
    {
        $addUrl = $this->getUrl("*/*/new", ['_current' => true, 'menu_id' => null, '_query' => false]);

        $this->addChild(
            'add_sub_button',
            'Magento\Backend\Block\Widget\Button',
            [
                'label' => __('Add Child Menu'),
                'onclick' => "addNew('" . $addUrl . "', false)",
                'class' => 'add',
                'id' => 'add_child_menu_button',
                'style' => $this->canAddChildMenu() ? '' : 'display: none;'
            ]
        );

        if ($this->canAddChildMenu()) {
            $this->addChild(
                'add_root_button',
                'Magento\Backend\Block\Widget\Button',
                [
                    'label' => __('Add Root Menu'),
                    'onclick' => "addNew('" . $addUrl . "', true)",
                    'class' => 'add',
                    'id' => 'add_root_menu_button'
                ]
            );
        }
        return parent::_prepareLayout();
    }

    /**
     * @param $menu_titlePart
     * @return string
     */
    public function getSuggestedMenusJson($menu_titlePart)
    {
        /* @var $collection \Solwin\Verticalmenu\Model\ResourceModel\Menu\Collection */
        $collection = $this->menuCollectionFactory->create();

        /* @var $matchingMenu_titleCollection \Solwin\Verticalmenu\Model\ResourceModel\Menu\Collection */
        $matchingMenu_titleCollection = clone $collection;
        $escapedMenu_titlePart = $this->resourceHelper->addLikeEscape(
            $menu_titlePart,
            ['position' => 'any']
        );
        $matchingMenu_titleCollection->addFieldToFilter(
            'menu_title',
            ['like' => $escapedMenu_titlePart]
        )
        ->addFieldToFilter(
            'menu_id',
            ['neq' => \Solwin\Verticalmenu\Model\Menu::TREE_ROOT_ID]
        );

        $shownMenusIds = [];
        foreach ($matchingMenu_titleCollection as $menu) {
            /** @var \Solwin\Verticalmenu\Model\Menu $menu */
            foreach (explode('/', $menu->getPath()) as $parentId) {
                $shownMenusIds[$parentId] = 1;
            }
        }
        $collection->addFieldToFilter(
            'menu_id',
            ['in' => array_keys($shownMenusIds)]
        );

        $menusById = [
            \Solwin\Verticalmenu\Model\Menu::TREE_ROOT_ID => [
                'id' => \Solwin\Verticalmenu\Model\Menu::TREE_ROOT_ID,
                'children' => [],
            ],
        ];
        foreach ($collection as $menu) {
            /** @var \Solwin\Verticalmenu\Model\Menu $menu */
            foreach ([$menu->getId(), $menu->getParentId()] as $menuId) {
                if (!isset($menusById[$menuId])) {
                    $menusById[$menuId] = ['id' => $menuId, 'children' => []];
                }
            }
            $menusById[$menu->getId()]['is_active'] = true;
            $menusById[$menu->getId()]['label'] = $menu->getMenu_title();
            $menusById[$menu->getParentId()]['children'][] = & $menusById[$menu->getId()];
        }
        return $this->jsonEncoder->encode($menusById[\Solwin\Verticalmenu\Model\Menu::TREE_ROOT_ID]['children']);
    }

    /**
     * @return string
     */
    public function getAddRootButtonHtml()
    {
        return $this->getChildHtml('add_root_button');
    }

    /**
     * @return string
     */
    public function getAddSubButtonHtml()
    {
        return $this->getChildHtml('add_sub_button');
    }

    /**
     * @return string
     */
    public function getExpandButtonHtml()
    {
        return $this->getChildHtml('expand_button');
    }

    /**
     * @return string
     */
    public function getCollapseButtonHtml()
    {
        return $this->getChildHtml('collapse_button');
    }


    /**
     * @param bool|null $expanded
     * @return string
     */
    public function getLoadTreeUrl($expanded = null)
    {
        $params = ['_current' => true, 'id' => null, 'store' => null];
        if (is_null($expanded) && $this->backendSession->getSolwinVerticalmenuMenuIsTreeWasExpanded()
            || $expanded == true) {
            $params['expand_all'] = true;
        }
        return $this->getUrl('*/*/menusJson', $params);
    }

    /**
     * @return string
     */
    public function getNodesUrl()
    {
        return $this->getUrl('solwin_verticalmenu/menu/jsonTree');
    }

    /**
     * @return string
     */
    public function getSwitchTreeUrl()
    {
        return $this->getUrl(
            'solwin_verticalmenu/menu/tree',
            ['_current' => true, 'store' => null, '_query' => false, 'id' => null, 'parent' => null]
        );
    }

    /**
     * @return bool
     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
     */
    public function getIsWasExpanded()
    {
        return $this->backendSession->getSolwinVerticalmenuMenuIsTreeWasExpanded();
    }

    /**
     * @return string
     */
    public function getMoveUrl()
    {
        return $this->getUrl('solwin_verticalmenu/menu/move');
    }

    /**
     * @param null $parentNodeMenu
     * @return array
     */
    public function getTree($parentNodeMenu = null)
    {
        $rootArray = $this->getNodeJson($this->getRoot($parentNodeMenu));
        $tree = isset($rootArray['children']) ? $rootArray['children'] : [];
        return $tree;
    }

    /**
     * @param mixed $parentNodeMenu
     * @return string
     */
    public function getTreeJson($parentNodeMenu = null)
    {
        $rootArray = $this->getNodeJson($this->getRoot($parentNodeMenu));
        $json = $this->jsonEncoder->encode(isset($rootArray['children']) ? $rootArray['children'] : []);
        return $json;
    }

    /**
     * Get JSON of array of Menus, that are breadcrumbs for specified Menu path
     *
     * @param string $path
     * @param string $javascriptVarName
     * @return string
     */
    public function getBreadcrumbsJavascript($path, $javascriptVarName)
    {
        if (empty($path)) {
            return '';
        }

        $menus = $this->menuTree->loadBreadcrumbsArray($path);
        if (empty($menus)) {
            return '';
        }
        foreach ($menus as $key => $menu) {
            $menus[$key] = $this->getNodeJson($menus);
        }
        return '<script>require(["prototype"], function(){' . $javascriptVarName . ' = ' . $this->jsonEncoder->encode(
            $menus
        ) .
            ';' .
            ($this->canAddChildMenus() ? '$("add_child_menu_button").show();' : '$("add_child_menu_button").hide();') .
            '});</script>';
    }

    /**
     * Get JSON of a tree node or an associative array
     *
     * @param Node|array $node
     * @param int $level
     * @return string
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    protected function getNodeJson($node, $level = 0)
    {
        // create a node from data array
        if (is_array($node)) {
            $node = new \Magento\Framework\Data\Tree\Node($node, 'menu_id', new \Magento\Framework\Data\Tree());
        }

        $item = [];
        $item['text'] = $this->buildNodeName($node);

        $item['id'] = $node->getId();
        $item['path'] = $node->getData('path');

        $item['cls'] = 'folder ' . 'active-category';
        $allowMove = $this->isMenuMoveable($node);
        $item['allowDrop'] = $allowMove;
        $item['allowDrag'] = $allowMove && ($node->getLevel() == 0 ? false : true);

        if ((int)$node->getChildrenCount() > 0) {
            $item['children'] = [];
        }

        $isParent = $this->isParentSelectedMenu($node);

        if ($node->hasChildren()) {
            $item['children'] = [];
            if (!($this->getUseAjax() && $node->getLevel() > 1 && !$isParent)) {
                foreach ($node->getChildren() as $child) {
                    $item['children'][] = $this->getNodeJson($child, $level + 1);
                }
            }
        }

        if ($isParent || $node->getLevel() < 2) {
            $item['expanded'] = true;
        }

        return $item;
    }

    /**
     * Get Menu Menu Title
     *
     * @param \Magento\Framework\DataObject $node
     * @return string
     */
    public function buildNodeName($node)
    {
        $result = $this->escapeHtml($node->getMenu_title());
        return $result;
    }

    /**
     * @param Node|array $node
     * @return bool
     */
    protected function isMenuMoveable($node)
    {
        $options = new \Magento\Framework\DataObject(['is_moveable' => true, 'menu' => $node]);
        $this->_eventManager->dispatch('adminhtml_solwin_verticalmenu_menu_tree_is_moveable', ['options' => $options]);
        return $options->getIsMoveable();
    }

    /**
     * @param \Magento\Framework\Data\Tree\Node $node
     * @return bool
     */
    protected function isParentSelectedMenu($node)
    {
        if ($node && $this->getMenu()) {
            $pathIds = $this->getMenu()->getPathIds();
            if (in_array($node->getId(), $pathIds)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if page loaded by outside link to Menu edit
     *
     * @return boolean
     */
    public function isClearEdit()
    {
        return (bool)$this->getRequest()->getParam('clear');
    }

    /**
     * Check availability of adding root Menu
     *
     * @return boolean
     */
    public function canAddRootMenu()
    {
        $options = new \Magento\Framework\DataObject(['is_allow' => true]);
        $this->_eventManager->dispatch(
            'adminhtml_solwin_verticalmenu_menu_tree_can_add_root_menu',
            ['menu' => $this->getMenu(), 'options' => $options]
        );

        return $options->getIsAllow();
    }

    /**
     * Check availability of adding child Menu
     *
     * @return boolean
     */
    public function canAddChildMenu()
    {
        $options = new \Magento\Framework\DataObject(['is_allow' => true]);
        $this->_eventManager->dispatch(
            'adminhtml_solwin_verticalmenu_menu_tree_can_add_child_menu',
            ['menu' => $this->getMenu(), 'options' => $options]
        );

        return $options->getIsAllow();
    }
}
