<?php
// +-------------------------------------------------+
// © 2002-2004 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: ProfileImport.php,v 1.2.2.6 2026/04/09 12:18:48 rtigero Exp $

namespace Pmb\ImportExport\Models\Profiles;

use encoding_normalize;
use onto_common_uri;
use Pmb\Common\Helper\GlobalContext;
use Pmb\ImportExport\Helper\HelperImportExport;
use Pmb\ImportExport\Models\Integrators\RdfEntitiesIntegratorImportExport;
use Pmb\ImportExport\Models\Ontology\OntologyPMB;
use Pmb\ImportExport\Models\Ontology\Store;
use Pmb\ImportExport\Models\Profiles\ProfileEntity;
use rdf_entities_store_arc2;
use skos_concept;
use skos_datastore;

class ProfileImport extends Profile
{
    protected const PROFILE_PURPOSE = "import";

    public const CUSTOM_PREFIXES = array(
        "anim_",
        "author_",
        "authperso_",
        "categ_",
        "cms_editorial_",
        "collection_",
        "collstate_",
        "demandes_",
        "empr_",
        "expl_",
        "explnum_",
        "gestfic0_",
        "indexint_",
        "notices_",
        "pret_",
        "publisher_",
        "serie_",
        "skos_",
        "subcollection_",
        "tu_"
    );

    public $profileType = "all_entities";

    /**
     * Tableau des entités à intégrer
     */
    protected static $entitiesList = array();

    /**
     * Intégrateur spécifique à l'import export
     */
    protected static RdfEntitiesIntegratorImportExport $integratorImportExport;

    /**
     * Store de la source
     */
    protected Store $store;

    /**
     * Ontologie PMB
     */
    protected OntologyPMB $onto;

    /**
     * URI de l'entité que le profile traite
     */
    protected string $uri = "";

    /**
     * Données de l'entité que le profile traite
     */
    protected array $entityData = array();

    /**
     * Profil du type d'entité que le profil traite
     */
    protected ProfileEntity $entityProfile;

    /**
     * Tableau des propriétés PMB du type d'entité
     */
    protected array $entityProperties = array();

    /**
     * Identifiant PMB de l'entité en cas de remplacement
     */
    protected $entityId = 0;

    /**
     * Comportement à adopter à l'import
     */
    protected $behavior = false;

    /**
     * Intégrateur RDF spécifique au type d'entité
     */
    protected $rdfIntegrator = null;

    /**
     * URI du type d'entité que le profile traite
     */
    protected $entityType = "";

    protected $storeEntities = array();

    public function applyProfile()
    {
        $this->setupProfile();
        $this->integrate();
    }

    protected function setupProfile()
    {
        $this->onto = OntologyPMB::getInstance();
    }

    /**
     * Intégration des entités pmb présentes dans le store de l'import export
     *
     * @return void
     */
    public function integrate()
    {
        $ontoEntities = $this->onto->getEntities();
        $store = new rdf_entities_store_arc2(array("store_name" => $this->store->config['store_name']));
        static::$integratorImportExport = new RdfEntitiesIntegratorImportExport($store);

        foreach ($ontoEntities as $entity) {
            //Si l'entité n'est pas activée, on passe à la suite
            if ($this->isEntityMustBeImported($entity->uri) == false) {
                continue;
            }
            $this->entityType = $entity->uri;
            $this->entityProfile = $this->getEntity($entity->uri);
            $this->entityProperties = $ontoEntities[$this->entityType]->properties;
            $this->rdfIntegrator = static::$integratorImportExport->get_entity_integrator_from_type_uri($entity->uri);

            $type = "pmb:" . $entity->name;
            //Cas des concepts qui sont decrits en skos et non dans l'ontologie pmb
            if ($entity->name == "concept") {
                $type = "skos:Concept";
            }

            $query = "SELECT ?uri WHERE {
                ?uri rdf:type $type .
            }";

            $result = $this->store->query($query);
            if (is_countable($result)) {
                //Si on n'a pas de profil particulier on ajoute juste la liste des uri dans les entités à importer
                if (is_null($this->entityProfile)) {
                    static::$entitiesList = array_merge(static::$entitiesList, array_column($result, 'uri'));
                    continue;
                }
                //On supprime les liens vers les entités ignorées dans l'import
                $this->removeIgnoredEntitiesLinks();
                $this->storeEntities = $result;
                while (! empty($this->storeEntities)) {
                    $row = array_shift($this->storeEntities);
                    $this->uri = $row['uri'];
                    $this->getEntityData();

                    if (empty($this->entityData)) {
                        continue;
                    }

                    $this->checkDeduplication();

                    if ($this->needSetup()) {
                        //On clear les données de l'entité sauf le rdf:type et l'identifiant
                        $this->clearEntityInStore($this->uri, false, "?predicate != rdf:type && ?predicate != pmb:identifier");
                        $this->setupEntity();
                    }

                    //On ajoute l'entité dans la liste des entités prêtes
                    static::$entitiesList[] = $this->uri;
                }
            }
        }
        //Le store est prêt, on lance l'intégration
        $this->integrateEntities();
    }

    /**
     * Récupère les données associées à l'entité dans le store et les stocke dans $this->entityData
     *
     * @return void
     */
    protected function getEntityData()
    {
        $this->entityData = array();
        $query = "SELECT * WHERE {
            <" . $this->uri . "> ?predicate ?object .
        }";
        $result = $this->store->query($query);

        if (is_countable($result)) {
            foreach ($result as $row) {
                //Si la propriété n'est pas définie dans l'ontologie, on ignore
                if (! array_key_exists($row['predicate'], $this->entityProperties)) {
                    continue;
                }

                if (! isset($this->entityData[$row['predicate']])) {
                    $this->entityData[$row['predicate']] = array();
                }

                $this->entityData[$row['predicate']][] = $row['object'];
            }
        }
    }

    /**
     * Effectue le traitement de dédoublonnage et remplit la propriété $this->entityId
     * avec l'id de l'entité si un doublon est trouvé
     *
     * @return void
     */
    protected function checkDeduplication()
    {
        //On dédoublonne d'abord le store pour retirer les entités superflues
        $this->checkStoreDeduplication();

        $this->entityId = 0;

        //Pas de paramétrage, donc pas de dédoublonnage
        if (is_null($this->entityProfile) || is_null($this->entityProfile->entitySettings)) {
            return;
        }

        $deduplicateFields = $this->entityProfile->entitySettings->doublon->fields;

        if (empty($deduplicateFields)) {
            return;
        }

        //Le retour du cas particulier des concepts
        if ($this->entityType == 'http://www.w3.org/2004/02/skos/core#Concept') {
            $query = "";
            foreach ($deduplicateFields as $field) {
                $code = $field->fieldCode;
                //TODO faire mieux et gerer les operateurs
                if ($field->fieldCode == "http://www.pmbservices.fr/ontology#label_multilingue") {
                    $code = "http://www.w3.org/2004/02/skos/core#prefLabel";
                }
                $query .= " ?uri <{$code}> \"" . $this->entityData[$field->fieldCode][0] . "\" .\n";
            }
            $query = "select ?uri where {\n" . $query . "}";
            skos_datastore::query($query);
            if (skos_datastore::num_rows()) {
                $results = skos_datastore::get_result();
                $concept = new skos_concept(0, $results[0]->uri);
                $this->entityId = $concept->get_id();
                $query = "insert into <" . $this->store->config['store_name'] . "> {
                    <" . $this->uri . "> pmb:identifier \"" . $this->entityId . "\" .
                }";
                $this->store->query($query);
            }
            return;
        }
        $query = "SELECT " . $this->rdfIntegrator->get_table_key() . " FROM " . $this->rdfIntegrator->get_table_name() . " !!JOIN!! WHERE ";
        $isFirstElement = true;
        $mapFields = $this->rdfIntegrator->get_map_fields();
        $customFields = array();

        foreach ($deduplicateFields as $field) {
            $values = $this->getFieldValues($field);

            if (empty($values)) {
                //Rien a comparer ? on s'en va
                continue;
            }

            //On regarde dans le profil si on doit remplacer la valeur
            //On part du principe qu'on est en remplacement puisqu'on teste le dédoublonnage
            //Nécessaire par exemple pour les thésaurus, où l'on doit reprendre celui paramétré dans le profil
            if (isset($this->entityProfile->entitySettings->replacement)) {
                $i =  array_search($field->fieldCode, array_column($this->entityProfile->entitySettings->replacement->fields, "fieldCode"));
                if ($i !== false && $this->entityProfile->entitySettings->replacement->fields[$i]->fieldOperation->change == "override") {
                    $this->entityData[$field->fieldCode] = array($this->entityProfile->entitySettings->replacement->fields[$i]->fieldDefaultValue);
                }
            }

            if (! array_key_exists($field->fieldCode, $mapFields)) {
                //Pas dans les map fields, c'est peut être un champ perso
                foreach (static::CUSTOM_PREFIXES as $prefix) {
                    //On postule qui si le nom du champ contient le prefix d'un champ perso, alors c'en est un
                    if (str_contains($field->fieldCode, $prefix)) {
                        //On recupere le nom du champ perso
                        $customProperty = $this->onto->getPropertyByURI($field->fieldCode);
                        $customFieldName = str_replace($prefix, "", $customProperty->name);

                        //On doit recuperer le datatype du champ perso
                        $cpResult = pmb_mysql_query("SELECT datatype FROM {$prefix}custom WHERE name = \"{$customFieldName}\"");

                        if (pmb_mysql_num_rows($cpResult) == 0) {
                            //Champ perso inconnu au bataillon, on passe a la suite
                            continue;
                        }

                        $cpDatatype = pmb_mysql_result($cpResult, 0, 0);

                        //On remplit un tableau permettant de gerer l'ajout des differents dédoublonnages de champs perso
                        if (!isset($customFields[$prefix])) {
                            $customFields[$prefix] = array();
                        }

                        if (!$isFirstElement && $field->fieldOperator) {
                            $query .= " " . $field->fieldOperator;
                        }
                        //Pattern qui permettra de placer la clause au bon endroit entre les operateurs
                        $query .=  " !!{$customFieldName}!!";

                        //On génère la clause
                        if (count($values) > 1) {
                            $clause = " ({$prefix}custom_{$cpDatatype} IN (\"" . implode('","', $values) . "\") AND {$prefix}custom.name = \"" . $customFieldName . "\") ";
                        } else {
                            $clause = " ({$prefix}custom_{$cpDatatype} = \"" . $values[0] . "\" AND {$prefix}custom.name = \"" . $customFieldName . "\") ";
                        }

                        $customFields[$prefix][] = array("customFieldName" => $customFieldName, "clause" => $clause);

                        $isFirstElement = false;
                    }
                }
                continue;
            }

            $column = $mapFields[$field->fieldCode];

            if (!$isFirstElement && $field->fieldOperator) {
                $query .= " " . $field->fieldOperator . " ";
            }

            if (count($values) > 1) {
                $query .= " {$column} IN (\"" . implode('","', $values) . "\") ";
            } else {
                $query .= " {$column} = \"" . $values[0] . "\" ";
            }

            $isFirstElement = false;
        }

        if (! $isFirstElement) {
            //Gestion des jointures pour les champs perso
            $join = "";
            if (! empty($customFields)) {
                foreach ($customFields as $prefix => $fields) {
                    //Une jointure par type de champ perso
                    $join .= " JOIN {$prefix}custom_values ON {$this->rdfIntegrator->get_table_key()} = {$prefix}custom_origine
                            JOIN {$prefix}custom ON {$prefix}custom_champ = {$prefix}custom.idchamp ";
                    foreach ($fields as $field) {
                        //Une clause par champ perso
                        $query = str_replace("!!{$field['customFieldName']}!!", $field['clause'], $query);
                    }
                }
            }
            //On ajoute les jointures à la requête
            $query = str_replace("!!JOIN!!", $join, $query);

            $result = pmb_mysql_query($query);
            if (pmb_mysql_num_rows($result)) {
                //Doublon trouvé, on ajoute l'identifiant dans le store
                $this->entityId = intval(pmb_mysql_result($result, 0, $this->rdfIntegrator->get_table_key()));

                $query = "insert into <" . $this->store->config['store_name'] . "> {
	                    <" . $this->uri . "> pmb:identifier \"" . $this->entityId . "\" .
	                }";
                $this->store->query($query);
            }
        }
    }

    /**
     * Teste la présence de doublons dans le store et les supprime le cas échéant
     *
     * @return void
     */
    protected function checkStoreDeduplication()
    {
        $deduplicateFields = $this->entityProfile->entitySettings->doublon->fields;
        $query = "SELECT ?uri WHERE {\n";
        $codes = array();
        $usedFields = array();
        foreach ($deduplicateFields as $field) {
            $value = explode("#", $field->fieldCode);
            $codes[$field->fieldCode] = end($value);
        }

        $operator = "";

        $multiple = false;
        $filter = "";
        for ($i = 0; $i < count($deduplicateFields); $i++) {
            $field = $deduplicateFields[$i];
            $values = array_filter($this->getFieldValues($field));

            if (empty($values)) {
                continue;
            }
            //Ajout d'un opérateur entre filtres
            if ($multiple) {
                $operator = "||";
                if ($field->fieldOperator == "AND") {
                    $operator = "&&";
                }
            }

            $multiple = true;

            if (count($values) > 1) {
                $filter  .= " $operator (?object_{$codes[$field->fieldCode]} IN (\"" . implode('","', $values) . "\")) .\n";
            } else {
                $filter .= " $operator (?object_{$codes[$field->fieldCode]} = \"" . $values[0] . "\")\n";
            }
            //Ajout dans les filtres utilisés
            $usedFields[] = $field->fieldCode;
        }

        if ($filter == "") {
            return;
        }

        //On filtre pour ne pas récupérer l'uri en cours de traitement
        $filter .= " && (?uri != <" . $this->uri . ">)";

        //On construit le select en fonction des filtres utilisés
        foreach ($usedFields as $field) {
            $query .= "?uri <{$field}> ?object_{$codes[$field]} .\n";
        }

        $query .= "FILTER($filter) }";

        $result = $this->store->query($query);
        if (is_countable($result)) {
            foreach ($result as $row) {
                //On rattache les triples du doublons à notre entité
                $query = "insert into <" . $this->store->getGraphURI() . "> CONSTRUCT {
                    ?subject ?predicate <" . $this->uri . "> .
                } WHERE {
                    ?subject ?predicate <" . $row['uri'] . "> .
                }";
                $this->store->query($query);

                $query = "insert into <" . $this->store->getGraphURI() . "> CONSTRUCT {
                    <" . $this->uri . "> ?predicate ?object .
                } WHERE {
                    <" . $row['uri'] . "> ?predicate ?object .
                }";
                $this->store->query($query);

                $this->checkDuplicatesAfterDeduplication();

                //On supprime du store et du tableau des entités le doublon
                if ($this->clearEntityInStore($row['uri'], true)) {
                    $i = array_search($row['uri'], array_column($this->storeEntities, "uri"));
                    if ($i !== false) {
                        array_splice($this->storeEntities, $i, 1);
                    }
                }

                //On reprend les données de l'entité avec ses nouveaux triplets
                $this->getEntityData();
            }
        }
    }

    /**
     * Une méthode qui permet de dédoublonner le dédoublonnage
     * Par exemple si on récupère un auteur auquel l'entité source était déjà liée,
     * on va supprimer cette liaison pour ne pas avoir de doublons de liens
     *
     * @return void
     */
    protected function checkDuplicatesAfterDeduplication(): void
    {
        //On s'assure d'avoir les triplets à jour
        $this->getEntityData();

        //Bon les galères commencent, on peut avoir récupéré des valeurs en doublon
        $triplesData = array();
        foreach ($this->entityData as $value) {
            //Pas de doublons si on a qu'une seule valeur
            if (count($value) == 1) {
                continue;
            }

            //Ici on veut récupérer toutes les entités reliées notre entité
            foreach ($value as $uri) {
                $query = "SELECT ?o ?type from <" . $this->store->getGraphURI() . "> WHERE {
                    <" . $uri . "> ?p ?o .
                    ?o rdf:type ?type .
                }";
                $result = $this->store->query($query);
                if (is_array($result)) {
                    foreach ($result as $triple) {
                        //Les doublons ne devraient concerner que les entités
                        if ($this->onto->entityExists($triple["type"])) {
                            if (! isset($triplesData[$triple["o"]])) {
                                $triplesData[$triple["o"]] = array();
                            }
                            $triplesData[$triple["o"]][] = $uri;
                        }
                    }
                }
            }
        }

        //On supprime les doublons trouvés
        foreach ($triplesData as $triples) {
            if (count($triples) <= 1) {
                continue;
            }
            $entitiesToCompare = array();
            //On récupère les valeurs pour chaque doublon potentiel
            foreach ($triples as $triple) {
                $query = "select ?o ?type from <" . $this->store->getGraphURI() . "> where {
                    <" . $triple . "> ?p ?o .
                    ?o rdf:type ?type .
                }";
                $result = $this->store->query($query);

                if (! is_array($result)) {
                    continue;
                }

                foreach ($result as $uri) {
                    //On ne prend que les entités présentes dans l'ontologie
                    if ($this->onto->entityExists($uri["type"])) {
                        $entitiesToCompare[] = [
                            "origin" => $triple,
                            "entity" => $uri["o"]
                        ];
                    }
                }
            }

            //On cherche les entités qui ont les mêmes prédicats et les mêmes objets
            //C'est plus fastidieux que comparer juste les URI mais ça évite de les doublons cachés
            $dataToCompare = array();
            foreach ($entitiesToCompare as $entity) {
                $query = "select * from <" . $this->store->getGraphURI() . "> where {
                    <" . $entity["entity"] . "> ?p ?o .
                }";
                $result = $this->store->query($query);

                if (! is_array($result)) {
                    continue;
                }

                $isDuplicate = false;

                foreach ($dataToCompare as $entityData) {
                    if (array_diff($result, $entityData) === array()) {
                        //Pas de différences ? c'est un doublon, on supprime
                        $this->clearEntityInStore($entity["origin"], true);
                        $isDuplicate = true;
                        break;
                    }
                }

                if (! $isDuplicate) {
                    $dataToCompare[] = $result;
                }
            }
        }
    }

    /**
     * Retourne la valeur d'un champ en appliquant la ou les fonctions de dédoublonnage
     * addslashes() est aussi appliqué sur les valeurs
     *
     * @param \StdClass $field
     * @return array
     */
    protected function getFieldValues($field)
    {
        if (! array_key_exists($field->fieldCode, $this->entityData)) {
            return array();
        }

        $values = $this->entityData[$field->fieldCode];
        $functions = array();

        if (isset($field->functions) && is_array($field->functions)) {
            $functions = $field->functions;
        }

        for ($i = 0; $i < count($values); $i++) {
            foreach ($functions as $namespace) {
                if (class_exists($namespace)) {
                    $class = new $namespace();
                    $values[$i] = $class->execute($values[$i]);
                }
            }
            $values[$i] = addslashes($values[$i]);
        }

        return $values;
    }

    /**
     * Indique le comportement d'import à adopter pour un champ et remplit la propriété $this->behavior
     * Peut avoir comme valeurs :
     * - false => ne pas importer le champ
     * - true => importer le champ normalement
     * - array("operation" => "operation", "value" => "valeur") => Comportement d'import spécifique
     *
     * @param string $uri
     * @return void
     */
    protected function fetchImportBehavior($uri)
    {
        $this->behavior = false;

        //Aucun paramétrage, on ignore
        if (is_null($this->entityProfile) || is_null($this->entityProfile->entitySettings)) {
            return;
        }

        //Doit-on importer toutes les entités par défaut ?
        if ($this->profileType == "selected_entities") {
            //L'entité en cours est-elle activée ?
            if ($this->entityProfile->entitySettings->activated == false) {
                return;
            }
        }

        //Doit-on regarder le paramétrage en ajout ou en remplacement ?
        $fieldSettings = $this->entityProfile->entitySettings->creation;
        if ($this->entityId) {
            $fieldSettings = $this->entityProfile->entitySettings->replacement;
        }

        //Doit-on importer tous les champs par défaut ?
        if ($fieldSettings->defaultAction == "selection") {
            //Le champ est-il dans la liste des champs à importer ?
            $fieldsURI = array_column($fieldSettings->fields, "fieldCode");
            if (!in_array($uri, $fieldsURI)) {
                return;
            }
        }
        if (! empty($fieldSettings->fields)) {
            $i =  array_search($uri, array_column($fieldSettings->fields, "fieldCode"));
            if ($i !== false) {
                //Le champ en cours est-il paramétré sur "ignorer" ?
                if ($fieldSettings->fields[$i]->fieldOperation->change == "ignore") {
                    return;
                }

                //J'ai un paramétrage spécifique alors je le renvoie
                $this->behavior = [
                    "operation" => $fieldSettings->fields[$i]->fieldOperation,
                    "value" => $fieldSettings->fields[$i]->fieldDefaultValue
                ];
                return;
            }
        }

        //Je suis arrivé ici je n'ai donc pas de paramétrage spécifique => import par défaut
        $this->behavior = true;
    }

    /**
     * Indique si l'entité en cours doit ou non etre importée
     * @param string $uri URI de l'entité dans l'ontologie PMB
     *
     * @return boolean
     */
    protected function isEntityMustBeImported($uri)
    {
        if ($this->profileType == "all_entities") {
            return true;
        }
        $profile = $this->getEntity($uri);

        if (is_null($profile->entitySettings)) {
            return false;
        }

        if ($this->profileType == "selected_entities") {
            if (! isset($profile->entitySettings->activated)) {
                return false;
            }

            //L'entité en cours est-elle activée ?
            if ($profile->entitySettings->activated == false) {
                return false;
            }
        }

        return true;
    }

    /**
     * Applique à l'entité les comportements d'import et modifie le store en conséquence
     *
     * @return void
     */
    protected function setupEntity()
    {
        $uri_list = array_keys($this->entityProperties);
        $query = "insert into <" . $this->store->config['store_name'] . "> {\n";
        foreach ($uri_list as $uri) {
            //Pour chaque prop on applique le profil
            $values = $this->getValuesFromBehavior($uri);
            foreach ($values as $value) {
                if ($value != "") {
                    //On insère dans le store la valeur déterminée à l'aide du profil
                    if ($this->isURI($value)) {
                        $query .= "<" . $this->uri . "> <" . $uri . "> <" . $value . "> .\n";
                    } else {
                        $query .= "<" . $this->uri . "> <" . $uri . "> \"" . addslashes($value) . "\" .\n";
                    }
                }
            }
        }
        $query .= "}";
        $this->store->query($query);
    }

    /**
     * Détermine la ou les valeur(s) à insérer pour un champ
     * Un tableau vide signifie qu'il n'y a rien à faire pour ce champ
     *
     * @param string $uri
     * @return array
     */
    protected function getValuesFromBehavior($uri)
    {
        $this->fetchImportBehavior($uri);
        if ($this->behavior === false) {
            return array();
        }

        //On passe un tableau avec au moins une valeur pour pouvoir appliquer les valeurs par défaut éventuelles
        $values = array("");
        if (array_key_exists($uri, $this->entityData)) {
            $values = $this->entityData[$uri];
        }
        for ($i = 0; $i < count($values); $i++) {
            $value = &$values[$i];
            if (is_array($this->behavior)) {
                if (! $this->entityId) {
                    //Si je suis en ajout
                    if ($this->behavior["operation"]->ignore) {
                        //Si j'ai le flag use_default_value je force la valeur par défaut
                        if ($this->behavior["operation"]->use_default_value) {
                            $value = $this->behavior["value"];
                        } else {
                            //Sinon je m'en vais
                            return array();
                        }
                    }
                } else {
                    //Je modifie la valeur si je suis paramétré sur forcer la valeur par défaut
                    if ($this->behavior["operation"]->change == "override") {
                        $value = $this->behavior["value"];
                    }

                    //Si je suis en modif que mon champ est multiple et que je suis en mode replace ou override, alors je vide la table
                    //En mode add ou ignore, j'ai juste a laisser la table tranquille
                    if (in_array($this->behavior["operation"]->change, array("replace", "override")) && $this->entityProperties[$uri]->maxCardinality == "n") {
                        $linkedEntities = $this->rdfIntegrator->get_linked_entities();
                        if (array_key_exists($uri, $linkedEntities)) {
                            $linkedQueryData = $linkedEntities[$uri];
                            $query = 'delete from ' . $linkedQueryData['table'] . ' where ' . $linkedQueryData['reference_field_name'] . ' = ' . $this->entityId;
                            pmb_mysql_query($query);
                        }
                    }
                }
                //Cas standard je prends la valeur par défaut si j'en ai pas dans le store
                if ($value == "" && ($this->behavior["operation"]->use_default_value || $this->behavior["operation"]->change == "replace" || $this->behavior["operation"]->change == "add")) {
                    $value = $this->behavior["value"];

                    //Si la range est une ressource, on doit insérer les données qui vont bien dans le store
                    //Car il manque certains triplets avec les données issues du profil
                    if ($value && count($this->entityProperties[$uri]->ranges) && is_numeric($value)) {
                        $ranges = array();
                        foreach ($this->entityProperties[$uri]->ranges as $range) {
                            if ($range->uri != "http://www.w3.org/2000/01/rdf-schema#Literal") {
                                $ranges[] = $range->uri;
                            }
                        }
                        if (! empty($ranges)) {
                            $range = $ranges[0];
                            $type = explode("#", $range);
                            $type = $type[1];
                            //Insertion de l'autorité
                            $authorityUri = onto_common_uri::get_temp_uri(GlobalContext::get("opac_url_base") . $type) . "#" . intval($value);
                            $query = "insert into <" . $this->store->config['store_name'] . "> {
                                <" . $this->uri . "> <" . $uri . "> <" . $authorityUri . "> .
                                <" . $authorityUri . "> rdf:type <" . $range . "> .
                                <" . $authorityUri . "> pmb:identifier \"" . addslashes(encoding_normalize::utf8_decode($value)) . "\" .
                            }";
                            $this->store->query($query);
                            $value = $authorityUri;

                            //Cas particulier des responsabilités
                            if (count($ranges) > 1) {
                                //On associe a la propriété $uri un noeud blanc contenant l'auteur et sa fonction (par défaut 070)
                                $bnodeUri = onto_common_uri::get_temp_uri(GlobalContext::get("opac_url_base") . "bnode") . "#" . intval($value);
                                $query = "insert into <" . $this->store->config['store_name'] . "> {
                                    <" . $this->uri . "> <" . $uri . "> <" . $bnodeUri . "> .
                                    <" . $bnodeUri . "> pmb:has_author <" . $authorityUri . "> .
                                    <" . $bnodeUri . "> pmb:author_function \"070\" .
                                }";
                                $this->store->query($query);
                                $value = $bnodeUri;
                                //Gestion des qualifications
                                if (GlobalContext::get("pmb_authors_qualifications")) {
                                    //TODO
                                }
                            }
                        }
                    }
                }
            }
        }
        //Si values est le meme qu'au départ on renvoie un tableau vide
        if (count($values) == 1 && $values[0] == "") {
            return array();
        }
        return $values;
    }

    /**
     * Détermine si une valeur est une URI
     *
     * @param string $value
     * @return boolean
     */
    public function isURI($value)
    {
        $namespaces = array_values($this->store->ns);
        return HelperImportExport::isURI($value, $namespaces);
    }

    /**
     * Retire l'entité du store
     * @param string $entityURI URI de l'entité
     * @param string $filters Contenu qui sera intégré dans le FILTER du SPARQL
     *
     * @return boolean
     */
    protected function clearEntityInStore($entityURI, $clearAsObject = false, $filters = "")
    {
        if ($clearAsObject) {
            $return = $this->clearEntityAsObject($entityURI, $filters);
        }
        if ($this->clearEntityAsSubject($entityURI, $filters)) {
            return $return;
        }
        return false;
    }

    /**
     * Retire les triplets du store où l'entité est sujet
     *
     * @param string $entityURI URI de l'entité
     * @param string $filters Contenu qui sera intégré dans le FILTER du SPARQL
     *
     * @return boolean
     */
    private function clearEntityAsSubject($entityURI, $filters = "")
    {
        $query = "DELETE FROM <" . $this->store->getGraphURI() . "> {
            <" . $entityURI . "> ?predicate ?object .
        } WHERE {
            <" . $entityURI . "> ?predicate ?object .
        ";

        if ($filters != "") {
            $query .= "FILTER(" . $filters . ")";
        }

        $query .= "}";
        $this->store->query($query);

        if (empty($this->store->errors)) {
            return true;
        }
        return false;
    }

    /**
     * Retire les triplets du store où l'entité est objet
     *
     * @param string $entityURI URI de l'entité
     * @param string $filters Contenu qui sera intégré dans le FILTER du SPARQL
     *
     * @return boolean
     */
    private function clearEntityAsObject($entityURI, $filters = "")
    {
        $query = "DELETE FROM <" . $this->store->getGraphURI() . "> {
            ?subject ?predicate <" . $entityURI . "> .
        } WHERE {
            ?subject ?predicate <" . $entityURI . "> .
        ";

        if ($filters != "") {
            $query .= "FILTER(" . $filters . ")";
        }

        $query .= "}";
        $this->store->query($query);

        if (empty($this->store->errors)) {
            return true;
        }
        return false;
    }

    /**
     * Appel des intégrateurs RDF sur la liste des entités présentes dans static::$entitiesList
     *
     * @return void
     */
    protected function integrateEntities()
    {
        foreach (static::$entitiesList as $uri) {
            static::$integratorImportExport->integrate_entity($uri);
        }
    }

    /**
     * Determine si la configuration de l'entité nécessite une modification du store
     * @return boolean
     */
    protected function needSetup()
    {
        if (isset($this->entityProfile) && isset($this->entityProfile->entitySettings)) {
            if ($this->entityId) {
                $behavior = $this->entityProfile->entitySettings->replacement;
            } else {
                $behavior = $this->entityProfile->entitySettings->creation;
            }

            //Si on prend tous les champs et qu'on n'a aucun champ spécifique alors aucun besoin de setup
            if ($behavior->defaultAction == "all" && empty($behavior->fields)) {
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * Supprime tous les liens de l'entité en cours vers les entités non importées
     * @return void
     */
    protected function removeIgnoredEntitiesLinks()
    {
        //On vérifie qu'on intègre bien les entités liées sinon on ne remplit pas les liens vers les autres entités
        $entitiesUri = array_keys($this->onto->getEntities());
        foreach ($this->entityProperties as $uri => $property) {
            if (count($property->ranges)) {
                foreach ($property->ranges as $range) {
                    if (in_array($range->uri, $entitiesUri)) {
                        if ($this->isEntityMustBeImported($range->uri) == false) {
                            //Obligé de passer les requetes une par une, sinon ca marche pas
                            //Merci arc2
                            $query = "DELETE {
                                ?subject <" . $uri . "> ?object
                            } WHERE {
                                ?subject rdf:type <" . $this->entityType . "> .
                                ?subject <" . $uri . "> ?object
                            }";
                            $this->store->query($query);
                        }
                    }
                }
            }
        }
    }
}
