<?php

namespace Comitium5\DesignerBundle\Entity;

use Comitium5\DesignerBundle\Model\Interfaces\Css\CssInterface;
use Comitium5\DesignerBundle\Model\Interfaces\LibraryJS\LibraryJSInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Template\TemplateGroupInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Template\TemplateInterface;
use Comitium5\DesignerBundle\Model\TemplateTypes;
use Comitium5\DesignerBundle\Model\Traits\Breakpoint\BreakpointCollectionTrait;
use Comitium5\DesignerBundle\Model\Traits\Css\CssCollectionTrait;
use Comitium5\DesignerBundle\Model\Traits\DateTimeTrait;
use Comitium5\DesignerBundle\Model\Traits\EnabledTrait;
use Comitium5\DesignerBundle\Model\Traits\HierarchicalTrait;
use Comitium5\DesignerBundle\Model\Traits\Layout\LayoutCollectionTrait;
use Comitium5\DesignerBundle\Model\Traits\LibraryJSCollectionTrait;
use Comitium5\DesignerBundle\Model\Traits\PublishDateTrait;
use Comitium5\DesignerBundle\Model\Traits\SortableTrait;
use Comitium5\DesignerBundle\Model\Traits\TmpIdentifierTrait;
use Comitium5\DesignerBundle\Model\Traits\View\ViewCollectionTrait;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

/**
 * Class Template.
 *
 * @author Oscar Jimenez <oscarjg19.developer@gmail.com>
 */
class Template extends AbstractSerializeEntity implements TemplateInterface
{
    use EnabledTrait,
        SortableTrait,
        DateTimeTrait,
        HierarchicalTrait,
        PublishDateTrait,
        LayoutCollectionTrait,
        BreakpointCollectionTrait,
        LibraryJSCollectionTrait,
        TmpIdentifierTrait,
        ViewCollectionTrait,
        CssCollectionTrait {
        BreakpointCollectionTrait::getBreakpoints as getOwnBreakpoints;
        CssCollectionTrait::getCss as getOwnCss;
        LibraryJSCollectionTrait::getLibraries as getOwnLibraries;
    }

    const FILE_FOLDER = 'Templates';
    const TMP_FOLDER = 'tmp';
    const FILE_NAME = 'template%d.html.twig';
    const BLOCK_CONTENT = 'cscontent';
    const MAX_BREAKPOINTS_ALLOWED = 4;

    /**
     * @var TemplateGroupInterface
     */
    private $group;
    /**
     * @var string
     */
    private $name;
    /**
     * @var string
     */
    private $description;
    /**
     * @var TemplateTypes
     */
    private $type;
    /**
     * @var float
     */
    private $width;
    /**
     * @var int
     */
    private $columns;
    /**
     * @var float
     */
    private $gutterWidth;
    /**
     * @var string
     */
    private $color;
    /**
     * @var string
     */
    private $html;

    /**
     * @var boolean
     */
    private $useBootstrapRows;

    /**
     * Class constructor.
     */
    public function __construct()
    {
        parent::__construct();
        $this->children = new ArrayCollection();
        $this->layouts = new ArrayCollection();
        $this->breakpoints = new ArrayCollection();
        $this->css = new ArrayCollection();
        $this->libraries = new ArrayCollection();
        $this->views = new ArrayCollection();
    }

    /**
     * @return mixed
     */
    public function getGroup()
    {
        return $this->group;
    }

    /**
     * @param TemplateGroupInterface|null $group
     *
     * @return mixed
     */
    public function setGroup(TemplateGroupInterface $group = null)
    {
        $this->group = $group;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param mixed $name
     *
     * @return mixed
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @param mixed $description
     *
     * @return mixed
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @param mixed $type
     *
     * @return mixed
     */
    public function setType($type)
    {
        $this->type = $type;

        return $this;
    }

    /**
     * @param  bool $parent (optional)
     * @return mixed
     */
    public function getWidth($parent = false)
    {
        $width = $this->width;
        if ($parent && $this->getParent()) {
            $width = $this->getParent()->getWidth($parent);
        }

        return $width;
    }

    /**
     * @param mixed $width
     *
     * @return mixed
     */
    public function setWidth($width)
    {
        $this->width = $width;

        return $this;
    }

    /**
     * @param  bool $parent (optional)
     * @return mixed
     */
    public function getColumns($parent = false)
    {
        $columns = $this->columns;
        if ($parent && $this->getParent()) {
            $columns = $this->getParent()->getColumns($parent);
        }

        return $columns;
    }

    /**
     * @param mixed $columns
     *
     * @return mixed
     */
    public function setColumns($columns)
    {
        $this->columns = $columns;

        return $this;
    }

    /**
     * @param  bool $parent (optional)
     * @return mixed
     */
    public function getGutterWidth($parent = false)
    {
        $gutterWidth = $this->gutterWidth;
        if ($parent && $this->getParent()) {
            $gutterWidth = $this->getParent()->getGutterWidth($parent);
        }

        return $gutterWidth;
    }

    /**
     * @param mixed $gutterWidth
     *
     * @return mixed
     */
    public function setGutterWidth($gutterWidth)
    {
        $this->gutterWidth = $gutterWidth;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getColor()
    {
        return $this->color;
    }

    /**
     * @param mixed $color
     *
     * @return mixed
     */
    public function setColor($color)
    {
        $this->color = $color;

        return $this;
    }

    /**
     * @return string
     */
    public function getHtml()
    {
        return $this->html;
    }

    /**
     * @param string $html
     *
     * @return self
     */
    public function setHtml(string $html)
    {
        $this->html = $html;

        return $this;
    }

    /**
     * @param  bool   $temporary (optional)
     * @return string
     */
    public function getFilePath($temporary = false)
    {
        $path = self::FILE_FOLDER.DIRECTORY_SEPARATOR;
        if ($temporary) {
            $path .= self::TMP_FOLDER.DIRECTORY_SEPARATOR;
        }

        return $path.sprintf(
                self::FILE_NAME, $this->getId()
            );
    }

    /**
     * @param string $code
     */
    public function defineBlockContent(string $code)
    {
        $content = $this->getHtml();
        $start = strpos(
            $content,
            '{% block '.self::BLOCK_CONTENT.' %}'
        );
        if ($start >= 0) {
            $end = strpos(
                $content,
                '{% endblock %}',
                $start
            );
            if ($end >= 0) {
                $end += strlen('{% endblock %}');
                $content = substr($content, 0, $start)
                    .'{% block '.self::BLOCK_CONTENT.' %}'
                    .$code
                    .'{% endblock %}'
                    .substr($content, $end);
            }
        }

        $this->setHtml($content);
    }

    /**
     * @param  bool $parent (optional)
     * @return Collection
     */
    public function getBreakpoints($parent = false)
    {
        $breakpoints = $this->getOwnBreakpoints();
        if ($breakpoints->count() == 0 && $parent && $this->getParent()) {
            $breakpoints = $this->getParent()->getBreakpoints($parent);
        }

        return $breakpoints;
    }

    /**
     * @param  bool $parent (optional)
     * @return Collection
     */
    public function getCss($parent = false, $onlyEntities = false)
    {
        $relations = $this->getOwnCss();
        if ($onlyEntities) {
            $css = new ArrayCollection();
            foreach ($relations as $relation) {
                if (!$relation instanceof CssInterface) {
                    $relation = $relation->getCss();
                }

                $css->add($relation);
            }
        } else {
            $css = $relations;
        }
        if ($parent && $this->getParent()) {
            $css = new ArrayCollection(
                array_merge(
                    $this->getParent()
                        ->getCss($parent, $onlyEntities)
                        ->toArray(),
                    $css->toArray()
                )
            );
        }

        return $css;
    }

    /**
     * @param  bool $parent (optional)
     * @return Collection
     */
    public function getLibraries($parent = false, $onlyEntities = false)
    {
        $relations = $this->getOwnLibraries();
        if ($onlyEntities) {
            $libraries = new ArrayCollection();
            foreach ($relations as $relation) {

                if (!$relation instanceof LibraryJSInterface) {
                    $relation = $relation->getLibraryJS();
                }

                $libraries->add($relation);
            }
        } else {
            $libraries = $relations;
        }
        if ($parent && $this->getParent()) {
            $libraries = new ArrayCollection(
                array_merge(
                    $this->getParent()
                        ->getLibraries($parent, $onlyEntities)
                        ->toArray(),
                    $libraries->toArray()
                )
            );
        }

        return $libraries;
    }

    /**
     * @param ExecutionContextInterface $context
     */
    public function validateBreakPoints(ExecutionContextInterface $context)
    {
        if ($this->parent == null && $this->breakpoints->count() != self::MAX_BREAKPOINTS_ALLOWED) {
            $context
                ->buildViolation('templates.breakpoints', ["%limit%" => self::MAX_BREAKPOINTS_ALLOWED])
                ->atPath('breakpoints')
                ->addViolation();
        }

    }

    /**
     * @param string[] $allowed      optional
     * @param string[] $denied       optional
     * @param string[] $entityFields optional
     *
     * @return mixed[]
     */
    public function toArray($allowed = array(), $denied = array('parent'), $entityFields = array('layouts', 'children', 'breakpoints'))
    {
        return parent::toArray($allowed, $denied, $entityFields);
    }

    /**
     * @return boolean
     */
    public function isUseBootstrapRows()
    {
        return $this->useBootstrapRows;
    }

    /**
     * @param boolean $useBootstrapRows
     * @return $this
     */
    public function setUseBootstrapRows($useBootstrapRows)
    {
        $this->useBootstrapRows = $useBootstrapRows;

        return $this;
    }

    /**
     * @return bool
     */
    public function isAllowedToGenerateTemplate()
    {
        if ($this->getParent() || !$this->isUseBootstrapRows()) {
            return false;
        }

        return true;
    }
}
