<?php

namespace Webapp\FileManagement\Service;

use Doctrine\Common\Collections\ArrayCollection;
use Mobile\Device\Entity\Block;
use Mobile\Device\Entity\Device;
use Mobile\Device\Entity\EntryNote;
use Mobile\Device\Entity\Factor;
use Mobile\Device\Entity\Individual;
use Mobile\Device\Entity\Modality;
use Mobile\Device\Entity\OutExperimentationZone;
use Mobile\Device\Entity\Protocol;
use Mobile\Device\Entity\SubBlock;
use Mobile\Device\Entity\Treatment;
use Mobile\Device\Entity\UnitParcel;
use Mobile\Measure\Entity\Annotation;
use Mobile\Measure\Entity\DataEntry;
use Mobile\Measure\Entity\FormField;
use Mobile\Measure\Entity\GeneratedField;
use Mobile\Measure\Entity\Measure;
use Mobile\Measure\Entity\Session;
use Mobile\Measure\Entity\Test\Base\Test;
use Mobile\Measure\Entity\Test\CombinationTest;
use Mobile\Measure\Entity\Test\GrowthTest;
use Mobile\Measure\Entity\Test\PreconditionedCalculation;
use Mobile\Measure\Entity\Test\RangeTest;
use Mobile\Measure\Entity\Variable\Base\Variable;
use Mobile\Measure\Entity\Variable\Driver;
use Mobile\Measure\Entity\Variable\GeneratorVariable;
use Mobile\Measure\Entity\Variable\Mark;
use Mobile\Measure\Entity\Variable\Material;
use Mobile\Measure\Entity\Variable\PreviousValue;
use Mobile\Measure\Entity\Variable\RequiredAnnotation;
use Mobile\Measure\Entity\Variable\Scale;
use Mobile\Measure\Entity\Variable\StateCode;
use Mobile\Measure\Entity\Variable\UniqueVariable;
use Mobile\Measure\Entity\Variable\ValueHint;
use Mobile\Measure\Entity\Variable\ValueHintList;
use Mobile\Project\Entity\DataEntryProject;
use Mobile\Project\Entity\GraphicalStructure;
use Mobile\Project\Entity\NatureZHE;
use Mobile\Project\Entity\Platform;
use Mobile\Project\Entity\Workpath;
use ReflectionClass;
use ReflectionException;
use Shared\Authentication\Repository\UserRepository;
use SimpleXMLElement;
use Webapp\Core\Enumeration\PathLevelEnum;
use Webapp\Core\Enumeration\VariableFormatEnum;
use Webapp\Core\Enumeration\VariableTypeEnum;
use Webapp\FileManagement\Exception\ParsingException;

/**
 * Class MobileReaderHelper.
 */
class MobileReaderHelper extends AbstractReaderHelper
{

    /**
     * MobileReaderHelper constructor.
     * @param UserRepository $userRepository
     * @param string $projectDir
     */
    public function __construct(UserRepository $userRepository, string $projectDir)
    {
        parent::__construct($userRepository, $projectDir);
    }

    /**
     * @param SimpleXMLElement $projectXml
     * @param $uriMap
     * @param DataEntryProject $project
     * @throws ParsingException
     */
    private function parseProject(SimpleXMLElement $projectXml, &$uriMap, DataEntryProject $project, string $projectUri)
    {
        $this->testProject($projectXml);
        $project->setName($projectXml->attributes()['nom']);
        $project->setCreationDate(date_create($projectXml->attributes()['dateCreation']));

        if (isset($projectXml->children()->materiel)) {
            $materialCnt = 0;
            foreach ($projectXml->children()->materiel as $materialXml) {
                $materialUri = $projectUri . "/@materiel." . $materialCnt;
                $material = $this->parseMaterial($materialXml, $uriMap, $materialUri);
                $project->addMaterial($material);
                $uriMap[$materialUri] = $material;
                $materialCnt++;
            }
        }

        if (isset($projectXml->children()->variables)) {
            $variableCnt = 0;
            foreach ($projectXml->children()->variables as $variableXml) {
                $variableUri = $projectUri . "/@variables." . $variableCnt;
                $variable = $this->parseVariable($variableXml, $uriMap, $variableUri);
                $project->addVariable($variable);
                $uriMap[$variableUri] = $variable;
                $variableCnt++;
            }

            // Test references variables, all variables must be handled before parsing tests
            $variableCnt = 0;
            foreach ($projectXml->children()->variables as $variableXml) {
                $variableUri = $projectUri . "/@variables." . $variableCnt;
                $variable = $uriMap[$variableUri];
                if (isset($variableXml->children()->tests)) {
                    foreach ($variableXml->children()->tests as $testXml) {
                        $test = $this->parseTest($testXml, $uriMap, $variableUri);
                        $variable->addTest($test);
                    }
                }
                $variableCnt++;
            }
        }
    }

    /**
     * @param SimpleXMLElement $deviceXml
     * @return Material
     */
    private function parseMaterial(SimpleXMLElement $deviceXml, array $uriMap, string $deviceUri): Material
    {
        $this->testDevice($deviceXml, $uriMap, $deviceUri);
        $material = new Material();
        $material->setName($deviceXml->attributes()["nom"]);
        if (isset($deviceXml->attributes()["type"])) {
            $material->setType($deviceXml->attributes()["type"]);
        }
        if (isset($deviceXml->attributes()["fabriquant"])) {
            $material->setManufacturer($deviceXml->attributes()["fabriquant"]);
        }
        if (isset($deviceXml->attributes()["appareil"])) {
            $material->setPhysicalDevice($deviceXml->attributes()["appareil"]);
        }
        $driver = $this->parseDriver($deviceXml->children()->driver);
        $material->setDriver($driver);

        return $material;
    }

    /**
     * @param SimpleXMLElement $driverXml
     * @return Driver
     */
    private function parseDriver(SimpleXMLElement $driverXml): Driver
    {
        $driver = new Driver();
        if (isset($driverXml->attributes("xsi", true)["type"]) && $driverXml->attributes("xsi", true)["type"]->__toString() === "adonis.modeleMetier.projetDeSaisie.variables:DriverCom") {
            $driver->setType("rs232");
            $driver->setBaudrate(intval($driverXml->attributes()["baudrate"]));
            $driver->setStopbit(floatval($driverXml->attributes()["stopbit"]));
            $driver->setParity($driverXml->attributes()["parity"]);
            if (isset($driverXml->attributes()["flowcontrol"])) {
                $driver->setFlowControl($driverXml->attributes()["flowcontrol"]);
            } else {
                $driver->setFlowControl("none");
            }
            if (isset($driverXml->attributes()["push"])) {
                $driver->setPush($driverXml->attributes()["push"]->__toString() === "true");
            } else {
                $driver->setPush(false);
            }

            if (isset($driverXml->attributes()["request"])) {
                $driver->setRequest($driverXml->attributes()["request"]);
            }
            if (isset($driverXml->attributes()["databitsFormat"])) {
                $driver->setDatabitsFormat(intval($driverXml->attributes()["databitsFormat"]));
            }
            if (isset($driverXml->attributes()["port"])) {
                $driver->setPort($driverXml->attributes()["port"]);
            }


        } else {
            $driver->setType($driverXml->attributes()["type"]);
        }
        $driver->setTimeout(isset($driverXml->attributes()['timeout']) ? intval($driverXml->attributes()['timeout']->__toString()) : 0);
        $driver->setFrameLength(isset($driverXml->attributes()['tailleTrame']) ? intval($driverXml->attributes()['tailleTrame']->__toString()) : 0);
        if (isset($driverXml->attributes()["debutTrame"])) {
            $driver->setFrameStart($driverXml->attributes()["debutTrame"]);
        }
        if (isset($driverXml->attributes()["finTrame"])) {
            $driver->setFrameEnd($driverXml->attributes()["finTrame"]);
        }
        if (isset($driverXml->attributes()["separateurCsv"])) {
            $driver->setCsvSeparator($driverXml->attributes()["separateurCsv"]);
        }
        return $driver;
    }

    /**
     * @param SimpleXMLElement $variableXml
     * @param array $uriMap
     * @param string $variableUri
     * @return Variable
     * @throws ParsingException
     */
    public function parseVariable(SimpleXMLElement $variableXml, array &$uriMap, string $variableUri): Variable
    {
        $this->testVariable($variableXml, $uriMap, $variableUri);
        if (isset($variableXml->attributes()['nomGenere'])) {
            $variable = new GeneratorVariable();
            $variable->setGeneratedPrefix($variableXml->attributes()['nomGenere']);
            $variable->setNumeralIncrement(isset($variableXml->attributes()['extension']) ? false : true);
            $variableCNT = 0;
            foreach ($variableXml->children()->variablesGenerees as $generatedVariableXML) {
                $generatedVariableURI = $variableUri . "/@variablesGenerees." . $variableCNT;
                $generatedVariable = $this->parseVariable($generatedVariableXML, $uriMap, $generatedVariableURI);
                $variable->addGeneratedVariable($generatedVariable);
                $uriMap[$generatedVariableURI] = $generatedVariable;
                $variableCNT++;
            }
        } else {
            $variable = new UniqueVariable();
            if (isset($variableXml->children()->listevaleur)) {
                $valueHintList = $this->parseValueHintList($variableXml->children()->listevaleur);
                $variable->setValueHintList($valueHintList);
            }
        }
        $variable->setCreationDate(date_create($variableXml->attributes()['dateCreation']));
        $variable->setModificationDate(date_create($variableXml->attributes()['dateDerniereModification']));
        $variable->setMandatory(isset($variableXml->attributes()['saisieObligatoire']) && $variableXml->attributes()['saisieObligatoire'] == "true");
        $variable->setName($variableXml->attributes()['nom']);
        $variable->setShortName($variableXml->attributes()['nomCourt']);
        $variable->setRepetitions(intval($variableXml->attributes()['nbRepetitionSaisies']->__toString()));
        switch ($variableXml->attributes()['typeVariable']) {
            case 'reel':
                $variable->setType(VariableTypeEnum::REAL);
                break;
            case 'booleen':
                $variable->setType(VariableTypeEnum::BOOLEAN);
                break;
            case 'entiere':
                $variable->setType(VariableTypeEnum::INTEGER);
                break;
            case 'date':
                $variable->setType(VariableTypeEnum::DATE);
                break;
            case 'heure':
                $variable->setType(VariableTypeEnum::HOUR);
                break;
            default:
                $variable->setType(VariableTypeEnum::ALPHANUMERIC);
        }
        $variable->setActive($variableXml->attributes()['active'] == "true");
        $variable->setOrder(isset($variableXml->attributes()['ordre']) ? intval($variableXml->attributes()['ordre']->__toString()) : 0);

        switch ($variableXml->attributes()['uniteParcoursType']) {
            case 'Dispositif':
                $variable->setPathLevel(PathLevelEnum::EXPERIMENT);
                break;
            case 'Bloc':
                $variable->setPathLevel(PathLevelEnum::BLOCK);
                break;
            case 'Sous Bloc':
                $variable->setPathLevel(PathLevelEnum::SUB_BLOCK);
                break;
            case 'Individu':
                $variable->setPathLevel(PathLevelEnum::INDIVIDUAL);
                break;
            default:
                $variable->setPathLevel(PathLevelEnum::UNIT_PLOT);
        }

        if (isset($variableXml->attributes()['horodatage'])) {
            $variable->setAskTimestamp($variableXml->attributes()['horodatage'] == "true");
        } else {
            $variable->setAskTimestamp(false);
        }
        if (isset($variableXml->attributes()['commentaire'])) {
            $variable->setComment($variableXml->attributes()['commentaire']);
        }
        if ($variable->getType() === VariableTypeEnum::REAL) {
            $variable->setFormat("1");
        }
        if (isset($variableXml->attributes()['format']) && $variableXml->attributes()['format']->__toString() !== "") {
            switch ($variableXml->attributes()['format']) {
                case 'quantieme':
                    $variable->setFormat(VariableFormatEnum::QUANTIEM);
                    break;
                default:
                    $variable->setFormat($variableXml->attributes()['format']);
            }
        }
        if (isset($variableXml->attributes()['valeurDefaut'])) {
            $variable->setDefaultValue($variableXml->attributes()['valeurDefaut']);
        }
        if (isset($variableXml->attributes()['unite'])) {
            $variable->setUnit($variableXml->attributes()['unite']);
        }
        if (isset($variableXml->attributes("xsi", true)['type']) && $variableXml->attributes("xsi", true)['type']->__toString() === "adonis.modeleMetier.projetDeSaisie.variables:VariableSemiAuto") {
            $variable->setFrameStartPosition(intval($variableXml->attributes()['debut']));
            $variable->setFrameEndPosition(intval($variableXml->attributes()['fin']));
            $uriMap[$variableXml->attributes()['materiel']->__toString()]->addVariable($variable);
        }

        if (isset($variableXml->children()->mesuresVariablesPrechargees)) {
            foreach ($variableXml->children()->mesuresVariablesPrechargees as $preloadedValueXml) {
                $preloadedValue = $this->parsePreviousValue($preloadedValueXml, $uriMap);
                $variable->addPreviousValue($preloadedValue);
            }
        }

        if (isset($variableXml->children()->echelle)) {
            $scale = $this->parseScale($variableXml->children()->echelle);
            $variable->setScale($scale);
        }

        return $variable;
    }

    /**
     * @param SimpleXMLElement $valueListXml
     * @return ValueHintList
     * @throws ParsingException
     */
    private function parseValueHintList(SimpleXMLElement $valueListXml): ValueHintList
    {
        $this->testValueHintList($valueListXml);
        $valueHintList = new ValueHintList();
        $valueHintList->setName($valueListXml->attributes()['nom']);
        foreach ($valueListXml->children()->valeurs as $valueHintXml) {
            $valueHint = new ValueHint();
            $valueHint->setValue($valueHintXml->__toString());
            $valueHintList->addValueHint($valueHint);
        }
        return $valueHintList;
    }

    /**
     * @param SimpleXMLElement $previousValueXml
     * @param array $uriMap
     * @return PreviousValue
     * @throws ParsingException
     */
    public function parsePreviousValue(SimpleXMLElement $previousValueXml, array $uriMap): PreviousValue
    {
        $this->testPreviousValue($previousValueXml, $uriMap);
        $previousValue = new PreviousValue();
        $previousValue->setOriginVariable($uriMap[$previousValueXml->attributes()['variable']->__toString()]);
        if (isset($previousValueXml->attributes()['horodatage'])) {
            $previousValue->setDate(date_create($previousValueXml->attributes()['horodatage']));
        }
        if (
            isset($previousValueXml->attributes()['codeEtat']) &&
            isset($uriMap[(string)$previousValueXml->attributes()['codeEtat']])
        ) {
            $stateCode = $uriMap[(string)$previousValueXml->attributes()['codeEtat']];
            $previousValue->setState($this->stateCodes[$stateCode->getCode()]);
        }
        $previousValue->setValue(isset($previousValueXml->attributes()['valeur']) ? $previousValueXml->attributes()['valeur'] : null);
        $objectUri = $uriMap[$previousValueXml->attributes()['objetMetier']->__toString()]->getUri();
        $previousValue->setObjectUri($objectUri);
        return $previousValue;
    }

    /**
     * @param SimpleXMLElement $scaleXml
     * @return Scale
     * @throws ParsingException
     */
    public function parseScale(SimpleXMLElement $scaleXml): Scale
    {
        $this->testScale($scaleXml);
        $scale = new Scale();
        $scale->setName($scaleXml->attributes()['nom']);
        $scale->setMinValue(isset($scaleXml->attributes()['valeurMin']) ? intval($scaleXml->attributes()['valeurMin']->__toString()) : 0);
        $scale->setMaxValue(isset($scaleXml->attributes()['valeurMax']) ? intval($scaleXml->attributes()['valeurMax']->__toString()) : 0);
        foreach ($scaleXml->children()->notations as $markXml) {
            $mark = $this->parseMark($markXml);
            $scale->addMark($mark);
        }

        return $scale;
    }

    /**
     * @param SimpleXMLElement $markXml
     * @return Mark
     * @throws ParsingException
     */
    public function parseMark(SimpleXMLElement $markXml): Mark
    {
        $this->testMark($markXml);
        $mark = new Mark();
        $mark->setText($markXml->attributes()['texte']);
        $mark->setValue(isset($markXml->attributes()['valeur']) ? intval($markXml->attributes()['valeur']->__toString()) : 0);
        if ($markXml->attributes()['image'] != "") {
            $mark->setImageName($this->projectDir . 'echelles/' . $markXml->attributes()['image']);
        }
        return $mark;
    }

    /**
     * @param SimpleXMLElement $testXml
     * @param array $uriMap
     * @return Test
     * @throws ParsingException
     */
    private function parseTest(SimpleXMLElement $testXml, array $uriMap, string $variableUri): Test
    {
        $this->testTest($testXml, $uriMap, $variableUri);
        switch ($testXml->attributes()['nom']->__toString()) {
            case "Test sur intervalle" :
                $test = new RangeTest();
                $test->setOptionalMinValue(
                    isset($testXml->children()->limiteOptionnel) &&
                    isset($testXml->children()->limiteOptionnel->attributes()['minDouble']) ?
                        floatval($testXml->children()->limiteOptionnel->attributes()['minDouble']->__toString()) :
                        0
                );
                $test->setOptionalMaxValue(
                    isset($testXml->children()->limiteOptionnel) &&
                    isset($testXml->children()->limiteOptionnel->attributes()['maxDouble']) ?
                        floatval($testXml->children()->limiteOptionnel->attributes()['maxDouble']->__toString()) :
                        0
                );

                $test->setMandatoryMinValue(
                    isset($testXml->children()->limiteObligatoire) &&
                    isset($testXml->children()->limiteObligatoire->attributes()['minDouble']) ?
                        floatval($testXml->children()->limiteObligatoire->attributes()['minDouble']->__toString()) :
                        0
                );
                $test->setMandatoryMaxValue(
                    isset($testXml->children()->limiteObligatoire) &&
                    isset($testXml->children()->limiteObligatoire->attributes()['maxDouble']) ?
                        floatval($testXml->children()->limiteObligatoire->attributes()['maxDouble']->__toString()) :
                        0
                );

                break;
            case "Test d'accroissement" :
                $test = new GrowthTest();
                $test->setMinDiff(
                    isset($testXml->children()->ecart) &&
                    isset($testXml->children()->ecart->attributes()['minDouble']) ?
                        floatval($testXml->children()->ecart->attributes()['minDouble']->__toString()) :
                        0
                );
                $test->setMaxDiff(
                    isset($testXml->children()->ecart) &&
                    isset($testXml->children()->ecart->attributes()['maxDouble']) ?
                        floatval($testXml->children()->ecart->attributes()['maxDouble']->__toString()) :
                        0
                );
                if (count(explode("/", $testXml->attributes()['variableDeComparaison'])) < 3) {
                    $test->setComparisonVariable($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()]);
                } else {
                    $test->setComparisonVariable($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()]);
                }

                break;
            case "Test sur combinaison entre variables" :
                $test = new CombinationTest();

                if (count(explode("/", $testXml->attributes()['operande1'])) < 3) {
                    $test->setFirstOperand($uriMap[$testXml->attributes()['operande1']->__toString()]);
                } else {
                    $test->setFirstOperand($uriMap[$testXml->attributes()['operande1']->__toString()]);
                }

                if (count(explode("/", $testXml->attributes()['operande2'])) < 3) {
                    $test->setSecondOperand($uriMap[$testXml->attributes()['operande2']->__toString()]);
                } else {
                    $test->setSecondOperand($uriMap[$testXml->attributes()['operande2']->__toString()]);
                }

                $test->setHighLimit(
                    isset($testXml->children()->limite) &&
                    isset($testXml->children()->limite->attributes()['maxDouble']) ?
                        floatval($testXml->children()->limite->attributes()['maxDouble']->__toString()) :
                        0
                );
                $test->setLowLimit(
                    isset($testXml->children()->limite) &&
                    isset($testXml->children()->limite->attributes()['minDouble']) ?
                        floatval($testXml->children()->limite->attributes()['minDouble']->__toString()) :
                        0
                );
                switch ($testXml->attributes()['operateur']) {
                    case "soustraction":
                        $test->setOperator("-");
                        break;
                    case "division":
                        $test->setOperator("/");
                        break;
                    case "multiplication":
                        $test->setOperator("*");
                        break;
                    default:
                        $test->setOperator("+");
                }
                break;
            case "Précalcul conditionnel" :
                $test = new PreconditionedCalculation();
                $test->setCondition($testXml->attributes()['condition']);

                if (count(explode("/", $testXml->attributes()['variableDeComparaison'])) < 3) {
                    $test->setComparisonVariable($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()]);
                } else {
                    $test->setComparisonVariable($uriMap[$testXml->attributes()['variableDeComparaison']->__toString()]);
                }

                if (isset($testXml->attributes()['variableAComparer'])) {
                    if (count(explode("/", $testXml->attributes()['variableAComparer'])) < 3) {
                        $test->setComparedVariable($uriMap[$testXml->attributes()['variableAComparer']->__toString()]);
                    } else {
                        $test->setComparedVariable($uriMap[$testXml->attributes()['variableAComparer']->__toString()]);
                    }
                } else {
                    $test->setComparedValue(
                        isset($testXml->children()->valeurAComparer) &&
                        isset($testXml->children()->valeurAComparer->attributes()['valeur']) ?
                            floatval($testXml->children()->valeurAComparer->attributes()['valeur']->__toString()) :
                            0
                    );
                }
                $test->setAffectationValue(
                    isset($testXml->children()->valeurAAffecter) &&
                    isset($testXml->children()->valeurAAffecter->attributes()['valeur']) ?
                        $testXml->children()->valeurAAffecter->attributes()['valeur']->__toString() :
                        '0'
                );
                break;
        }
        $test->setActive($testXml->attributes()['active'] == "true");
        $test->setName($testXml->attributes()['nom']);

        return $test;
    }

    /**
     * @param SimpleXMLElement $dataEntryProjectXml
     * @param $uriMap
     * @return DataEntryProject
     * @throws ParsingException
     */
    public function parseDataEntryProject(SimpleXMLElement $dataEntryProjectXml, &$uriMap, string $projectUri): DataEntryProject
    {
        $this->testDataEntryProject($dataEntryProjectXml);
        $dataEntryProject = new DataEntryProject();
        $dataEntryProject->setImprovised(false);

        $this->parseProject($dataEntryProjectXml, $uriMap, $dataEntryProject, $projectUri);

        foreach ($dataEntryProjectXml->children()->metadonneesASaisir as $mandatoryAnnotationXml) {
            $requiredAnnotation = $this->parseRequiredAnnotation($mandatoryAnnotationXml, $uriMap);
            $dataEntryProject->addRequiredAnnotation($requiredAnnotation);
        }


        return $dataEntryProject;
    }

    /**
     * @param SimpleXMLElement $mandatoryAnnotationXml
     * @param $uriMap
     * @return RequiredAnnotation
     * @throws ParsingException
     */
    private function parseRequiredAnnotation(SimpleXMLElement $mandatoryAnnotationXml, $uriMap): RequiredAnnotation
    {
        $this->testRequiredAnnotation($mandatoryAnnotationXml, $uriMap);
        $requiredAnnotation = new RequiredAnnotation();
        $requiredAnnotation->setActive(isset($mandatoryAnnotationXml->attributes()['active']) && $mandatoryAnnotationXml->attributes()['active']->__toString() === 'true');
        $requiredAnnotation->setAskWhenEntering(isset($mandatoryAnnotationXml->attributes()['avantSaisie']) && $mandatoryAnnotationXml->attributes()['avantSaisie']->__toString() === 'true');
        $requiredAnnotation->setComment($mandatoryAnnotationXml->attributes()['commentaire']);

        switch ($mandatoryAnnotationXml->attributes()['nature'] === null ? "son" : $mandatoryAnnotationXml->attributes()['nature']->__toString()) {
            case "texte":
                $type = Annotation::ANNOT_TYPE_TEXT;
                break;
            case "photo":
                $type = Annotation::ANNOT_TYPE_IMAG;
                break;
            case "son":
                $type = Annotation::ANNOT_TYPE_SONG;
                break;
        }
        $requiredAnnotation->setType($type);
        $requiredAnnotation->setPathLevel($mandatoryAnnotationXml->attributes()['typeUniteParcours'] === null ? "Parcelle Unitaire" : $mandatoryAnnotationXml->attributes()['typeUniteParcours']);
        foreach ($mandatoryAnnotationXml->children()->categories as $category) {
            $requiredAnnotation->addCategory($category->__toString());
        }
        foreach ($mandatoryAnnotationXml->children()->motscles as $keyword) {
            $requiredAnnotation->addKeyword($keyword->__toString());
        }
        $idPath = "";
        foreach (explode(' ', $mandatoryAnnotationXml->attributes()['objetMetier']) as $item) {
            if (!empty($item)) {
                $idPath = $idPath . $uriMap[$item]->getUri();
            }
        }
        $requiredAnnotation->setBuisnessObjects($idPath);
        return $requiredAnnotation;
    }

    /**
     * @param $pathsXml
     * @param $otherPathXml
     * @param $uriMap
     *
     * @return ArrayCollection
     *
     * @throws ParsingException
     * @throws ReflectionException
     */
    public function parsePaths($pathsXml, $otherPathXml, $uriMap): ArrayCollection
    {
        $this->testPaths($pathsXml, $uriMap);
        $paths = new ArrayCollection();
        foreach ($pathsXml as $pathXml) {
            if (
                !isset($pathXml->attributes()['objets']) ||
                !isset($pathXml->attributes()['nom'])
            ) {
                continue;
            }
            $path = new Workpath();
            $path->setUsername($pathXml->attributes()['nom']);
            $idPath = "";
            foreach (explode(' ', $pathXml->attributes()['objets']) as $item) {
                $pathItem = $uriMap[$item];
                $className = (new ReflectionClass(get_class($pathItem)))->getShortName();
                $idPath = $idPath . "/" . $className . "." . $pathItem->getId();
            }
            $path->setPath($idPath);
            $path->setStartEnd(isset($pathXml->attributes()['declenchement']));
            $paths->add($path);
        }
        if ($otherPathXml !== null) {
            $otherPaths = $this->parsePaths($otherPathXml, null, $uriMap);
            foreach ($otherPaths as $otherPath) {
                $paths->add($otherPath);
            }
        }

        return $paths;
    }

    /**
     * @param SimpleXMLElement $platformXml
     * @param array $uriMap
     * @return Platform
     * @throws ParsingException
     */
    public function parsePlatform(SimpleXMLElement $platformXml, array &$uriMap, string $platformUri): Platform
    {
        $this->testPlatform($platformXml, $uriMap);
        $platform = new Platform();
        $platform->setSite($platformXml->attributes()['nomSite']);
        $platform->setPlace($platformXml->attributes()['nomLieu']);
        $platform->setName($platformXml->attributes()['nom']);
        $platform->setCreationDate(date_create($platformXml->attributes()['dateCreation']));
        $devCNT = 0;
        foreach ($platformXml->children()->dispositifs as $deviceXml) {
            $devURI = $platformUri . "/@dispositifs." . $devCNT;
            $device = $this->parseExperiment($deviceXml, $uriMap, $devURI);
            $platform->addDevice($device);
            $uriMap[$devURI] = $device;
            $devCNT++;
        }
        return $platform;
    }

    /**
     * @param SimpleXMLElement $experimentXml
     * @param array $uriMap
     * @param string $experimentUri
     * @return Device
     * @throws ParsingException
     */
    public function parseExperiment(SimpleXMLElement $experimentXml, array &$uriMap, string $experimentUri): Device
    {
        $this->testExperiment($experimentXml, $uriMap, $experimentUri);
        $experiment = new Device();
        $experiment->setName(isset($experimentXml->attributes()['nom'])
            ? $experimentXml->attributes()['nom']->__toString()
            : '0');
        $experiment->setCreationDate(date_create($experimentXml->attributes()['dateCreation']));
        if (isset($experimentXml->attributes()['dateValidation'])) {
            $experiment->setValidationDate(date_create($experimentXml->attributes()['dateValidation']));
        }
        $surfaceUnitPlot = isset($experimentXml->attributes()['puSurfacique']) && $experimentXml->attributes()['puSurfacique']->__toString() === 'true';
        $experiment->setIndividualPU(!$surfaceUnitPlot);
        $blocCNT = 0;

        $protocolUri = $experimentUri . "/@protocoles";
        $protocol = $this->parseProtocol($experimentXml->children()->protocoles, $uriMap, $protocolUri);
        $experiment->setProtocol($protocol);
        $uriMap[$protocolUri] = $protocol;

        foreach ($experimentXml->children()->blocs as $blocXml) {
            $blocURI = $experimentUri . "/@blocs." . $blocCNT;
            $block = $this->parseBlock($blocXml, $uriMap, $blocURI);
            $experiment->addBlock($block);
            $uriMap[$blocURI] = $block;
            $blocCNT++;
        }
        foreach ($experimentXml->children()->zheAvecEpaisseurs as $outExperimentationZoneXml) {
            $outExperimentationZone = $this->parseOutExperimentationZone($outExperimentationZoneXml, $uriMap);
            $experiment->addOutExperimentationZone($outExperimentationZone);
        }
        foreach ($experimentXml->children()->notes as $noteXml) {
            $note = $this->parseEntryNote($noteXml);
            $experiment->addNote($note);
        }

        return $experiment;
    }

    /**
     * @param SimpleXMLElement $protocolXml
     * @param array $uriMap
     * @param string $protocolUri
     * @return Protocol
     * @throws ParsingException
     */
    public function parseProtocol(SimpleXMLElement $protocolXml, array &$uriMap, string $protocolUri): Protocol
    {
        $this->testProtocol($protocolXml, $uriMap, $protocolUri);
        $protocol = new Protocol();
        $protocol->setName($protocolXml->attributes()['nom']);
        $protocol->setCreationDate(date_create($protocolXml->attributes()['dateCreation']));
        $protocol->setAim(isset($protocolXml->attributes()['objectifs']) ? $protocolXml->attributes()['objectifs'] : '');
        $protocol->setAlgorithm($protocolXml->attributes()['algorithmeTirage']);
        $protocol->setCreator($uriMap[$protocolXml->attributes()['createur']->__toString()]);
        $factorCount = 0;
        foreach ($protocolXml->children()->facteurs as $factorXml) {
            $factorUri = $protocolUri . "/@facteurs." . $factorCount;
            $factor = $this->parseFactor($factorXml, $uriMap, $factorUri);
            $protocol->addFactor($factor);
            $uriMap[$factorUri] = $factor;
            $factorCount++;
        }

        $treatmentCount = 0;
        foreach ($protocolXml->children()->traitements as $treatmentXml) {
            $treatmentUri = $protocolUri . "/@traitements." . $treatmentCount;
            $treatment = $this->parseTreatment($treatmentXml, $uriMap, $treatmentUri);
            $uriMap[$treatmentUri] = $treatment;
            $protocol->addTreatment($treatment);
            $treatmentCount++;
        }

        // When protocole has a child "structureInitiale", it meens that the protocol defines information such as
        // the number of individual to insert in a parcel when working on spacialization project.
        if (isset($protocolXml->children()->structureInitiale)) {
            $protocol->setNbIndividualsPerParcel(
                $this->parseNbIndividualPerParcel($protocolXml->children()->structureInitiale[0])
            );
        }

        return $protocol;
    }

    /**
     * @param SimpleXMLElement $structureInitialeXML
     * @return int|null
     */
    private function parseNbIndividualPerParcel(SimpleXMLElement $structureInitialeXML): ?int
    {
        $nbIndividuals = null;
        if (isset($structureInitialeXML->children()->parcellesUnitaire)) {
            $nbIndividuals = (int)$structureInitialeXML->children()->parcellesUnitaire[0]->attributes()['nbIndividus'];
        }
        return $nbIndividuals;
    }

    /**
     * @param SimpleXMLElement $factorXml
     * @param array $uriMap
     * @param string $factorUri
     * @return Factor
     * @throws ParsingException
     */
    public function parseFactor(SimpleXMLElement $factorXml, array &$uriMap, string $factorUri): Factor
    {
        $this->testFactor($factorXml, $uriMap, $factorUri);
        $factor = new Factor();
        $factor->setName($factorXml->attributes()['nom']);
        $modalityCount = 0;
        foreach ($factorXml->children()->modalites as $modalityXml) {
            $modalityUri = $factorUri . "/@modalites." . $modalityCount;
            $modality = $this->parseModality($modalityXml, $uriMap, $modalityUri);
            $factor->addModality($modality);
            $uriMap[$modalityUri] = $modality;
            $modalityCount++;
        }
        return $factor;
    }

    /**
     * @param SimpleXMLElement $modalityXml
     * @return Modality
     */
    public function parseModality(SimpleXMLElement $modalityXml, array $uriMap, string $modalityUri): Modality
    {
        $this->testModality($modalityXml, $uriMap, $modalityUri);
        $modality = new Modality();
        $modality->setValue($modalityXml->attributes()['valeur']);
        return $modality;
    }

    /**
     * @param SimpleXMLElement $treatmentXml
     * @param array $uriMap
     * @return Treatment
     * @throws ParsingException
     */
    public function parseTreatment(SimpleXMLElement $treatmentXml, array $uriMap, string $treatmentUri): Treatment
    {
        $this->testTreatment($treatmentXml, $uriMap, $treatmentUri);
        $treatment = new Treatment();
        $treatment->setName($treatmentXml->attributes()['nom']);
        $treatment->setShortName($treatmentXml->attributes()['nomCourt']);
        $treatment->setRepetitions(isset($treatmentXml->attributes()['nbRepetitions'])
            ? intval($treatmentXml->attributes()['nbRepetitions'])
            : 1);
        foreach (explode(' ', trim($treatmentXml->attributes()['combinaisons'])) as $item) {
            $combinationItem = $uriMap[$item];
            $treatment->addModality($combinationItem);
        }

        return $treatment;
    }

    /**
     * @param SimpleXMLElement $blocXml
     * @param array $uriMap
     * @param string $blocUri
     * @return Block
     * @throws ParsingException
     */
    public function parseBlock(SimpleXMLElement $blocXml, array &$uriMap, string $blocUri): Block
    {
        $this->testBlock($blocXml, $uriMap, $blocUri);
        $bloc = new Block();
        $bloc->setName(isset($blocXml->attributes()['numero'])
            ? $blocXml->attributes()['numero']->__toString()
            : '0');
        foreach ($blocXml->children()->zheAvecEpaisseurs as $outExperimentationZoneXml) {
            $outExperimentationZone = $this->parseOutExperimentationZone($outExperimentationZoneXml, $uriMap);
            $bloc->addOutExperimentationZone($outExperimentationZone);
        }
        if (isset($blocXml->children()->parcellesUnitaire)) {
            $this->parseBlockOrSubBlockChildren($blocXml, $uriMap, $blocUri, $bloc);
        } elseif (isset($blocXml->children()->sousBlocs)) {
            $subBlocCNT = 0;
            foreach ($blocXml->children()->sousBlocs as $subBlocXml) {
                $subBlocURI = $blocUri . "/@sousBlocs." . $subBlocCNT;
                $subBloc = $this->parseSubBloc($subBlocXml, $uriMap, $subBlocURI);
                $bloc->addSubBlock($subBloc);
                $uriMap[$subBlocURI] = $subBloc;
                $subBlocCNT++;
            }
        }

        foreach ($blocXml->children()->notes as $noteXml) {
            $note = $this->parseEntryNote($noteXml);
            $bloc->addNote($note);
        }
        return $bloc;
    }

    /**
     * @param SimpleXMLElement $outExperimentationZoneXml
     * @param array $uriMap
     * @return OutExperimentationZone
     * @throws ParsingException
     */
    public function parseOutExperimentationZone(SimpleXMLElement $outExperimentationZoneXml, array $uriMap): OutExperimentationZone
    {
        $this->testOutExperimentationZone($outExperimentationZoneXml, $uriMap);
        $outExperimentationZone = new OutExperimentationZone();
        $outExperimentationZone->setX(isset($outExperimentationZoneXml->attributes()['x']) ? intval($outExperimentationZoneXml->attributes()['x']->__toString()) : 0);
        $outExperimentationZone->setY(isset($outExperimentationZoneXml->attributes()['y']) ? intval($outExperimentationZoneXml->attributes()['y']->__toString()) : 0);
        $outExperimentationZone->setNumber($outExperimentationZoneXml->attributes()['numero']);
        $outExperimentationZone->setNatureZHE($uriMap[$outExperimentationZoneXml->attributes()['natureZhe']->__toString()]);
        return $outExperimentationZone;
    }

    /**
     * @param SimpleXMLElement $blocXml
     * @param array $uriMap
     * @param string $blocUri
     * @param Block|SubBlock $bloc
     * @return array
     * @throws ParsingException
     */
    private function parseBlockOrSubBlockChildren(SimpleXMLElement $blocXml, array &$uriMap, string $blocUri, $bloc): array
    {
        $puCNT = 0;
        foreach ($blocXml->children()->parcellesUnitaire as $puXml) {
            if (isset($puXml->attributes()['traitement'])) {
                $puUri = $blocUri . "/@parcellesUnitaire." . $puCNT;
                $unitParcel = $this->parsePU($puXml, $uriMap, $puUri);
                $bloc->addUnitParcel($unitParcel);
                $uriMap[$puUri] = $unitParcel;
            }
            $puCNT++;
        }
        return $uriMap;
    }

    /**
     * @param SimpleXMLElement $puXml
     * @param array $uriMap
     * @param string $puUri
     * @return UnitParcel
     * @throws ParsingException
     */
    public function parsePU(SimpleXMLElement $puXml, array &$uriMap, string $puUri): UnitParcel
    {
        $this->testUnitPlot($uriMap, $puUri);
        $unitParcel = new UnitParcel();
        $unitParcel->setName(isset($puXml->attributes()['numero']) ?
            $puXml->attributes()['numero']->__toString() :
            '0');
        $unitParcel->setX(isset($puXml->attributes()['x']) ? intval($puXml->attributes()['x']->__toString()) : 0);
        $unitParcel->setY(isset($puXml->attributes()['y']) ? intval($puXml->attributes()['y']->__toString()) : 0);
        $unitParcel->setDemiseDate(isset($puXml->attributes()["dateDisparition"]) ? date_create($puXml->attributes()['dateDisparition']) : null);
        $unitParcel->setApparitionDate(isset($puXml->attributes()["dateApparition"]) ? date_create($puXml->attributes()['dateApparition']) : null);
        if (isset($puXml->attributes()['idRfidCodeBarre'])) {
            $unitParcel->setIdent($puXml->attributes()['idRfidCodeBarre']);
        }
        if (isset($uriMap[$puXml->attributes()['traitement']->__toString()])) {
            $unitParcel->setTreatment($uriMap[$puXml->attributes()['traitement']->__toString()]);
        }

        $individuCNT = 0;
        foreach ($puXml->children()->individus as $individualXml) {
            $individualURI = $puUri . "/@individus." . $individuCNT;
            $individual = $this->parseIndividual($individualXml, $uriMap, $individualURI);
            $individuCNT++;
            $uriMap[$individualURI] = $individual;
            $unitParcel->addIndividual($individual);
        }
        foreach ($puXml->children()->zheAvecEpaisseurs as $outExperimentationZoneXml) {
            $outExperimentationZone = $this->parseOutExperimentationZone($outExperimentationZoneXml, $uriMap);
            $unitParcel->addOutExperimentationZone($outExperimentationZone);
        }
        foreach ($puXml->children()->codeEtat as $stateCodeXml) {
            $stateCode = $this->parseStateCode($stateCodeXml);
            $stateCode = $this->stateCodes[$stateCode->getCode()];
            $uriMap[$puUri . "/@codeEtat"] = $stateCode;
            $unitParcel->setStateCode($stateCode);
        }
        foreach ($puXml->children()->notes as $noteXml) {
            $note = $this->parseEntryNote($noteXml);
            $unitParcel->addNote($note);
        }
        return $unitParcel;
    }

    /**
     * @param SimpleXMLElement $individualXml
     * @return Individual
     */
    public function parseIndividual(SimpleXMLElement $individualXml, array $uriMap, string $individualUri): Individual
    {
        $this->testIndividual($uriMap, $individualUri);
        $individual = new Individual();
        $individual->setX(intval($individualXml->attributes()['x']));
        $individual->setY(intval($individualXml->attributes()['y']));
        $individual->setName(isset($individualXml->attributes()['numero'])
            ? $individualXml->attributes()['numero']->__toString()
            : '0');
        $individual->setApparitionDate(date_create($individualXml->attributes()['dateApparition']));
        $individual->setDemiseDate(isset($individualXml->attributes()["dateDisparition"]) ? date_create($individualXml->attributes()['dateDisparition']) : null);

        if (isset($individualXml->attributes()['idRfidCodeBarre'])) {
            $individual->setIdent($individualXml->attributes()['idRfidCodeBarre']);
        }
        foreach ($individualXml->children()->codeEtat as $stateCodeXml) {
            $stateCode = $this->parseStateCode($stateCodeXml);
            $stateCode = $this->stateCodes[$stateCode->getCode()];
            $uriMap[$individualUri . "/@codeEtat"] = $stateCode;
            $individual->setStateCode($stateCode);
        }
        foreach ($individualXml->children()->notes as $noteXml) {
            $note = $this->parseEntryNote($noteXml);
            $individual->addNote($note);
        }
        return $individual;
    }

    /**
     * @param SimpleXMLElement $stateCodeXml
     *
     * @return StateCode
     *
     * @throws ParsingException
     */
    public function parseStateCode(SimpleXMLElement $stateCodeXml): StateCode
    {
        $this->testStateCode($stateCodeXml);
        $stateCode = new StateCode();
        $stateCode->setCode(intval($stateCodeXml->attributes()['code']));
        $stateCode->setLabel($stateCodeXml->attributes()['intitule']);
        switch ($stateCode->getLabel()) {
            case "Mort":
                $stateCode->setType(StateCode::DEAD_STATE_CODE);
                break;
            case "Donnée Manquante":
                $stateCode->setType(StateCode::MISSING_STATE_CODE);
                break;
            default:
                $stateCode->setType(StateCode::NON_PERMANENT_STATE_CODE);

        }
        if (isset($stateCodeXml->attributes()['signification'])) {
            $stateCode->setDescription($stateCodeXml->attributes()['signification']);
        }
        if (isset($stateCodeXml->attributes()['propagation'])) {
            switch ($stateCodeXml->attributes()['propagation']) {
                case 'individu':
                    $stateCode->setPropagation(PathLevelEnum::INDIVIDUAL);
                    break;
                case 'parcelle':
                    $stateCode->setPropagation(PathLevelEnum::UNIT_PLOT);
            }
        }
        if (isset($stateCodeXml->attributes()['couleur'])) {
            $stateCode->setColor($stateCodeXml->attributes()['couleur']);
        }
        return $stateCode;
    }

    /**
     * @param SimpleXMLElement $subBlocXml
     * @param array $uriMap
     * @param string $subBlocURI
     * @return SubBlock
     * @throws ParsingException
     */
    public function parseSubBloc(SimpleXMLElement $subBlocXml, array &$uriMap, string $subBlocURI): SubBlock
    {
        $this->testSubBlock($uriMap, $subBlocURI);
        $subBloc = new SubBlock();
        $subBloc->setName(isset($subBlocXml->attributes()['numero'])
            ? $subBlocXml->attributes()['numero']->__toString()
            : '0');
        foreach ($subBlocXml->children()->zheAvecEpaisseurs as $outExperimentationZoneXml) {
            $outExperimentationZone = $this->parseOutExperimentationZone($outExperimentationZoneXml, $uriMap);
            $subBloc->addOutExperimentationZone($outExperimentationZone);
        }
        $this->parseBlockOrSubBlockChildren($subBlocXml, $uriMap, $subBlocURI, $subBloc);

        foreach ($subBlocXml->children()->notes as $noteXml) {
            $note = $this->parseEntryNote($noteXml);
            $subBloc->addNote($note);
        }
        return $subBloc;
    }

    /**
     * @param SimpleXMLElement $graphicalStructureXml
     * @param SimpleXMLElement $platformXml
     * @return GraphicalStructure
     */
    public function parseGraphicalStructure(SimpleXMLElement $graphicalStructureXml, SimpleXMLElement $platformXml): GraphicalStructure
    {
        $graphicalStructure = new GraphicalStructure();
        $graphicalStructure
            ->setAbnormalIndividualColor($graphicalStructureXml->attributes()["couleurIndAnomalie"])
            ->setTooltipActive($graphicalStructureXml->attributes()["tooltipActive"] == "true")
            ->setSurfaceUnitParcelColor($graphicalStructureXml->attributes()["couleurPuSurfacique"])
            ->setIndividualColor($graphicalStructureXml->attributes()["couleurIndividu"])
            ->setSubBlockColor($graphicalStructureXml->attributes()["couleurSousBloc"])
            ->setEmptyColor($graphicalStructureXml->attributes()["couleurMailleVide"])
            ->setPlatformColor($graphicalStructureXml->attributes()["couleurPlateforme"])
            ->setIndividualUnitParcelColor($graphicalStructureXml->attributes()["couleurPuIndividuel"])
            ->setDeviceColor($graphicalStructureXml->attributes()["couleurDispositif"])
            ->setBlockColor($graphicalStructureXml->attributes()["couleurBloc"])
            ->setAbnormalUnitParcelColor($graphicalStructureXml->attributes()["couleurPuAnomalie"])
            ->setSurfaceUnitParcelLabel($graphicalStructureXml->children()->labelsPuSurfacique)
            ->setSubBlockLabel($graphicalStructureXml->children()->labelsSousBloc)
            ->setIndividualUnitParcelLabel($graphicalStructureXml->children()->labelsPuIndividuel)
            ->setIndividualLabel($graphicalStructureXml->children()->labelsIndividu)
            ->setDeviceLabel($graphicalStructureXml->children()->labelsDispo)
            ->setBlockLabel($graphicalStructureXml->children()->labelsBloc);

        if (isset($platformXml->children()->structureGraphique)) {
            $originXml = $platformXml->children()->structureGraphique;
            $graphicalStructure
                ->setOrigin($originXml["origine"])
                ->setBaseHeight(intval($originXml["hauteurMaille"]))
                ->setBaseWidth(intval($originXml["largeurMaille"]));
        }

        return $graphicalStructure;
    }

    /**
     * @param SimpleXMLElement $natureZHEXml
     * @return NatureZHE
     */
    public function readNatureZheItem(SimpleXMLElement $natureZHEXml): NatureZHE
    {
        $natureZHE = new NatureZHE();
        $natureZHE->setName($natureZHEXml->attributes()["nom"]);
        $natureZHE->setColor($natureZHEXml->attributes()["couleur"]);
        $natureZHE->setTexture($natureZHEXml->attributes()["texture"]);
        return $natureZHE;
    }

    /**
     * @param SimpleXMLElement $noteXml
     * @return EntryNote
     * @throws ParsingException
     */
    private function parseEntryNote(SimpleXMLElement $noteXml): EntryNote
    {
        $this->testEntryNote($noteXml);
        $note = new EntryNote();
        $note->setCreationDate(date_create($noteXml->attributes()["dateCreation"]));
        if (isset($noteXml->attributes()["supprime"])) {
            $note->setDeleted($noteXml->attributes()["supprime"]->__toString() === "true");
        } else {
            $note->setDeleted(false);
        }
        $note->setText($noteXml->attributes()["texte"]->__toString());
        $creator = $this->userRepository->findOneBy(["username" => $noteXml->attributes()["loginCreateur"]->__toString()]);
        $note->setCreator($creator);
        return $note;
    }

    /**
     * @param $dataEntryXml
     * @param $uriMap
     * @param string $dataEntryUri
     * @return DataEntry
     * @throws ParsingException
     */
    public function parseDataEntry($dataEntryXml, $uriMap, string $dataEntryUri, DataEntryProject $project): DataEntry
    {
        $this->testDataEntry($dataEntryXml, $uriMap);
        $dataEntry = (new DataEntry());
        $sessionCnt = 0;

        if (isset($dataEntryXml->children()->materiel)) {
            $materialCnt = 0;
            foreach ($dataEntryXml->children()->materiel as $materialXml) {
                $materialUri = $dataEntryUri . "/@materiel." . $materialCnt;
                $material = $this->parseMaterial($materialXml, $uriMap, $materialUri);
                $project->addMaterial($material);
                $uriMap[$materialUri] = $material;
                $materialCnt++;
            }
        }

        if (isset($dataEntryXml->children()->variables)) {
            $variableCnt = 0;
            foreach ($dataEntryXml->children()->variables as $variableXml) {
                $variableUri = $dataEntryUri . "/@variables." . $variableCnt;
                $variable = $this->parseVariable($variableXml, $uriMap, $variableUri);
                $project->addVariable($variable);
                $uriMap[$variableUri] = $variable;
                $variableCnt++;
            }
        }
        foreach ($dataEntryXml->children()->sessions as $sessionXml) {
            $sessionUri = $dataEntryUri . '/@sessions.' . $sessionCnt++;
            $session = $this->parseSession($sessionXml, $uriMap, $sessionUri);
            $dataEntry->setEndedAt($dataEntry->getEndedAt() > $session->getEndedAt() ? $dataEntry->getEndedAt() : $session->getEndedAt());
            $dataEntry->addSession($session);
        }
        return $dataEntry;
    }

    /**
     * @param $sessionXml
     * @param $uriMap
     * @param string $sessionUri
     * @return Session
     * @throws ParsingException
     */
    private function parseSession($sessionXml, $uriMap, string $sessionUri): Session
    {
        $this->testSession($sessionXml);
        $session = (new Session())
            // TODO gérer l'utilisateur ayant effectué la session
            ->setStartedAt(date_create($sessionXml->attributes()['dateDebut']))
            ->setEndedAt(date_create($sessionXml->attributes()['dateFin']));

        /** @var array<GeneratedField> $generatedVariables generatedMeasureUri => FieldGeneration */
        $generatedVariables = [];
        /** @var array<FormField> $generatorVariables generatorMeasureUri => generatorField */
        $generatorVariables = [];
        /** @var array<GeneratedField> $generatorFields generatorIndex . generatorMeasureUri => FieldGeneration */
        $generatorFields = [];
        /** @var array<FormField> $repetitionMap generatorIndex . generatorMeasure . variable . businessObject => FieldMeasure */
        $repetitionMap = [];

        $measureCnt = 0;
        foreach ($sessionXml->children()->mesureVariables as $measureXml) {
            $measureUri = $sessionUri . '/@mesureVariables.' . $measureCnt;
            $measure = (new Measure())
                ->setTimestamp(date_create($sessionXml->attributes()['horodatage']))
                ->setState(isset($measureXml->attributes()['codeEtat']) ? $uriMap[$measureXml->attributes()['codeEtat']->__toString()] : null)
                ->setValue($measureXml->attributes()['valeur']);
            $uriMap[$measureUri] = $measure;
            if (isset($measureXml->attributes("xsi", true)["type"]) &&
                $measureXml->attributes("xsi", true)["type"]->__toString() === "adonis.modeleMetier.saisieTerrain:MesureVariableBooleen") {
                $measure->setValue(isset($measureXml->attributes()['valeur']) ? 'true' : 'false');
            }

            // Handle repetitions (not available on generatorVariables)
            $repetitionUri = (isset($measureXml->attributes()['indiceGeneratrice']) ? $measureXml->attributes()['indiceGeneratrice']->__toString() : '') .
                (isset($measureXml->attributes()['mesureGeneratrice']) ? $measureXml->attributes()['mesureGeneratrice']->__toString() : '') .
                $measureXml->attributes()['variable']->__toString() .
                $measureXml->attributes()['objetMetier']->__toString();
            if (!isset($repetitionMap[$repetitionUri])) {
                $fieldMeasure = (new FormField())
                    ->setVariable($uriMap[$measureXml->attributes()['variable']->__toString()])
                    ->setTarget($uriMap[$measureXml->attributes()['objetMetier']->__toString()]);

                $repetitionMap[$repetitionUri] = $fieldMeasure;

                // Generator values
                if (isset($measureXml->attributes()['mesuresGenerees'])) {
                    foreach (explode(' ', $measureXml->attributes()['mesuresGenerees']) as $measureUri) {
                        $generatorVariables[$measureUri] = $fieldMeasure;
                        if (isset($generatedVariables[$measureUri])) {
                            $fieldMeasure->addGeneratedField($generatedVariables[$measureUri]);
                        }
                    }
                }

                // Generated values
                if (isset($measureXml->attributes()['mesureGeneratrice'])) {
                    $generatorFieldUri = $measureXml->attributes()['indiceGeneratrice']->__toString() .
                        $measureXml->attributes()['mesureGeneratrice']->__toString();
                    if (!isset($generatorFields[$generatorFieldUri])) {
                        $generatorFields[$generatorFieldUri] = (new GeneratedField())
                            ->setIndex(intval($measureXml->attributes()['indiceGeneratrice']))
                            ->setNumeralIncrement(false) // TODO prendre les vraies valeur ou retirer ces attributs de la classe (redondance avec la variable)
                            ->setPrefix($fieldMeasure->getVariable()->getGeneratorVariable()->getGeneratedPrefix());
                    }
                    $generatorFields[$generatorFieldUri]->addFormField($fieldMeasure);

                    // If the generator measure was already found before
                    if (isset($generatorVariables[$measureXml->attributes()['mesureGeneratrice']->__toString()])) {
                        $generatorVariables[$measureXml->attributes()['mesureGeneratrice']]->addGeneratedField($generatorFields[$generatorFieldUri]);
                    } else {
                        $generatedVariables[$measureUri] = $generatorFields[$generatorFieldUri];
                    }
                } else {
                    $session->addFormField($fieldMeasure);
                }
            }
            $measure->setRepetition($repetitionMap[$repetitionUri]->getMeasures()->count() + 1);
            $repetitionMap[$repetitionUri]->addMeasure($measure);
            $measureCnt++;
        }

        foreach ($sessionXml->children()->metadonnees as $annotationXml) {
            $this->parseAnnotation($annotationXml, $uriMap);
        }
        return $session;
    }

    private function parseAnnotation($annotationXml, $uriMap): Annotation
    {
        $annotation = (new Annotation())
            ->setValue($annotationXml->attributes()['donnee'])
            ->setCategories([])
            ->setKeywords([])
            ->setTimestamp(date_create($annotationXml->attributes()['date']));

        if (isset($annotationXml->attributes()['mesureVariable'])) {
            $annotation->setProjectObject($uriMap[$annotationXml->attributes()['mesureVariable']->__toString()]);
        } else {
            $annotation->setProjectObject($uriMap[$annotationXml->attributes()['objetMetier']->__toString()]);
        }

        switch ($annotationXml->attributes()['nature'] === null ? "son" : $annotationXml->attributes()['nature']->__toString()) {
            case "texte":
                $annotation->setType(Annotation::ANNOT_TYPE_TEXT);
                break;
            case "photo":
                $annotation->setType(Annotation::ANNOT_TYPE_IMAG);
                break;
            case "son":
                $annotation->setType(Annotation::ANNOT_TYPE_SONG);
                break;
        }

        return $annotation;
    }

}
