<?php

namespace Croogo\Blocks\View\Helper;

use Cake\Event\Event;
use Cake\Log\LogTrait;
use Cake\Utility\Hash;
use Cake\View\Helper;
use Croogo\Blocks\Model\Entity\Block;
use Croogo\Core\Croogo;

/**
 * Regions Helper
 *
 * @category Helper
 * @package  Croogo.Blocks.View.Helper
 * @version  1.0
 * @author   Fahad Ibnay Heylaal <contact@fahad19.com>
 * @license  http://www.opensource.org/licenses/mit-license.php The MIT License
 * @link     http://www.croogo.org
 */
class RegionsHelper extends Helper
{

    use LogTrait;

    /**
     * Region is empty
     *
     * returns true if Region has no Blocks.
     *
     * @param string $regionAlias Region alias
     * @return bool
     */
    public function isEmpty($regionAlias)
    {
        if (!empty($this->_View->viewVars['blocksForLayout'][$regionAlias])) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Show Block
     *
     * By default block is rendered using Blocks.block element. If `Block.element` is
     * set and exists, the render process will pass it through given element before wrapping
     * it inside the Blocks.block container. You disable the wrapping by setting
     * `enclosure=false` in the `params` field.
     *
     * @param Block $block Block
     * @param string $regionAlias Alias of the region
     * @param array $options
     * @return string
     */
    public function block(Block $block, $regionAlias = null, $options = [])
    {
        $output = '';

        $options = Hash::merge([
            'elementOptions' => [],
        ], $options);
        $elementOptions = $options['elementOptions'];

        $defaultElement = 'Croogo/Blocks.block';

        $element = $block->element;
        $exists = $this->_View->elementExists($element);

        $event = Croogo::dispatchEvent('Helper.Regions.beforeSetBlock', $this->_View, [
            'content' => $block->body,
        ]);
        $block->body = $event->getData()['content'];

        if ($exists) {
            $blockOutput = $this->_View->element($element, compact('block'), $elementOptions);
        } else {
            if (!empty($element)) {
                $this->log(sprintf(
                    'Missing element `%s` in block `%s` (%s)',
                    $block->element,
                    $block->alias,
                    $block->id
                ), LOG_WARNING);
            }
            $blockOutput = $this->_View->element(
                $defaultElement,
                compact('block'),
                ['ignoreMissing' => true] + $elementOptions
            );
        }

        if ($block->get('cell')) {
            $parts = explode('::', $block->get('cell'));

            if (count($parts) === 2) {
                list($pluginAndCell, $action) = [$parts[0], $parts[1]];
            } else {
                list($pluginAndCell, $action) = [$parts[0], $regionAlias];
            }

            $blockOutput = (string)$this->_View->cell($pluginAndCell . '::' . $action, [], $block->params);
        }

        Croogo::dispatchEvent('Helper.Regions.afterSetBlock', $this->_View, [
            'content' => &$blockOutput,
        ]);

        $enclosure = isset($block['Params']['enclosure']) ? $block['Params']['enclosure'] === "true" : true;
        if ($exists && $element != $defaultElement && $enclosure) {
            $block->body = $blockOutput;
            $block->element = null;
            $output .= $this->_View->element($defaultElement, compact('block'), $elementOptions);
        } else {
            $output .= $blockOutput;
        }

        return $output;
    }

    /**
     * Show Blocks for a particular Region
     *
     * By default block are rendered using Blocks.block element. If `Block.element` is
     * set and exists, the render process will pass it through given element before wrapping
     * it inside the Blocks.block container. You disable the wrapping by setting
     * `enclosure=false` in the `params` field.
     *
     * @param string $regionAlias Region alias
     * @param array $options
     * @return string
     */
    public function blocks($regionAlias, $options = [])
    {
        $output = '';
        if ($this->isEmpty($regionAlias)) {
            return $output;
        }

        $options = Hash::merge([
            'elementOptions' => [],
        ], $options);

        $defaultElement = 'Croogo/Blocks.block';
        $blocks = $this->_View->viewVars['blocksForLayout'][$regionAlias];
        foreach ($blocks as $block) {
            $output .= $this->block($block, $regionAlias, $options);
        }

        return $output;
    }

    /**
     * @return array
     */
    public function implementedEvents()
    {
        $events = parent::implementedEvents();
        $events['Helper.Layout.beforeFilter'] = [
            'callable' => 'filter',
            'passParams' => true,
        ];

        return $events;
    }

    /**
     * Filter content for Scripts and css tags
     *
     * Replaces [region:alias]
     *
     * @param Event $event
     * @return string
     */
    public function filter(Event $event)
    {
        preg_match_all('/\[(region):([A-Za-z0-9_\-]*)(.*?)\]/i', $event->getData('content'), $tagMatches);
        for ($i = 0, $ii = count($tagMatches[1]); $i < $ii; $i++) {
            $regex = '/(\S+)=[\'"]?((?:.(?![\'"]?\s+(?:\S+)=|[>\'"]))+.)[\'"]?/i';
            preg_match_all($regex, $tagMatches[3][$i], $attributes);
            $regionAlias = $tagMatches[2][$i];
            $options = [];
            for ($j = 0, $jj = count($attributes[0]); $j < $jj; $j++) {
                $options[$attributes[1][$j]] = $attributes[2][$j];
            }
            $options = Hash::expand($options);
            $event->data['content'] = str_replace($tagMatches[0][$i], $this->blocks($regionAlias, $options), $event->data['content']);
        }

        return $event->getData();
    }
}
