<?php

namespace Comitium5\DesignerBundle\Manager\Template;

use Comitium5\DesignerBundle\Entity\Template;
use Comitium5\DesignerBundle\Helper\Utils;
use Comitium5\DesignerBundle\Manager\AbstractManager;
use Comitium5\DesignerBundle\Manager\Breakpoint\BreakpointManager;
use Comitium5\DesignerBundle\Manager\Designer\DesignerManager;
use Comitium5\DesignerBundle\Manager\Layout\LayoutManager;
use Comitium5\DesignerBundle\Manager\View\ViewManager;
use Comitium5\DesignerBundle\Model\Interfaces\AbstractFactoryInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PageRepositoryInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Template\TemplateInterface;
use Comitium5\DesignerBundle\Repository\Page\PageDoctrineRepository;
use Comitium5\DesignerBundle\Repository\Page\PageRepository;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;

/**
 * Class TemplateManager
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Manager\Template
 */
class TemplateManager extends AbstractManager
{
    const TWIG_PATH = 'Resources/views/Designer/%s';
    const TWIG_NAMESPACE = '@Comitium/Designer/%s';
    const FOLDER_PERMISSION = 0755;

    /**
     * @var LayoutManager
     */
    private $layoutManager;

    /**
     * @var BreakpointManager
     */
    private $breakpointManager;

    /**
     * @var ViewManager
     */
    private $viewManager;

    /**
     * @var PageRepositoryInterface
     */
    private $pageRepository;

    /**
     * TemplateManager constructor.
     *
     * @param AbstractFactoryInterface $factory
     * @param ObjectRepository $repository
     * @param ObjectManager $objectManager
     * @param LayoutManager $layoutManager
     * @param BreakpointManager $breakpointManager
     * @param ViewManager $viewManager
     * @param PageRepositoryInterface $pageRepository
     */
    public function __construct(
        AbstractFactoryInterface $factory,
        ObjectRepository $repository,
        ObjectManager $objectManager,
        LayoutManager $layoutManager,
        BreakpointManager $breakpointManager,
        ViewManager $viewManager,
        PageRepositoryInterface $pageRepository
    ) {
        $this->factory = $factory;
        $this->repository = $repository;
        $this->objectManager = $objectManager;
        $this->layoutManager = $layoutManager;
        $this->breakpointManager = $breakpointManager;
        $this->viewManager = $viewManager;
        $this->pageRepository = $pageRepository;
    }

    /**
     * @param $data
     * @param bool $update
     *
     * @return array|mixed|void
     */
    public function save($data, $update = false)
    {
        if (is_array($data)) {
            foreach ($data as &$entity) {
                if ($entity->getParent()) {
                    $entity->setGroup(null);
                }
            }
        } else {
            if ($data->getParent()) {
                $data->setGroup(null);
            }
        }

        return parent::save($data, $update);
    }

    /**
     * @param $data
     *
     * @return mixed
     */
    public function update($data)
    {
        if (is_array($data)) {
            foreach ($data as &$entity) {
                try {
                    if ($entity->getUuid() === null) {
                        $entity->setUuid(Utils::uuidV4());
                    }
                } catch (\Exception $e) {
                    return false;
                }
            }
        } else {
            try {
                if ($data->getUuid() === null) {
                    $data->setUuid(Utils::uuidV4());
                }
            } catch (\Exception $e) {
                return false;
            }
        }

        return parent::update($data);
    }

    /**
     * @param  mixed $data
     * @param  TemplateGroupManager $templateGroupManager
     *
     * @return TemplateInterface
     */
    public function fromArray($data, TemplateGroupManager $templateGroupManager)
    {
        try {
            $template = $this->find(
                !empty($data['id']) ? $data['id'] : 0
            );
        } catch (\Exception $e) {
            $template = $this->getFactory()->create();
        }

        $dateFields = ['createdAt', 'updatedAt'];

        $specificFields = ['group', 'parent', 'children', 'layouts', 'breakpoints', 'css'];

        foreach (array_merge($specificFields, $dateFields) as $field) {
            if (isset($data[$field])) {
                if (in_array($field, $dateFields)) {
                    $data[$field] = Utils::getDateTime($data[$field]);
                } else {
                    $$field = $data[$field];
                    unset($data[$field]);
                }
            }
        }

        $template->fromArray($data);

        if (!empty($group)) {
            $templateGroup = $templateGroupManager
                ->fromArray($group);
            $template->setGroup($templateGroup);
        }

        if (isset($children)) {
            foreach ($children as $childData) {
                $child = $this->fromArray($childData, $templateGroupManager);
                $template->addChild($child);
            }
        }

        if (isset($layouts)) {
            $template
                ->getLayouts()
                ->clear();

            foreach ($layouts as $layoutData) {
                $layout = $this
                    ->layoutManager
                    ->fromArray($layoutData);

                if (!$layout->getId()) {
                    $this
                        ->objectManager
                        ->persist($layout);
                }

                $template->addLayout($layout);
            }
        }

        if (isset($breakpoints)) {
            foreach ($breakpoints as $breakpointData) {
                $breakpoint = $this->breakpointManager
                    ->fromArray($breakpointData);
                $template->addBreakpoint($breakpoint);
            }
        }

        return $template;
    }

    /**
     * @param TemplateInterface $template
     * @param string $code
     * @param bool $temporary (optional)
     *
     * @return bool
     */
    public function saveCode(TemplateInterface $template, $code, $temporary = false)
    {
        if ($template->getParent()) {
            $path = sprintf(
                self::TWIG_NAMESPACE,
                $template
                    ->getParent()
                    ->getFilePath($temporary)
            );

            $code = "{% extends '".$path."' %}\n".$code;
        }

        return $this->saveFile(
            $template, $code, $temporary
        );
    }

    /**
     * @param TemplateInterface $template
     * @param $code
     * @param $temporary
     *
     * @return bool
     */
    public function saveFile(TemplateInterface $template, $code, $temporary)
    {
        try {
            $this
                ->viewManager
                ->create(
                    sprintf(
                        self::TWIG_NAMESPACE,
                        $template->getFilePath($temporary)
                    ),
                    $code,
                    $template
                );
        } catch (\Exception $exception) {
            return false;
        }

        return true;
    }

    /**
     * @param  TemplateInterface $template
     * @param  bool $temporary (optional)
     *
     * @return bool
     */
    public function fileExists(TemplateInterface $template, $temporary = false)
    {
        $view = $this
            ->viewManager
            ->findByName($this->buildFilePath($template));

        return $view instanceof ViewInterface;
    }

    /**
     * @param TemplateInterface $template
     *
     * @return bool
     */
    public function checkDeletion(TemplateInterface $template)
    {
        $pages = $this->pageRepository->findByTemplate($template);

        return count($pages) > 0 ? false : true;
    }

    /**
     * @param TemplateInterface $template
     *
     * @return string
     */
    public function buildFilePath(TemplateInterface $template)
    {
        return sprintf(
            DesignerManager::BUNDLE_TWIG_PATH,
            $template->getFilePath()
        );
    }

    /**
     * @param TemplateInterface $template
     *
     * @return bool
     */
    public function existFilePath(TemplateInterface $template)
    {
        return $this->fileExists($template);
    }

}
