<?php

/**
 * This file contains \QUI\Bricks\Brick
 */

namespace QUI\Bricks;

use QUI;

/**
 * Class Brick
 * A Brick from the Brickmanager
 *
 * @author  www.pcsg.de (Henning Leutz)
 * @package quiqqer/bricks
 */
class Brick extends QUI\QDOM
{
    /**
     * internal brick id
     *
     * @var
     */
    protected $id = false;

    /**
     * internal unique ID
     * This ID is unique for the complete system
     *
     * @var string
     */
    protected $uniqueId = false;

    /**
     * Brick settings
     *
     * @var array
     */
    protected $settings = [];

    /**
     * Fields can be overwritten by another user
     *
     * @var array
     */
    protected $customfields = [];

    /**
     * Internal control
     *
     * @var null|QUI\Control
     */
    protected $Control = null;

    /**
     * List of extra css classes
     *
     * @var array
     */
    protected $cssClasses = [];

    /**
     * @var string
     */
    protected $hash;

    /**
     * Constructor
     *
     * @param array $params - brick params
     */
    public function __construct($params = [])
    {
        // default
        $default = [
            'type'          => 'content',
            'content'       => '',
            'title'         => '',
            'description'   => '',
            'project'       => '',
            'lang'          => '',
            'areas'         => '',
            'height'        => '',
            'width'         => '',
            'classes'       => '',
            'frontendTitle' => '',
            'hasContent'    => 1,
            'cacheable'     => 1    // if the brick is cacheable or not
        ];

        $this->setAttributes($default);

        foreach ($default as $key => $value) {
            if (isset($params[$key])) {
                $this->setAttribute($key, $params[$key]);
            }
        }

        if (isset($params['id'])) {
            $this->id = (int)$params['id'];
        }

        if (isset($params['uniqueId'])) {
            $this->uniqueId = $params['uniqueId'];
        }

        if (isset($params['classes'])) {
            $cssClasses = \json_decode($params['classes'], true);

            if (!$cssClasses) {
                $cssClasses = [];
            }

            foreach ($cssClasses as $cssClass) {
                $this->addCSSClass($cssClass);
            }

            unset($params['classes']);
        }

        // default settings from control
        $Control = $this->getControl();
        $Manager = Manager::init();

        $availableSettings = $Manager->getAvailableBrickSettingsByBrickType(
            $this->getAttribute('type')
        );

        foreach ($availableSettings as $entry) {
            $this->settings[$entry['name']] = false;
        }

        $availableAttributes = Utils::getAttributesForBrick($this);

        foreach ($availableAttributes as $attribute) {
            $this->settings[$attribute] = false;
        }


        // control default settings
        if (\is_object($Control)) {
            $controlSettings = $Control->getAttributes();

            foreach ($this->settings as $key => $value) {
                if (isset($controlSettings[$key])) {
                    $this->settings[$key] = $controlSettings[$key];
                }
            }
        }

        // settings from database
        if (isset($params['settings'])) {
            $settings = $params['settings'];

            if (\is_string($settings)) {
                $settings = \json_decode($settings, true);
            }

            if (\is_array($settings)) {
                foreach ($this->settings as $key => $value) {
                    if (isset($settings[$key])) {
                        $this->settings[$key] = $settings[$key];
                    }
                }
            }
        }

        // customfields
        if (isset($params['customfields'])) {
            $customfields = $params['customfields'];

            if (\is_string($customfields)) {
                $customfields = \json_decode($customfields, true);
            }

            if (\is_array($customfields)) {
                $this->customfields = $customfields;
            }
        }

        $this->hash = $this->createBrickHash();
    }

    /**
     * Return the class type
     *
     * @return String
     */
    public function getType()
    {
        $Control = $this->getControl();

        if (\is_object($Control)) {
            return \get_class($Control);
        }

        return \get_class($this);
    }

    /**
     * Checks if the internal control is of this class or has this class as one of its parents
     *
     * @param string $className
     * @return bool
     */
    public function isInstanceOf($className)
    {
        $Control = $this->getControl();

        if (\is_object($Control)) {
            return $Control instanceof $className;
        }

        return $this instanceof $className;
    }

    /**
     * Check, if control canbe created
     *
     * @return QUI\Bricks\Brick
     * @throws QUI\Exception
     */
    public function check()
    {
        if ($this->getAttribute('type') == 'content') {
            return $this;
        }

        $Control = $this->getControl();

        if (!$Control) {
            throw new QUI\Exception(
                'Control not found. Brick could not be created. Maybe wrong type '.$this->getAttribute('type')
            );
        }

        return $this;
    }

    /**
     * Create a unique brick hash
     * which is created from the brick data
     *
     * @return string
     */
    protected function createBrickHash()
    {
        $attributes = $this->getAttributes();
        $hashParams = [];

        foreach ($attributes as $name => $value) {
            if (\is_object($value)) {
                continue;
            }

            $hashParams[$name] = \serialize($value);
        }

        $hash = \serialize($hashParams);
        $hash = \md5($hash);

        return $hash;
    }

    /**
     * Return the HTML of the Brick
     *
     * @return string
     *
     * @throws QUI\Exception
     */
    public function create()
    {
        $cacheName = Manager::getBrickCacheNamespace()
                     .\md5($this->getType())
                     .'/'
                     .$this->hash;

        if ($this->getAttribute('cacheable')) {
            try {
                $data       = QUI\Cache\Manager::get($cacheName);
                $cssFiles   = $data['cssFiles'];
                $cssClasses = $data['cssClasses'];

                if (\is_array($cssClasses)) {
                    foreach ($cssClasses as $cssClass) {
                        $this->addCSSClass($cssClass);
                    }
                }

                if (\is_array($cssFiles)) {
                    foreach ($cssFiles as $cssFile) {
                        QUI\Control\Manager::addCSSFile($cssFile);
                    }
                }

                if (!empty($data['html'])) {
                    return $data['html'];
                }
            } catch (\Exception $Exception) {
            }
        }

        if ($this->getAttribute('type') == 'content') {
            $_classes = [
                'brick-'.$this->id
            ];

            //check if is json
            if (\is_string($this->cssClasses)) {
                $jsonArray = \json_decode($this->cssClasses);

                if (\is_array($jsonArray)) {
                    $this->cssClasses = $jsonArray;
                }
            }

            foreach ($this->cssClasses as $cssClass) {
                $_classes[] = $cssClass;
            }


            $oldCssClasses     = $this->getAttribute('classes');
            $oldCssClassesJson = null;

            if (\is_string($oldCssClasses)) {
                $oldCssClassesJson = \json_decode($oldCssClasses, true);
            }

            if (\is_array($oldCssClassesJson)) {
                $oldCssClasses = $oldCssClassesJson;
            }

            $classes = $oldCssClasses;

            if (\is_string($oldCssClasses)) {
                $classes = \explode(' ', $oldCssClasses);
            }

            foreach ($classes as $class) {
                $_classes[] = \trim($class);
            }

            $_classes   = \array_unique($_classes);
            $classesStr = \implode(' ', $_classes);
            $classesStr = 'class="'.$classesStr.'"';

            $Engine = QUI::getTemplateManager()->getEngine();

            $Engine->assign([
                'this'       => $this,
                'classesStr' => $classesStr
            ]);

            $result = $Engine->fetch(\dirname(__FILE__).'/Brick.html');

            QUI\Cache\Manager::set($cacheName, [
                'html'       => $result,
                'cssClasses' => $this->cssClasses,
                'cssFiles'   => []
            ]);

            return $result;
        }

        $Control = $this->getControl();

        if (!$Control) {
            throw new QUI\Exception('Control not found. Brick could not be created');
        }

        $Control->setAttributes($this->getSettings());

        if ($this->getAttribute('classes')) {
            $Control->addCSSClass($this->getAttribute('classes'));
        }

        if ($Control->existsAttribute('cacheable')) {
            $Control->setAttribute('cacheable', $Control->getAttribute('cacheable'));
        }

        // workaround wegen title bug
        // @todo backendTitle einführen und title als frontend Title nutzen (Versionssprung)
        $Control->setAttribute('title', $this->getAttribute('frontendTitle'));

        if ($this->id) {
            $Control->addCSSClass('brick-'.$this->id);
            $Control->setAttribute('data-brickid', $this->id);
        }

        if ($this->uniqueId) {
            $Control->setAttribute('data-brickuid', $this->uniqueId);
        }

        foreach ($this->cssClasses as $cssClass) {
            $Control->addCSSClass($cssClass);
        }

        $result   = $Control->create();
        $cssFiles = $Control->getCSSFiles();

        QUI\Cache\Manager::set($cacheName, [
            'html'       => $result,
            'cssClasses' => $this->cssClasses,
            'cssFiles'   => $cssFiles
        ]);

        return $result;
    }

    /**
     * Return the internal control
     *
     * @return QUI\Control|Bool
     */
    protected function getControl()
    {
        if ($this->Control) {
            return $this->Control;
        }

        $Ctrl = $this->getAttribute('type');

        if ($Ctrl === 'content') {
            return true;
        }

        if (!\is_callable($Ctrl) && !\class_exists($Ctrl)) {
            return false;
        }

        /* @var $Control \QUI\Control */
        $Control = new $Ctrl(
            \array_merge($this->getSettings(), $this->getAttributes())
        );

        $Control->setAttribute('height', $this->getAttribute('height'));
        $Control->setAttribute('width', $this->getAttribute('width'));
        $Control->setAttribute('content', $this->getAttribute('content'));

        if ($this->getAttribute('Site')) {
            $Control->setAttribute('Site', $this->getAttribute('Site'));
        } else {
            $Control->setAttribute('Site', QUI::getRewrite()->getSite());
        }


        if (!($Control instanceof QUI\Control) || !$Control) {
            return false;
        }

        $this->Control = $Control;

        if ($this->Control->existsAttribute('cacheable')) {
            $this->setAttribute('cacheable', $this->Control->getAttribute('cacheable'));
        }

        return $Control;
    }

    /**
     * Return the brick settings
     *
     * @return array
     */
    public function getSettings()
    {
        $this->settings['classes'] = $this->getCSSClasses();

        return $this->settings;
    }

    /**
     * Set brick settings
     *
     * @param array $settings - list of settings
     *
     * @return void
     */
    public function setSettings($settings)
    {
        foreach ($settings as $key => $value) {
            if ($key === 'classes') {
                $this->clearCSSClasses();
                $this->addCSSClass($value);
                continue;
            }
            $this->setSetting($key, $value);
        }
    }

    /**
     * Return the setting of the brick
     *
     * @param String $name - Name of the setting
     *
     * @return boolean|string|array
     */
    public function getSetting($name)
    {
        if ($name === 'classes') {
            return $this->getCSSClasses();
        }

        if (isset($this->settings[$name])) {
            return $this->settings[$name];
        }

        return false;
    }

    /**
     * Set a brick setting
     *
     * @param string $name - name of the setting
     * @param string $value - value of the setting
     *
     * @return void
     */
    public function setSetting($name, $value)
    {
        if (isset($this->settings[$name])) {
            $this->settings[$name] = $value;
        }

        if ($this->Control && $this->Control instanceof QUI\Control) {
            $this->Control->setAttribute($name, $value);
        }
    }

    /**
     * @param string $name
     * @return array|mixed
     */
    public function getAttribute($name)
    {
        if ($name === 'classes') {
            return $this->getCSSClasses();
        }

        return parent::getAttribute($name);
    }

    /**
     * @return array
     */
    public function getAttributes()
    {
        $attributes            = parent::getAttributes();
        $attributes['classes'] = $this->getCSSClasses();

        return $attributes;
    }

    /**
     * This fields can be overwritten by another user
     *
     * @return array
     */
    public function getCustomFields()
    {
        return $this->customfields;
    }

    /**
     * Add an extra CSS Class to the control
     *
     * @param string $cssClass - Name of the CSS Class
     *
     * @return void
     */
    public function addCSSClass($cssClass)
    {
        if (\is_array($cssClass)) {
            $cssClass = \implode(' ', $cssClass);
        }

        if (!\is_string($cssClass)) {
            return;
        }

        if (empty($cssClass)) {
            return;
        }

        $classes = QUI\ControlUtils::clearClassName($cssClass);
        $classes = \explode(' ', $classes);

        $keys = \array_flip($this->cssClasses);

        foreach ($classes as $cssClass) {
            if (!isset($keys[$cssClass])) {
                $this->cssClasses[] = $cssClass;
                $keys[$cssClass]    = true;
            }
        }
    }

    /**
     * Remove all css classes
     */
    public function clearCSSClasses()
    {
        $this->cssClasses = [];
    }

    /**
     * Return all css classes
     *
     * @return array
     */
    public function getCSSClasses()
    {
        return $this->cssClasses;
    }

    /**
     * Match pattern agains the css classes
     *
     * @param string $pattern - The shell wildcard pattern.
     *
     * @return boolean
     */
    public function hasCSSClass($pattern)
    {
        $cssClasses = $this->getAttribute('classes');

        if (\is_array($cssClasses)) {
            $cssClasses = \implode(' ', $cssClasses);
        }

        if ($cssClasses && \fnmatch($pattern, $cssClasses)) {
            return true;
        }

        if (empty($this->cssClasses)) {
            return false;
        }

        foreach ($this->cssClasses as $cssClass) {
            if (\fnmatch($pattern, $cssClass)) {
                return true;
            }
        }

        return false;
    }
}