<?php

namespace Dalten\RuianToUir\Converter;

use Dalten\RuianToUir\Converter\Field\GeometryFieldInterface;
use Dalten\RuianToUir\Converter\Field\Point;
use Dalten\RuianToUir\Converter\Field\PointCoordinatesConverter;

/**
 * Abstraktní převaděč - obsahuje základní společnou funkcionalitu převaděčů.
 */
abstract class AbstractConverter
{
    /**
     * Název tagu, který třída obsluhuje.
     */
    protected string $handledTag = '';

    private string $name;

    public function __construct(protected PointCoordinatesConverter $coordinateConverter)
    {
    }

    /**
     * Zjišťuje, zda tato třída dokáže obsloužit daný element.
     *
     * @param \XMLReader $document instance readeru s kurzorem na testovaném prvku
     *
     * @return bool Obsluhuje tato třída daný prvek?
     */
    public function accepts(\XMLReader $document): bool
    {
        return \XMLReader::ELEMENT === $document->nodeType && $document->name === $this->handledTag;
    }

    public function getName(): string
    {
        if (!isset($this->name)) {
            $namespaceParts = explode('\\', static::class);

            $this->name = end($namespaceParts) ?: '?';
        }

        return $this->name;
    }

    /**
     * Slouží k nalezení dalšího prvku. Hledá prvek a ignoruje všechny ostatní typy XML uzlů.
     *
     * @param \XMLReader $document instance readeru s kurzorem na testovaném prvku
     *
     * @return false|\XMLReader XMLreader s nastaveným kurzorem a nebo false, pokud bychom opustili obsluhovaný prvek
     */
    protected function getNextElement(\XMLReader $document): \XMLReader|false
    {
        $document->read();
        while (\XMLReader::ELEMENT !== $document->nodeType) {
            if ($document->name === $this->handledTag && \XMLReader::END_ELEMENT === $document->nodeType) {
                return false;
            }
            $document->read();
        }

        return $document;
    }

    /**
     * Slouží přesunu kurzoru na konec aktuálního prvku.
     */
    protected function skipToElementEnd(\XMLReader $document): \XMLReader|false
    {
        $elementName = $document->name;
        $depth = 1;
        while ($depth > 0) {
            $document->read();
            if ($document->name === $this->handledTag && \XMLReader::END_ELEMENT === $document->nodeType) {
                return false;
            }
            if ($document->name === $elementName && \XMLReader::END_ELEMENT === $document->nodeType) {
                --$depth;
            } elseif ($document->name === $elementName && \XMLReader::ELEMENT === $document->nodeType) {
                ++$depth;
            }
        }

        return $document;
    }

    /**
     * Vytvoří instanci třídy podle daného Geometrického prvku (Point, Polygon, ...).
     */
    protected function getGeometryData(\XMLReader $document): ?GeometryFieldInterface
    {
        do {
            if ('gml:Point' == $document->name) {
                $this->getNextElement($document);
                if ('gml:pos' === $document->name && $document->readString()) {
                    return new Point($this->coordinateConverter->convertCoords($document->readString()));
                }
            }
        } while ($this->getNextElement($document));

        return null;
    }
}
