<?php

namespace Velis\Filter;

use DOMDocument;

/**
 * Utility class to truncate HTML to a certain length
 */
class TruncateHtml
{
    private int $maxLength = 0;

    public function __construct(int $maxLength)
    {
        $this->maxLength = $maxLength;
    }

    private function traverseDom($node, $newDom, $newNode, &$currentLength)
    {
        foreach ($node->childNodes as $childNode) {
            if ($childNode->nodeType === XML_TEXT_NODE) {
                if ($currentLength + mb_strlen($childNode->nodeValue) > $this->maxLength) {
                    $remaining = $this->maxLength - $currentLength;
                    $cutPosition = strrpos(mb_substr($childNode->nodeValue, 0, $remaining), ' ');
                    $newText = $newDom->createTextNode(mb_substr($childNode->nodeValue, 0, $cutPosition) . '...');
                    $newNode->appendChild($newText);
                    return false;
                } else {
                    $newText = $newDom->createTextNode($childNode->nodeValue);
                    $newNode->appendChild($newText);
                    $currentLength += mb_strlen($childNode->nodeValue);
                }
            } else {
                $newChildNode = $newDom->importNode($childNode, false);
                $newNode->appendChild($newChildNode);
                if (!self::traverseDom($childNode, $newDom, $newChildNode, $currentLength)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Main function, truncates HTML to a certain length
     * @param string $html
     * @return string
     */
    public function truncate($html)
    {
        if (strpos($html, '<body') === false) {
            $html = '<body>' . $html . '</body>';
        }

        $dom = new DOMDocument();
        $dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

        $body = $dom->getElementsByTagName('body')->item(0);

        $newDom = new DOMDocument();
        $newBody = $newDom->importNode($body, false);
        $newDom->appendChild($newBody);

        $currentLength = 0;

        static::traverseDom($body, $newDom, $newBody, $currentLength);

        return $newDom->saveHTML();
    }
}
