<?php
// +-------------------------------------------------+
// © 2002-2004 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: XMLBuilder.php,v 1.2.2.2 2025/06/27 14:21:07 rtigero Exp $

namespace Pmb\ImportExport\Models\Steps\StepExport\StepExportFile\Builders;

use Pmb\ImportExport\Models\Ontology\importExportOntology;
use SimpleXMLElement;

class XMLBuilder implements BuilderInterface
{
    protected const ATTRIBUTE_TYPE = "http://www.pmbservices.fr/ontology#attribute";
    protected const VALUE_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#value";

    protected $file = null;
    protected string $encoding = "";
    protected importExportOntology $ontology;
    protected array $data = array();
    protected SimpleXMLElement $xml;
    protected bool $htmlEntitiesConversion = false;

    public function __construct($file, importExportOntology $ontology, array $data, \stdClass $baseParameters)
    {
        $this->file = $file;
        $this->ontology = $ontology;
        $this->data = $data;
        $this->encoding = $baseParameters->encoding;
        $this->htmlEntitiesConversion = $baseParameters->htmlEntitiesConversion;
    }

    public function build(): bool
    {
        //On ajoute d'abord le header xml
        fwrite($this->file, $this->getXMLHeader());
        foreach ($this->data as $entityUri => $entity) {
            //On parcourt les différents types d'entités
            $ontoEntity = $this->ontology->getEntityByURI($entityUri);
            foreach ($entity as $value) {
                //On parcourt les entités
                $this->xml = new SimpleXMLElement("<" . $ontoEntity->name . "></" . $ontoEntity->name . ">");
                $this->parseEntity($value, $this->xml);
                $element = $this->htmlEntitiesConversion ? html_entity_decode($this->xml->asXML(), ENT_NOQUOTES, $this->encoding) : $this->xml->asXML();
                //On vire l'entete XML
                $element = preg_replace('/<\?xml[^>]*\?>/', '', $element);
                //On remplit le fichier
                fwrite($this->file, $element);
            }
        }
        return true;
    }

    /**
     * Retourne le header XML
     * @return string
     */
    private function getXMLHeader(): string
    {
        return '<?xml version="1.0" encoding="' . $this->encoding . '"?>';
    }

    /**
     * Replit la propriété $this->xml avec les données passées en paramètre
     * @param array $data
     * @param SimpleXMLElement|null $parentElement
     *
     * @return void
     */
    private function parseEntity(array $data, SimpleXMLElement $parentElement): void
    {
        foreach ($data as $key => $value) {
            //Si on est sur un numérique on parcourt juste
            if (is_numeric($key)) {
                $this->parseEntity($value, $parentElement);
                continue;
            }
            switch (true) {
                case $this->ontology->propertyExists($key):
                    //On est sur une propriete definie, on regarde alors si c'est un attribut
                    $property = $this->ontology->getPropertyByURI($key);
                    $propertyName = $property->name;
                    if ($this->isAttribute($value)) {
                        //On recupere la valeur de l'attribut et on l'ajoute au noeud parent
                        $attributeValue = $value[0][static::ATTRIBUTE_TYPE][0];
                        $parentElement->addAttribute($propertyName, $attributeValue);
                    } else {
                        //C'est un sous noeud, on le cree et on le parse
                        if (is_array($value) && count($value) == 1 && is_string($value[0])) {
                            //On a directement la valeur alors on ajoute le noeud
                            $childElement = $parentElement->addChild($propertyName);
                            $childElement[0] = $value[0];
                        } else {
                            //Sinon on cree un sous noeud pour chaque element et on parse
                            foreach ($value as $subElement) {
                                $childElement = $parentElement->addChild($propertyName);
                                $this->parseEntity($subElement, $childElement);
                            }
                        }
                    }
                    break;
                case $key == static::VALUE_TYPE:
                    //On est sur une valeur literale, on ajoute la valeur au noeud parent
                    $content = $value[0];
                    $parentElement[0] = $content;
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * Vérifie si le tableau représente un attribut XML
     * @param array $value
     * @return bool
     */
    private function isAttribute(array $value): bool
    {
        if (is_array($value[0]) && array_key_first($value[0]) == static::ATTRIBUTE_TYPE) {
            return true;
        }
        return false;
    }
}
