<?php
/**
 * Description of MetaHelper
 *
 * @author Elze Kool <info@visualmedia.nl>
 */

namespace VisualM\TagManagerBundle\Content;

use Exception;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

use VisualM\TagManagerBundle\DataCollector\TagDataCollector;
use VisualM\TagManagerBundle\Doctrine\FieldType\MetaType;
use VisualM\TagManagerBundle\Entity\MetaTag;
use VisualM\TagManagerBundle\Entity\MetaTagRepository;
use VisualM\TagManagerBundle\Util\ExtendedDataDictionary;
use VisualM\TagManagerBundle\Model\MetaTagSetManager;
use VisualM\TagManagerBundle\Model\TagSet\LanguageTagSetFactory;

/**
 * Description of MetaHelper
 *
 * @author Elze Kool <info@visualmedia.nl>
 */
class MetaHelper
{
    protected $metaTagManager;
    protected $content;
    protected $extendedDataDictionary;
    protected $expressionLanguage;
    protected $dataCollector;
    protected $metaTagSetManager;

    /**
     * Constructor
     *
     * @param MetaTagRepository $metaTagManager
     * @param ExtendedDataDictionary $extendedDataDictionary
     * @param ExpressionLanguage $expressionLanguage
     * @param TagDataCollector $dataCollector
     * @param MetaTagSetManager $metaTagSetManager
     */
    public function __construct(MetaTagRepository $metaTagManager, ExtendedDataDictionary $extendedDataDictionary, ExpressionLanguage $expressionLanguage, TagDataCollector $dataCollector, MetaTagSetManager $metaTagSetManager)
    {
        $this->metaTagManager = $metaTagManager;
        $this->extendedDataDictionary = $extendedDataDictionary;
        $this->expressionLanguage = $expressionLanguage;
        $this->dataCollector = $dataCollector;
        $this->metaTagSetManager = $metaTagSetManager;
    }

    /**
     * Collect Data for Object
     *
     * @param object $object Object to collect data for
     *
     * @return MetaHelper self
     */
    public function collectObject($object)
    {
        $this->extendedDataDictionary->pushObject($object);

        if ($object instanceof TaggableContentInterface) {
            $this->dataCollector->addCollectedContentId(sprintf('%s (%s)', join('', array_slice(explode('\\', get_class($object)), -1)), $object->getContentId()));
        } else {
            $this->dataCollector->addCollectedContentId(join('', array_slice(explode('\\', get_class($object)), -1)));
        }

        return $this;
    }

    /**
     * Set Content
     * @param TaggableContentInterface $content
     */
    public function setContent(TaggableContentInterface $content)
    {
        $this->content = $content;
        $this->dataCollector->updateContentId(sprintf('%s (%s)', join('', array_slice(explode('\\', get_class($this->content)), -1)), $this->content->getContentId()));
    }

    /**
     * Get MetaTags
     */
    public function getMetaTags()
    {
        $meta_tags = $this->metaTagManager->getIndex([ 'content_id' => $this->content->getContentId() ]);

        foreach ($meta_tags as $meta_tag) {
            $metatag_preset = $meta_tag->getMetaTagPreset();
            $metatag_set = $metatag_preset->getMetaTagSet();

            $factoryService = $metatag_set->getFactoryService();
            $factory = $this->metaTagSetManager->getFactory($factoryService);

            if ($factory != null) {
                $meta_tag = $factory->processMetaTag($meta_tag);
            }
        }

        return $meta_tags;
    }

    /**
     * Render MetaTags
     *
     * @param array $types MetaTypes to render
     *
     * @return string Rendered tags
     */
    public function renderMetaTags($types = [])
    {
        if ($this->content === null) {
            return '';
        }

        $meta_tags = $this->getMetaTags();

        $rendered = '';
        foreach($meta_tags as $meta_tag) {
            if (!in_array($meta_tag->getMetaTagPreset()->getType(), $types)) {
                continue;
            }

            if (null !== ($rendered_meta_tag = $this->renderMetaTag($meta_tag))) {
                $rendered .= $rendered_meta_tag . "\n";
            }
        }

        return $rendered;
    }

    /**
     * Evaluate and Render MetaTag
     *
     * @param MetaTag $metaTag MetaTag
     *
     * @return string Rendered MetaTag
     */
    protected function renderMetaTag(MetaTag $metaTag)
    {
        $data = $this->extendedDataDictionary->getData();
        foreach($data as &$value) {
            if (is_array($value)) {
                $value = (object) $value;
            }
        }

        $this->dataCollector->updateAvailableData($data);

        $language = $this->expressionLanguage;

        $exception = '';

        // In zichtbaar is null return null
        if ($metaTag->getZichtbaar() === null OR $metaTag->getZichtbaar() === null) {
            $this->dataCollector->addTag(new \VisualM\TagManagerBundle\DataCollector\CollectionItem(
                $metaTag->getMetaTagPreset()->getTitel(),
                $metaTag->getMetaTagPreset()->getType(),
                '',
                false,
                'Zichtbaar is NULL'
            ));

            return null;
        }

        // Evaluate or set zichtbaar
        if ($metaTag->getZichtbaarIsExpression()) {
            try { $zichtbaar = $language->evaluate($metaTag->getZichtbaar(), $data);
            } catch(Exception $e) { $zichtbaar = 0; $exception .= $e->getMessage() . PHP_EOL; }
        }
        else {
            $zichtbaar = $metaTag->getZichtbaar();
        }

        // Check if zichtbaar evaluates to 1, else return null
        if (intval($zichtbaar) != 1) {
            $this->dataCollector->addTag(new \VisualM\TagManagerBundle\DataCollector\CollectionItem(
                $metaTag->getMetaTagPreset()->getTitel(),
                $metaTag->getMetaTagPreset()->getType(),
                '',
                false,
                $exception
            ));

            return null;
        }

        // Evaluate or set inhoud
        if ($metaTag->getInhoudIsExpression()) {
            try {
                $inhoud = $language->evaluate($metaTag->getInhoud(), $data);
            }
            catch(Exception $e) {
                $inhoud = ''; $exception .= $e->getMessage() . PHP_EOL; $zichtbaar = false;
            }
        }
        else {
            $inhoud = $metaTag->getInhoud();
        }

        if ($metaTag->getMetaTagPreset()->getType() === MetaType::META_TAG) {
            $html = sprintf(
                '<meta name="%s" content="%s" />',
                htmlspecialchars($metaTag->getMetaTagPreset()->getNaam()),
                htmlspecialchars($inhoud)
            );
        }
        else {
            $html = $inhoud;
        }

        // Add line to DataCollector
        $this->dataCollector->addTag(new \VisualM\TagManagerBundle\DataCollector\CollectionItem(
            $metaTag->getMetaTagPreset()->getTitel(),
            $metaTag->getMetaTagPreset()->getType(),
            $html,
            $zichtbaar,
            $exception
        ));

        return $zichtbaar ? $html : null;
    }

    /**
     * Get HrefLangTags
     *
     * @return Array
     */
    public function getHrefLangTags()
    {
        $meta_tags = $this->getMetaTags();

        $rendered_hreflang_tags = array();
        foreach ($meta_tags as $meta_tag) {
            $metatag_preset = $meta_tag->getMetaTagPreset();
            $metatag_set = $metatag_preset->getMetaTagSet();

            $factoryService = $metatag_set->getFactoryService();
            $factory = $this->metaTagSetManager->getFactory($factoryService);

            if ($factory instanceof LanguageTagSetFactory) {
                $factory->extractHrefLangTags($meta_tag, $rendered_hreflang_tags);
            }
        }

        $hreflang_tags = array();
        foreach ($rendered_hreflang_tags as $lang_key=>$hreflang_tag) {
            preg_match('/href="([^"]+)"/', $hreflang_tag, $matches);

            $hreflang_tags[$lang_key] = array(
                'html' => $hreflang_tag,
                'target' => $matches[1],
            );
        }

        return $hreflang_tags;
    }
}
