<?php
/**
 * @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
 * @date 02/17/2017
 */

namespace AppBundle\Service;


use AppBundle\Entity\Keyword;
use AppBundle\Entity\Page;
use Doctrine\ORM\EntityManager;

class KeywordService
{
    protected static $DO_SKIP_KEYWORD_CHECK_BY_DOM_ELEMENT_NAME = ['h1' => true, 'h2' => true, 'h3' => true,
        'h4' => true, 'h5' => true, 'h6' => true, 'a' => true, 'audio' => true, 'video' => true, 'object' => true,
        'nav' => true, 'form' => true
    ];

    private $entityManager;
    private $dictByContext = null;
    private $globalDict = null;

    /**
     * KeywordService constructor.
     *
     * @param EntityManager $entityManager
     */
    public function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * Source: http://78.47.251.156/svn/stern-consulting/sunstarPlugin/branches/1.4/lib/sun.class.php
     *
     * @param $html
     * @param string $classes Space separated values for the "class"-attribute
     * @return string
     */
    public function addKeywordLinksToHtml($html, $classes = '')
    {
        $dict = $this->getKeywordsToLinksDict($classes);
        $doc = new \DOMDocument('1.0', 'utf-8');
        $doc->loadHTML('<?xml encoding="utf-8" ?><html><body>'. $html .'</body></html>');
        $xp = new \DOMXPath($doc);
        $rootNodes = $xp->query('//body')->item(0)->childNodes;
        $this->addKeywordLinksToDomNodes($rootNodes, $dict, $doc);
        $ret = '';
        foreach($rootNodes as $node)
        {
            $ret .= $doc->saveHTML($node);
        }
        return $ret;
    }

    /**
     * Quelle: http://78.47.251.156/svn/stern-consulting/sunstarPlugin/branches/1.4/lib/sun.class.php
     *
     * @param \DOMNodeList|\DOMNode[] &$nodes
     * @param array &$dict
     * @param \DOMDocument &$doc
     */
    private function addKeywordLinksToDomNodes(&$nodes, &$dict, &$doc)
    {
        // As $nodes is a direct reference to $parentNode->childNodes it will be changed when
        // replacing text-nodes due to adding keywords. Therefore we loop through this
        // auxilary array '$originalNodes' which simply contains all nodes to process.
        $originalNodes = [];
        foreach ($nodes as $node)
        {
            $originalNodes[] = $node;
        }

        /** @var \DOMNode $node */
        foreach($originalNodes as $node)
        {
            if($node->nodeType == XML_TEXT_NODE)
            {
                $nodeValue = $node->nodeValue;
                $foundKeywords = false;
                foreach($dict as $keyword => $link)
                {
                    $nodeValue = preg_replace('/(^|\W)'. preg_quote($keyword, '/') .'($|\W)/u',
                        '$1'. $link .'$2', $nodeValue, 1, $count);
                    if($count > 0)
                    {
                        $foundKeywords = true;
                        unset($dict[$keyword]);
                    }
                }
                if($foundKeywords)
                {
                    $newNode = $doc->createDocumentFragment();
                    $newNode->appendXML($nodeValue);
                    $node->parentNode->replaceChild($newNode, $node);
                }
            }
            elseif($node->nodeType == XML_ELEMENT_NODE &&
                !isset(self::$DO_SKIP_KEYWORD_CHECK_BY_DOM_ELEMENT_NAME[strtolower($node->nodeName)]))
            {
                $classAttr = $node->attributes->getNamedItem("class");
                if (!(isset($classAttr) && $classAttr->nodeValue &&
                    preg_match('/(^|\s)skip-keywords($|\s)/', $classAttr->nodeValue) == 1))
                {
                    $this->addKeywordLinksToDomNodes($node->childNodes, $dict, $doc);
                }
            }
        }
    }

    /**
     * Source: http://78.47.251.156/svn/stern-consulting/sunstarPlugin/branches/1.4/lib/sun.class.php
     *
     * @param string $classes Space separated values for the "class"-attribute
     * @return array
     */
    private function getKeywordsToLinksDict($classes = '', $context = null)
    {
        if ($context)
        {
            if (!isset($this->dictByContext[$context]))
            {
                $this->dictByContext[$context] = null;
            }
            $dict = &$this->dictByContext[$context];
        }
        else
        {
            $dict = &$this->globalDict;
        }

        if($dict === null)
        {
            $dict = [];
            $keywords = $this->entityManager->getRepository('AppBundle:Keyword')->findAll();
            /** @var Keyword $keyword */
            foreach($keywords as $keyword)
            {
                if(isset($dict[$keyword->getValue()]))
                {
                    continue;
                }
                $dictEntry = $this->createKeywordDictEntry($keyword->getUrl(), $keyword->getValue(), $classes);
                if ($dictEntry !== null)
                {
                    $dict[$keyword->getValue()] = $dictEntry;
                }
            }

            $pages = $this->entityManager->createQueryBuilder()
                ->from('AppBundle:Page', 'p')
                ->select('p')
                ->where('p.keyword IS NOT NULL')
                ->getQuery()
                ->execute()
            ;
            /** @var Page $page */
            foreach($pages as $page)
            {
                if(isset($dict[$page->getKeyword()]))
                {
                    continue;
                }
                $dictEntry = $this->createKeywordDictEntry($page->getUrlPath(), $page->getKeyword(), $classes);
                if ($dictEntry !== null)
                {
                    $dict[$page->getKeyword()] = $dictEntry;
                }
            }
        }
        return $dict;
    }

    private function createKeywordDictEntry($url, $keywordValue, $classes)
    {
        $port = parse_url($url, PHP_URL_PORT);
        if($url[0] == '/' || strtolower($_SERVER['HTTP_HOST']) ==
            strtolower(parse_url($url, PHP_URL_HOST) .($port ? ':'. $port : '')))
        {
            // Same host => check if same page
            $urlPath = parse_url($url, PHP_URL_PATH);
            if($_SERVER['REQUEST_URI'] == $urlPath ||
                ($_SERVER['REQUEST_URI'] == '/' && $urlPath == ''))
            {
                // Yes => we don't want to link to the current URL
                return null;
            }
            // No => setup a JS popup layer
            $attr = '';
            $additional_classes = 'intern-link show-layer ';
        }
        else
        {
            $additional_classes = '';
            $attr = 'target="_blank"';
        }
        return '<a href="'. $url .'" '. $attr .' class="'. $additional_classes . $classes .'">'. $keywordValue .'</a>';
    }

}