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

namespace Pmb\ImportExport\Models\Sources;

use Pmb\Common\Helper\Helper;
use Pmb\Common\Helper\ParserMessage;
use Pmb\ImportExport\Models\Chunks\Chunk;
use Pmb\ImportExport\Models\Transformers\TransformerInterface;
use Pmb\ImportExport\Models\ImportExportParserDirectory;
use Pmb\ImportExport\Models\ImportExportRoot;
use Pmb\ImportExport\Models\RDFTransformers\RDFTransformer;
use stdClass;

class Source extends ImportExportRoot
{
    use ParserMessage;

    protected static $transformersManifests = null;
    protected static $sourcesTypes = null;

    public $id = 0;
    public $sourceName = '';
    public $sourceComment = '';
    public $sourceType = '';
    public $sourceSettings = null;
    public $numScenario = 0;

    protected $RDFTransformer = null;
    protected $transformers = [];

    protected $outFormat;
    protected $chunk;
    protected $baseParameters;
    protected $contextParameters;
    protected $ormName = "Pmb\ImportExport\Orm\SourceOrm";
    protected $caller = null;

    /**
     * Constructeur
     *
     * @param integer $id : id source
     * @param object $caller : object appellant
     * @param boolean $runMode : mode execution
     */
    public function __construct($id = 0, $caller = null, bool $runMode = false)
    {
        parent::__construct($id);
        $this->caller = $caller;
        if ($id && $runMode) {
            $this->configure();
        }
    }

    public function setFromForm(object $data)
    {
        $this->sourceName = $data->sourceName ?? '';
        $this->sourceComment = $data->sourceComment ?? '';
        $this->sourceType = $data->sourceType ?? '';
        $this->sourceSettings = $data->sourceSettings ?? null;
    }

    public function save()
    {
        $orm = new $this->ormName($this->id);

        $orm->source_name = $this->sourceName;
        $orm->source_comment = $this->sourceComment;
        $orm->source_type = $this->sourceType;
        $orm->num_scenario = $this->numScenario;
        $orm->source_settings = \encoding_normalize::json_encode($this->sourceSettings);
        $orm->save();
        if (!$this->id) {
            $this->id = $orm->id_source;
        }
        return $orm;
    }

    public function remove()
    {
        $orm = new $this->ormName($this->id);
        try {
            $orm->delete();
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
    }

    public function duplicate()
    {
        $newSource = clone $this;

        $newSource->id = 0;
        $newSource->sourceName .= " - copy";
        $newSource->save();

        return $newSource;
    }

    public function setRDFTransformer(RDFTransformer $RDFTransformer)
    {
        $this->RDFTransformer = $RDFTransformer;
    }

    public function setChunk(Chunk $chunk)
    {
        $this->chunk = $chunk;
    }

    public function getChunk()
    {
        return $this->chunk;
    }

    public function getTransformers()
    {
        return $this->transformers;
    }

    public function addTransformer(TransformerInterface $transformer)
    {
        $this->transformers[] = $transformer;
    }

    public function next()
    {
        if (is_object($this->chunk)) {
            //On parcourt les chunks
            foreach ($this->chunk->next() as $chunk) {
                if (is_null($chunk)) {
                    yield null;
                }
                foreach ($this->transformers as $transformer) {
                    $chunk = $transformer->transform($chunk);
                }
                yield $chunk;
            }
            //Fini, on génère donc la structure RDF
            $this->RDFTransformer->generateTriplesDescriptions();
            $this->getOntology();
        }
    }

    public function initSync() {}

    public function closeSync() {}

    public function getOntology()
    {
        $onto = $this->RDFTransformer->getStore()->getOntology();
        return $onto;
    }

    public function setBaseParameters(array $parameters)
    {
        $this->baseParameters = $parameters;
    }

    public function getBaseParameters() {}

    public function setContextParameters(array $parameters)
    {
        $this->contextParameters = $parameters;
    }

    public function getContextParameters() {}

    public function setNumScenario(int $numScenario)
    {
        $this->numScenario = $numScenario;
    }

    public function toTriples($entity)
    {
        $this->RDFTransformer->toTriples($entity);
    }

    /**
     * Retourne la liste des types de sources
     *
     * @return array
     */
    final public static function getSourcesTypes()
    {
        if (!is_null(static::$sourcesTypes)) {
            return static::$sourcesTypes;
        }

        static::$sourcesTypes = [];
        $parser = ImportExportParserDirectory::getInstance();
        $manifests = $parser->getManifests(str_replace('\\', '/', __NAMESPACE__));
        foreach ($manifests as $manifest) {
            if (!empty($manifest->type) && strpos($manifest->type, "Source") !== false) {
                if (method_exists($manifest->namespace, "getSourceType")) {
                    static::$sourcesTypes[] = $manifest->namespace::getSourceType();
                }
            }
        }
        return static::$sourcesTypes;
    }

    /**
     * Retourne le type de source
     * Peut se deriver pour certains types de sources
     *
     * @return array
     */
    public static function getSourceType()
    {
        $parser = ImportExportParserDirectory::getInstance();
        $manifest = $parser->getManifestByNamespace(static::class);
        $messages = $manifest->namespace::getMessages();
        $compatibility = $parser->getCompatibility($manifest->namespace);
        $compatiblesFormats = array();
        if (!empty($compatibility["chunk"])) {
            if (is_array($compatibility["chunk"])) {
                foreach ($compatibility["chunk"] as $chunk) {
                    $chunkManifest = $parser->getManifestByNamespace($chunk);

                    $compatiblesFormats[] = array(
                        "format" => $chunkManifest->format,
                        "namespace" => $chunkManifest->namespace,
                        "settings" => $chunkManifest->settings,
                        "msg" =>  $chunkManifest->namespace::getMessages(),
                        "transformers" => static::getCompatiblesTransformers($chunk)
                    );
                }
            } else {
                $chunkManifest = $parser->getManifestByNamespace($compatibility["chunk"]);
                $compatiblesFormats[] = array(
                    "format" => $chunkManifest->format,
                    "namespace" => $chunkManifest->namespace,
                    "settings" => $chunkManifest->settings,
                    "msg" =>  $chunkManifest->namespace::getMessages(),
                    "transformers" => static::getCompatiblesTransformers($compatibility["chunk"])
                );
            }
        }
        return array(
            'type' => $manifest->type,
            'namespace' => $manifest->namespace,
            'settings' => $manifest->settings,
            'contextParameters' => $manifest->contextSettings,
            'msg' =>  $messages,
            'formats' => $compatiblesFormats
        );
    }

    /**
     * Retourne la liste des transformers compatibles avec le chunk passé en parametre
     *
     * @param string $chunk : Chunk namespace
     * @return array
     */
    public static function getCompatiblesTransformers($chunk)
    {
        if (is_null(static::$transformersManifests)) {

            static::$transformersManifests = [];

            $parser = ImportExportParserDirectory::getInstance();
            $transformersManifests = $parser->getManifests("Pmb/ImportExport/Models/Transformers");
            foreach ($transformersManifests as $transformerManifest) {
                $compatibility = $parser->getCompatibility($transformerManifest->namespace);
                if (!empty($compatibility["chunk"])) {
                    $transformer = array(
                        "namespace" => $transformerManifest->namespace,
                        "settings" => $transformerManifest->settings,
                        "contextParameters" => $transformerManifest->contextSettings,
                        "msg" =>  $transformerManifest->namespace::getMessages()
                    );
                    if (!is_countable($compatibility["chunk"])) {
                        static::$transformersManifests[$compatibility["chunk"]][] = $transformer;
                        continue;
                    }
                    foreach ($compatibility["chunk"] as $compatibleChunk) {
                        static::$transformersManifests[$compatibleChunk][] = $transformer;
                    }
                }
            }
        }

        if (array_key_exists($chunk, static::$transformersManifests)) {
            return static::$transformersManifests[$chunk];
        }

        return [];
    }

    protected function configure()
    {
        $this->instantiateChunk();
        $this->instantiateTransformers();
    }

    /**
     * Instanciation du Chunk de la source
     *
     * @return void
     */
    protected function instantiateChunk()
    {
        if (empty($this->sourceSettings->entryFormat)) {
            return;
        }
        $chunkClass = $this->sourceSettings->entryFormat ?? '';
        $chunkSettings = Helper::toArray($this->sourceSettings->{$this->sourceSettings->entryFormat} ?? []);
        $resource = null;
        if (class_exists($chunkClass)) {
            $chunkObject = new $chunkClass($resource, $chunkSettings);
            $this->setChunk($chunkObject);

            $rdfChunkClass = $this->getRDFChunkClass();
            $this->instantiateRDFTransformer($rdfChunkClass);
        }
    }

    protected function getRDFChunkClass()
    {
        $chunkClass = $this->sourceSettings->entryFormat ?? '';
        $transformers = $this->getTransformers();
        if (!empty($transformers)) {
            $chunksTypes = chunk::getChunksList();
            foreach ($transformers as $transformer) {
                if (!empty($transformer->getSettings()['outFormat'])) {
                    foreach ($chunksTypes as $chunkType) {
                        if ($chunkType['format'] == $transformer->getSettings()['outFormat']) {
                            $chunkClass = $chunkType['namespace'];
                        }
                    }
                }
            }
        }
        return $chunkClass;
    }

    /**
     * Instanciation des transformers de la source
     *
     * @return void
     */
    protected function instantiateTransformers()
    {
        if (empty($this->sourceSettings->transformers)) {
            return;
        }

        foreach ($this->sourceSettings->transformers as $transformer) {

            $transformerClass = $transformer->namespace ?? '';
            $transformerSettings =  Helper::toArray($transformer->settings ?? []);
            if ($transformerClass && class_exists($transformerClass)) {
                $this->addTransformer(new $transformerClass($transformerSettings));
            }
        }
    }

    /**
     * Instanciation du RDFTransformer de la source
     *
     * @param string $chunkClass : namespace Chunk
     * @return void
     */
    protected function instantiateRDFTransformer(string $chunkClass = '')
    {
        $RDFTransformerClass = '';
        $chunksTypes = chunk::getChunksList();
        foreach ($chunksTypes as $chunkType) {
            if ($chunkType['namespace'] == $chunkClass) {
                $this->outFormat = $chunkType['format'];
                $RDFTransformerClass = $chunkType['rdfTransformer'];
                break;
            }
        }
        if ($RDFTransformerClass && class_exists($RDFTransformerClass)) {
            $this->RDFTransformer = new $RDFTransformerClass($this);
        }
    }

    public function getRDFTransformer()
    {
        return $this->RDFTransformer;
    }

    public function getOutFormat()
    {
        return $this->outFormat;
    }

    public function setOutFormat(string $outFormat)
    {
        $this->outFormat = $outFormat;
    }

    public function import(stdClass $data, int $numScenario)
    {
        $this->sourceName = $data->sourceName ?? "";
        $this->sourceComment = $data->sourceComment ?? "";
        $this->sourceType = $data->sourceType ?? "";
        $this->sourceSettings = $data->sourceSettings ?? null;
        $this->numScenario = $numScenario;
        $this->save();
    }
}
