<?php

/*
 * This file is part of Sulu.
 *
 * (c) Sulu GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Component\DocumentManager\Subscriber\Core;

use PHPCR\NodeInterface;
use Sulu\Component\DocumentManager\Event\CreateEvent;
use Sulu\Component\DocumentManager\Event\HydrateEvent;
use Sulu\Component\DocumentManager\Events;
use Sulu\Component\DocumentManager\Metadata;
use Sulu\Component\DocumentManager\MetadataFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Responsible for instantiating documents from PHPCR nodes and
 * setting the document in the event so that other listeners can
 * take further actions (such as hydrating it for example).
 *
 * NOTE: This should always be the first thing to be called
 */
class InstantiatorSubscriber implements EventSubscriberInterface
{
    /**
     * @var MetadataFactoryInterface
     */
    private $metadataFactory;

    public function __construct(
        MetadataFactoryInterface $metadataFactory
    ) {
        $this->metadataFactory = $metadataFactory;
    }

    public static function getSubscribedEvents()
    {
        return [
            Events::HYDRATE => ['handleHydrate', 500],
            Events::CREATE => ['handleCreate', 500],
        ];
    }

    public function handleHydrate(HydrateEvent $event)
    {
        // don't need to instantiate the document if it is already existing.
        if ($event->hasDocument()) {
            return;
        }

        $node = $event->getNode();

        $document = $this->getDocumentFromNode($node);
        $event->setDocument($document);
    }

    public function handleCreate(CreateEvent $event)
    {
        $metadata = $this->metadataFactory->getMetadataForAlias($event->getAlias());
        $document = $this->instantiateFromMetadata($metadata);
        $event->setDocument($document);
    }

    /**
     * Instantiate a new document. The class is determined from
     * the mixins present in the PHPCR node for legacy reasons.
     *
     * @return object
     */
    private function getDocumentFromNode(NodeInterface $node)
    {
        $metadata = $this->metadataFactory->getMetadataForPhpcrNode($node);

        return $this->instantiateFromMetadata($metadata);
    }

    /**
     * @return object
     */
    private function instantiateFromMetadata(Metadata $metadata)
    {
        $class = $metadata->getClass();

        if (!\class_exists($class)) {
            throw new \RuntimeException(\sprintf(
                'Document class "%s" does not exist', $class
            ));
        }

        $document = new $class();

        return $document;
    }
}
