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

namespace Pmb\ImportExport\Models\Ontology;

use ARC2;
use onto_store_arc2_extended;
use Pmb\Common\Helper\Helper;

class Store extends onto_store_arc2_extended
{
    public $importExportOntology = null;
    public $ns = array(
        "skos"    => "http://www.w3.org/2004/02/skos/core#",
        "dc"    => "http://purl.org/dc/elements/1.1",
        "dct"    => "http://purl.org/dc/terms/",
        "owl"    => "http://www.w3.org/2002/07/owl#",
        "rdf"    => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
        "rdfs"    => "http://www.w3.org/2000/01/rdf-schema#",
        "xsd"    => "http://www.w3.org/2001/XMLSchema#",
        "pmb"    => "http://www.pmbservices.fr/ontology#"
    );

    public $config = array(
        /* db */
        'db_name' => DATA_BASE,
        'db_user' => USER_NAME,
        'db_pwd' => USER_PASS,
        'db_host' => SQL_SERVER,
        /* store */
        'store_name' => "rdfstore",
        /* stop after 100 errors */
        'max_errors' => 100,
        'store_strip_mb_comp_str' => 0
    );

    protected $graph = "";
    protected $graphURI = "";
    protected $ontologyType = "";
    protected $ontology = null;


    public function __construct($storeName, $graph = "", $ontologyType = "")
    {
        $this->config['store_name'] = $storeName;
        parent::__construct($this->config);
        $this->reset();
        $this->graph = $graph;
        $this->ontologyType = $ontologyType;
        $this->importExportOntology = new ImportExportOntology($this);
    }

    public function storeTriples(array $arrayTriples = array())
    {
        if (count($arrayTriples)) {
            $q = "INSERT INTO <" . $this->graph . "> {";
            $q .= implode(" .\n", $arrayTriples);
            $q .= "}";
            $r = $this->query($q);
            if (!empty($this->errors)) {
                var_dump($q, $r, $this->errors);
            }
        }
    }

    public function deleteTriples(array $arrayTriples = array())
    {
        if (count($arrayTriples)) {
            $q = "DELETE FROM <" . $this->graph . "> {";
            $q .= implode(" .\n", $arrayTriples);
            $q .= "}";
            $r = $this->query($q);
            if (!empty($this->errors)) {
                var_dump($q, $r, $this->errors);
            }
        }
    }

    public function query($query, $prefix = "")
    {
        $this->result = array();
        parent::query($query, $this->ns);
        if (empty($this->result) && empty($this->errors)) {
            return true;
        }
        return Helper::toArray($this->result);
    }

    public function __call($name, $arguments)
    {
        if (method_exists($this->importExportOntology, $name) && !method_exists($this, $name)) {
            return call_user_func_array(array($this->importExportOntology, $name), $arguments);
        }
    }

    public function getOntology()
    {
        if (is_null($this->ontology)) {
            $this->ontology = $this->importExportOntology->readFromStore();
        }

        return $this->ontology;
    }

    /**
     * Retourne le prefixe du graphe
     * @return string
     */
    public function getGraph()
    {
        return $this->graph;
    }

    /**
     * Interroge le store pour obtenir l'URI du graphe a partir du prefixe
     * @return string
     */
    public function getGraphURI()
    {
        if (!empty($this->graphURI)) {
            return $this->graphURI;
        }
        if (!$this->graph) {
            return "";
        }
        $query = "SELECT ?g
            WHERE {
                GRAPH ?g { ?s ?p ?o }
                FILTER (regex (?g, '\/" . $this->graph . "$', '')) .
            }
            LIMIT 1";
        $result = $this->query($query);
        if (!empty($result[0]['g'])) {
            $this->graphURI = $result[0]['g'];
        }
        return $this->graphURI;
    }


    public function getOntologyType()
    {
        return $this->ontologyType;
    }

    public function getUri($prefix, $name)
    {
        if (array_key_exists($prefix, $this->ns)) {
            return $this->ns[$prefix] . $name;
        }
        return "";
    }

    /**
     * Retourne les données d'un graphe sous forme de tableau
     *
     * @return array
     */
    public function getGraphData()
    {
        $data = array();
        foreach ($this->importExportOntology->getEntities() as $entity) {
            $query = "SELECT * WHERE {
                GRAPH " . (empty($this->getGraphURI()) ? "?g" : "<" . $this->getGraphURI() . ">") . " {
                    ?s rdf:type <" . $entity->uri . "> ;
                        ?p ?o .
                }
                FILTER(?p != rdf:type)
            } ORDER BY ?s";
            $result = $this->query($query);
            if (is_countable($result)) {
                foreach ($result as $row) {
                    // ...
                    if (!isset($data[$entity->uri][$row["s"]][$row["p"]])) {
                        $data[$entity->uri][$row["s"]][$row["p"]] = array();
                    }
                    $data[$entity->uri][$row["s"]][$row["p"]][] = ($row["o_type"] == "bnode") ? $this->getDataFromBnode($row["o"]) : $row["o"];
                }
            }
        }
        return $data;
    }

    /**
     * Récupération récursive des données d'un noeud blanc
     * @param string $bnode Noeud blanc
     *
     * @return array
     */
    protected function getDataFromBnode($bnode)
    {
        $data = array();

        $query = "SELECT * WHERE {
            GRAPH " . (empty($this->getGraphURI()) ? "?g" : "<" . $this->getGraphURI() . ">") . " {
                <" . $bnode . "> ?p ?o .
            }
        }";
        $result = $this->query($query);
        if (is_countable($result)) {
            foreach ($result as $row) {
                if (!isset($data[$row["p"]])) {
                    $data[$row["p"]] = array();
                }
                $data[$row["p"]][] = ($row["o_type"] == "bnode") ? $this->getDataFromBnode($row["o"]) : $row["o"];
            }
        }
        return $data;
    }


    /**
     * Retourne le graphe sous forme de RDF
     * @return string
     */
    public function getRDFFromGraph()
    {
        if ("" == $this->getGraphURI()) {
            return "";
        }

        $rdf = "";
        $currentElementNumber = 0;
        $ser = ARC2::getRDFXMLSerializer(array('ns' => $this->ns ?? array()));
        $start = true;

        while (true) {
            $result = $this->store->query($this->format_namespaces() .
                "SELECT ?s ?p ?o WHERE {
                    GRAPH <" . $this->getGraphURI() . "> { ?s ?p ?o }
                } LIMIT 10000 OFFSET " . $currentElementNumber);
            if (empty($result["result"]) || empty($result["result"]["rows"])) {
                break;
            }
            $currentElementNumber += count($result["result"]["rows"]);
            $content = $ser->getSerializedTriples($result["result"]["rows"]);
            //Gestion du fichier pour n'avoir qu'une seule balise <rdf:RDF>
            if ($start) {
                $start = false;
                $content = preg_replace('/<\/rdf:RDF>/', '', $content);
            } else {
                $content = preg_replace('/<\?xml[^>]*>\s*<rdf:RDF[^>]*>.*?/', '', $content);
                $content = preg_replace('/<\/rdf:RDF>/', '', $content);
            }
            $rdf .= $content;
        }

        return $rdf . "</rdf:RDF>";
    }
}
