<?php
namespace Helpers\Webservice;
use Helpers\Outil\XmlHelper;
// phpcs:ignoreFile
// use Helpers\Webservice\BaseXmlDocument;

class XmlDocument implements BaseXmlDocument
{
    private ?\DOMXPath $xpath = null;
    
    /**
     * Creation d'un document xml
     * avec gestion correcte de l'encodage UTF8
     */
    private ?\DOMDocument $doc = null;
    
    function __construct()
    {
        $this->doc = new \DOMDocument('1.0', 'UTF-8');
        $this->doc->preserveWhiteSpace = false;
    }
    
    /**
     * Permet de recuperer le root du document
     */
    public function & getDocument() :\DOMDocument
    {
        return $this->doc;
    }
    
    /**
     * Function mise en place car apparemment le loadxml ne gere pas un trap d'erreur
     *
     * @param String $errno
     * @param String $errstr
     * @param String $errfile
     * @param String $errline
     *
     * http://php.net/manual/fr/function.set-error-handler.php
     * @return boolean ou exception
     */
    function HandleXmlError($errno, $errstr, $errfile, $errline)
    {
        if ($errno==E_WARNING && (substr_count($errstr, 'DOMDocument::loadXML()')>0)) {
            throw new \DOMException($errstr);
        } else {
            return false;
        }
    }
    
    function & loadXmlFile(string $string) :bool
    {
        $ok = false;
        // Prise en compte de la gestion des erreurs par le handle
        // @phpstan-ignore-next-line
        set_error_handler(array($this,'HandleXmlError'));
        
        try {
            $this->doc->load($string);
            $this->xpath = new \DOMXPath($this->doc);
            $ok = true;
        } catch (\DOMException $e) {
            //echo $e->getMessage();
            $ok = false;
        }
        // remise en place du handleur generique
        restore_error_handler();
        
        return $ok;
    }
    
    /**
     * Permet de charger un string xml
     */
    function & loadXmlString(string &$string) :bool
    {
        $ok = false;
        // Prise en compte de la gestion des erreurs par le handle
        // @phpstan-ignore-next-line
        set_error_handler(array($this,'HandleXmlError'));
        
        try {
            $this->doc->loadXML($string);
            $this->xpath = new \DOMXPath($this->doc);
            $ok = true;
        } catch (\DOMException $e) {
            //echo $e->getMessage();
            try {
                @$this->doc->loadHTML($string);
                $this->xpath = new \DOMXPath($this->doc);
                $ok = true;
            } catch (\DOMException $e) {
                $ok = false;
            }
        }
        // remise en place du handleur generique
        restore_error_handler();
        
        return $ok;
    }
    
    function getQueryXpath(string $query) :mixed
    {
        return $this->xpath->query($query);
    }
    
    // /**
    //  * Ajoute une section au document sur la base d'un noeud
    //  *
    //  * @param object $root
    //  * @param string $texte
    //  *
    //  * @return string domnode
    //  */
    // function AddSection($root, $texte)
    // {
    //     if ($root instanceof \DOMDocument) {
    //         $element = $root->createElement($texte);
    //     } else {
    //         $element = $root->ownerDocument->createElement($texte);
    //     }
                
    //     $root->appendChild($element);
    //     return $element;
    // }
    
    // /**
    //  * Ajoute une section avec valeur au document sur la base d'un noeud
    //  *
    //  * @param object $root
    //  * @param string $node
    //  * @param string $content
    //  */
    // function AddElementValue($root, $node, $content = '')
    // {
    //     $element = $root->ownerDocument->createElement($node);
    //     $root->appendChild($element);
        
    //     if ($content!='') {
    //         $content = htmlentities($content, ENT_COMPAT, 'UTF-8');
    //         $element->appendChild($root->ownerDocument->createTextNode($content));
    //     }
    //     return $element;
    // }
    
    /**
     * Sauvegarde de le flux sous format xml avec prise en compte
     * des entities pour eviter les probl�mes d'encodage norme ISO8859-1 / 15
     *
     * @return mixed
     */
    function & SaveXml()
    {
        return XmlDocument::parseEntities(
            XmlHelper::saveXML($this->doc, $this->doc, LIBXML_NOEMPTYTAG)
        );
    }
    
    static function & parseEntities(string $string) :string
    {
        $entities = array (
            'auml' => '&#228;',
            'ouml' => '&#246;',
            'uuml' => '&#252;',
            'szlig' => '&#223;',
            'Auml' => '&#196;',
            'Ouml' => '&#214;',
            'Uuml' => '&#220;',
            'nbsp' => '&#160;',
            'Agrave' => '&#192;',
            'Egrave' => '&#200;',
            'Eacute' => '&#201;',
            'Ecirc' => '&#202;',
            'egrave' => '&#232;',
            'eacute' => '&#233;',
            'ecirc' => '&#234;',
            'agrave' => '&#224;',
            'iuml' => '&#239;',
            'ugrave' => '&#249;',
            'ucirc' => '&#251;',
            'ccedil' => '&#231;',
            'AElig' => '&#198;',
            'aelig' => '&#330;',
            'OElig' => '&#338;',
            'oelig' => '&#339;',
            'angst' => '&#8491;',
            'cent' => '&#162;',
            'copy' => '&#169;',
            'Dagger' => '&#8225;',
            'dagger' => '&#8224;',
            'deg' => '&#176;',
            'emsp' => '&#8195;',
            'ensp' => '&#8194;',
            'ETH' => '&#208;',
            'eth' => '&#240;',
            'euro' => '&#8364;',
            'half' => '&#189;',
            'laquo' => '&#171;',
            'ldquo' => '&#8220;',
            'lsquo' => '&#8216;',
            'mdash' => '&#8212;',
            'micro' => '&#181;',
            'middot' => '&#183;',
            'ndash' => '&#8211;',
            'not' => '&#172;',
            'numsp' => '&#8199;',
            'para' => '&#182;',
            'permil' => '&#8240;',
            'puncsp' => '&#8200;',
            'raquo' => '&#187;',
            'rdquo' => '&#8221;',
            'rsquo' => '&#8217;',
            'reg' => '&#174;',
            'sect' => '&#167;',
            'THORN' => '&#222;',
            'thorn' => '&#254;',
            'trade' => '&#8482;',
            
            
            'amp' => '&#38;' // Obligatoire en dernier
        );
        
        foreach ($entities as $ent => &$repl) {
            $string = preg_replace('/&amp;'.$ent.';?/m', $repl, $string);
        }
            
        return $string;
    }
}
