<?php

namespace Comitium5\DesignerBundle\Manager\Layout;

use Comitium5\DesignerBundle\Factory\AbstractFactory;
use Comitium5\DesignerBundle\Manager\AbstractManager;
use Comitium5\DesignerBundle\Manager\Page\PageManager;
use Comitium5\DesignerBundle\Manager\View\ViewManager;
use Comitium5\DesignerBundle\Model\Interfaces\Layout\LayoutInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PageInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Symfony\Component\HttpKernel\Config\FileLocator;

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

    /**
     * @var LayoutPageManager
     */
    private $layoutPageManager;

    /**
     * @var LayoutBreakpointManager
     */
    private $layoutBreakpointManager;

    /**
     * @var PageManager
     */
    private $pageManager;

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

    /**
     * LayoutManager constructor.
     *
     * @param AbstractFactory $factory
     * @param ObjectRepository $repository
     * @param ObjectManager $objectManager
     * @param LayoutPageManager $layoutPageManager
     * @param LayoutBreakpointManager $layoutBreakpointManager
     * @param PageManager $pageManager
     * @param ViewManager $viewManager
     */
    public function __construct(
        AbstractFactory $factory,
        ObjectRepository $repository,
        ObjectManager $objectManager,
        LayoutPageManager $layoutPageManager,
        LayoutBreakpointManager $layoutBreakpointManager,
        PageManager $pageManager,
        ViewManager $viewManager
    ) {
        $this->factory = $factory;
        $this->repository = $repository;
        $this->objectManager = $objectManager;
        $this->layoutPageManager = $layoutPageManager;
        $this->layoutBreakpointManager = $layoutBreakpointManager;
        $this->pageManager = $pageManager;
        $this->viewManager = $viewManager;
    }

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

        $dateFields = array('createdAt', 'updatedAt');
        $specificFields = array('parent', 'children', 'layoutBreakpoints', 'layoutPages', 'layouts');
        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]);
                }
            }
        }

        $layout->fromArray($data);
        if (!empty($parent)) {
            $entity = $this->fromArray($parent);
            $layout->setParent($entity);
        }
        if (isset($layoutBreakpoints)) {
            $layout->getLayoutBreakpoints()->clear();
            foreach ($layoutBreakpoints as $layoutBreakpointData) {
                $layoutBreakpoint = $this->layoutBreakpointManager
                    ->fromArray($layoutBreakpointData, $this);
                $layoutBreakpoint->setLayout($layout);
                $layout->addLayoutBreakpoint($layoutBreakpoint);
            }
        }
        if (isset($layoutPages)) {
            $currentPageId = (!empty($page) ? $page->getId() : 0);
            $layoutId = $layout->getId();
            if ($layoutId > 0) {
                $currentLayoutPages = $layout->getLayoutPages()
                    ->filter(function($item) use ($currentPageId, $layoutId) {
                        $pageId = (
                        !is_null($item->getPage()) ? $item->getPage()->getId() : 0
                        );
                        return (
                            $pageId == $currentPageId &&
                            $layoutId == $item->getLayout()->getId()
                        );
                    });
            } else {
                $currentLayoutPages = new ArrayCollection();
            }
            foreach ($layoutPages as $layoutPageData) {
                if ($currentLayoutPages->count() > 0) {
                    $currentLayoutPage = $currentLayoutPages->first();
                    $layoutPageData['id'] = $currentLayoutPage->getId();
                    $currentLayoutPages->removeElement($currentLayoutPage);
                }
                $layoutPage = $this->layoutPageManager
                    ->fromArray(
                        $layoutPageData, $this, $this->pageManager
                    );
                $layoutPage->setLayout($layout);
                $layout->addLayoutPage($layoutPage);
            }
            if ($currentLayoutPages->count() > 0) {
                foreach ($currentLayoutPages as $layoutPage) {
                    $layout->getLayoutPages()->removeElement($layoutPage);
                }
            }
        }
        if (isset($children)) {
            foreach ($children as $childData) {
                $child = $this->fromArray($childData);
                $child->setParent($layout);
                $layout->addChild($child);
            }
        }
        if (isset($layouts)) {
            foreach ($layouts as $layoutData) {
                $entity = $this->fromArray($layoutData);
                if (!$entity->isLinked() && !$entity->getParent()) {
                    $entity = $this->cloneItem($entity);
                }
                $layout->addLayout($entity);
            }
        }

        return $layout;
    }

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

        return true;
    }

    /**
     * @param LayoutInterface $layout
     * @return string
     */
    public function getNamespacePath(LayoutInterface $layout)
    {
        return sprintf(self::TWIG_NAMESPACE, $layout->getFilePath());
    }

    /**
     * @param  LayoutInterface $item
     * @return LayoutInterface
     */
    public function cloneItem($item)
    {
        $entity = clone $item;

        $this->cloneHandler($entity, $item);
        $entity->setParent(null);
        $entity->setLayouts(
            new ArrayCollection()
        );

        foreach ($item->getLayoutBreakpoints() as $layoutBreakpointData) {
            $layoutBreakpoint = $this->layoutBreakpointManager
                ->cloneItem($layoutBreakpointData);
            $entity->addLayoutBreakpoint($layoutBreakpoint);
        }
        foreach ($item->getLayoutPages() as $layoutPageData) {
            $layoutPage = $this->layoutPageManager
                ->cloneItem($layoutPageData);
            $entity->addLayoutPage($layoutPage);
        }
        foreach ($item->getChildren() as $childData) {
            $child = $this->cloneItem($childData);
            $child->setParent($entity);
            $entity->addChild($child);
        }

        return $entity;
    }
}
