<?php

use CorpusParole\Libraries\Mergers\CocoonSoundRdfMerger;
use CorpusParole\Libraries\CocoonUtils;

use EasyRdf\Graph;
use EasyRdf\Resource;
use EasyRdf\Literal;

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class CocoonSoundRdfMergerTest extends TestCase
{

    const TEST_INPUT_DOCS = [
        'BASE' => __DIR__.'/files/CocoonSoundRdfMergerTest/base_doc.ttl',
        'SOURCE' => __DIR__.'/files/CocoonSoundRdfMergerTest/source_doc.ttl',
        'BASE_NO_SPATIAL' => __DIR__.'/files/CocoonSoundRdfMergerTest/base_no_spatial.ttl',
        'SOURCE_NO_SPATIAL' => __DIR__.'/files/CocoonSoundRdfMergerTest/source_no_spatial.ttl',
        'SOURCE_MORE_SPATIAL_NOTES' => __DIR__.'/files/CocoonSoundRdfMergerTest/source_more_spatial_notes.ttl',
        'SOURCE_RIGHTS' => __DIR__.'/files/CocoonSoundRdfMergerTest/source_rights.ttl',
    ];

    const TEST_INPUT_ID = "crdo-ESLO1_ENT_019";
    const TEST_GRAPH_URI = "http://corpusdelaparole.huma-num.fr/corpus/res/crdo-ESLO1_ENT_019";
    const TEST_CHO_URI = "http://purl.org/poi/crdo.vjf.cnrs.fr/crdo-ESLO1_ENT_019";


    private $inputGraphes = [];
    private $resGraph;

    function __construct(string $name = null) {
        parent::__construct($name);
    }

    public function setUp() {
        parent::setUp();

        foreach(CocoonSoundRdfMergerTest::TEST_INPUT_DOCS as $key => $inputDoc) {
            $this->inputGraphes[$key] = new Graph("http://purl.org/poi/crdo.vjf.cnrs.fr/crdo-CFPP2000_35_SOUND", file_get_contents($inputDoc));
        }

    }

    public function tearDown() {
        parent::tearDown();
    }

    /**
     * Just test that the construct and setup are ok
     *
     * @return void
     */
    public function testInit()
    {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE']);

        $this->assertTrue(true);
        //echo $this->resGraph['NORMAL']->serialise('turtle');
    }

    /**
     * test the result graph.
     *
     * @return void
     */
    public function testResGraph() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE']);

        $this->assertNotNull($this->resGraph['NORMAL'], "Res graph must not be null");
        $this->assertEquals("http://purl.org/poi/crdo.vjf.cnrs.fr/crdo-CFPP2000_35_SOUND", $this->resGraph['NORMAL']->getUri(), "graph uri must be equals to http://purl.org/poi/crdo.vjf.cnrs.fr/crdo-CFPP2000_35_SOUND");
    }

    /**
     * test the merged Aggregation
     *
     * @return void
     */
    public function testAggregationResource() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE']);

        $aggregationResList = $this->resGraph['NORMAL']->allOfType('http://www.openarchives.org/ore/terms/Aggregation');
        $this->assertCount(1, $aggregationResList, "Only one Aggregation node");
        $aggregationRes = $aggregationResList[0];

        $this->assertEquals("http://corpusdelaparole.huma-num.fr/corpus/res/crdo-CFPP2000_35_SOUND",$aggregationRes->getUri(),"Aggregation node uri must be http://corpusdelaparole.huma-num.fr/corpus/res/crdo-CFPP2000_35_SOUND");

        $singleValuesRes = [
            'http://www.europeana.eu/schemas/edm/aggregatedCHO' => ['size'=>1, 'val' => $this->resGraph['NORMAL']->resource("http://purl.org/poi/crdo.vjf.cnrs.fr/crdo-CFPP2000_35_SOUND")],
            'http://www.europeana.eu/schemas/edm/provider' => ['size'=>1, 'val' => new Literal("Corpus de la Parole", "fr", null)],
            'http://www.europeana.eu/schemas/edm/dataProvider' => ['size'=>1, 'val' => $this->resGraph['NORMAL']->resource("http://viaf.org/viaf/142432638")],
            'http://www.europeana.eu/schemas/edm/isShownAt' => ['size'=>1, 'val' => $this->resGraph['NORMAL']->resource("http://corpusdelaparole.huma-num.fr/corpus-app#/detail/crdo-CFPP2000_35_SOUND")],
            'http://www.europeana.eu/schemas/edm/isShownBy' => ['size'=>1, 'val' => $this->resGraph['NORMAL']->resource("http://cocoon.huma-num.fr/data/archi/masters/372593.wav")],
            'http://www.europeana.eu/schemas/edm/rights' => ['size'=>1, 'val' => $this->resGraph['NORMAL']->resource("http://creativecommons.org/licenses/by-nc-sa/4.0/")],
            'http://www.europeana.eu/schemas/edm/hasView' => ['size' => 4, 'val' => [
                $this->resGraph['NORMAL']->resource("http://cocoon.huma-num.fr/data/cfpp2000/Ozgur_Kilic_H_32_alii_3e-2.wav"),
                $this->resGraph['NORMAL']->resource("http://cocoon.huma-num.fr/data/cfpp2000/Ozgur_Kilic_H_32_alii_3e-2.mp3"),
                $this->resGraph['NORMAL']->resource("http://cocoon.huma-num.fr/exist/crdo/cfpp2000/fra/Ozgur_Kilic_H_32_alii_3e-2.xml"),
                $this->resGraph['NORMAL']->resource("http://cocoon.huma-num.fr/exist/crdo/cfpp2000/fra/Ozgur_Kilic_H_32_alii_3e-2.xhtml")
            ]]
        ];

        foreach($singleValuesRes as $property => $resVal) {
            $resList = $aggregationRes->all($this->inputGraphes['BASE']->resource($property));
            $this->assertCount($resVal['size'], $resList, "$property list size $resVal[size]");
            if($resVal['size'] == 1) {
                $this->assertEquals($resVal['val'], $resList[0], "$property size one not equals");
            }
            else {
                $this->assertEquals($resVal['val'], $resList, "$property size more than one not equals");
            }
        }

    }

    /**
     * test web resources
     *
     * @return void
     */
    public function testWebResources() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE']);

        $webResList = $this->resGraph['NORMAL']->allOfType('http://www.europeana.eu/schemas/edm/WebResource');

        $this->assertCount(5, $webResList, "Must have 5 webResource");
    }


    /**
    * Test one to one mapping spatial info
    *
    * @return void
    */

    public function testProvidedCHOSpatial() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE']);

        $providedCHO = $this->resGraph['NORMAL']->get('edm:ProvidedCHO', '^rdf:type');

        $this->assertNotNull($providedCHO);

        $spatials = $providedCHO->all($this->resGraph['NORMAL']->resource('http://purl.org/dc/terms/spatial'));

        $this->assertCount(1, $spatials, "Must have only one spatial node");
        $spatial = $spatials[0];
        $this->assertTrue($spatial->isBNode(),"spatial node must be blank");
        $this->assertEquals("edm:Place", $spatial->type(), "spatial node type must be edm:Place");

        $lats = $spatial->all('geo:lat');
        $this->assertCount(1, $lats, "One latitude");
        $this->assertInstanceOf("EasyRdf\Literal", $lats[0], "Latitude must be a litteral");
        $this->assertEquals(Literal::create("48.83975", null, 'xsd:float'), $lats[0], "lat must be '48.83975'^^xsd:float");

        $longs = $spatial->all('geo:long');
        $this->assertCount(1, $longs, "One longitude");
        $this->assertInstanceOf("EasyRdf\Literal", $longs[0], "Longitude must be a litteral");
        $this->assertEquals(Literal::create("2.3542", null, 'xsd:float'), $longs[0], "long must be '2.3542'^^xsd:float");

        $notes = $spatial->all('skos:note');
        $this->assertCount(3, $notes, "3 notes");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Literal", $notes, "Notes mus be only literals");
        $this->assertEquals(Literal::create("FR", null, "dc:ISO3166"), $notes[0], "notes contains 'FR'^^dc:ISO3166");
        $this->assertEquals(Literal::create("France, Paris, Université Sorbonne Nouvelle Paris 3, site Censier", "fr", null), $notes[1], "notes contains 'France, Centre, Loiret, Orléans'@fr");
        $this->assertEquals(Literal::create("Other place very important", "fr", null), $notes[2], "notes contains 'Other place very important'@fr");

        $sameas = $spatial->all('owl:sameAs');
        $this->assertCount(2, $sameas, "2 same as");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Resource", $sameas, "Notes mus be only resources");
        $this->assertEquals('http://vocab.getty.edu/tgn/7008038', $sameas[0]->getUri(), "uri must be http://vocab.getty.edu/tgn/7008038");
        $this->assertEquals('http://vocab.getty.edu/tgn/1234567890', $sameas[1]->getUri(), "uri must be http://vocab.getty.edu/tgn/1234567890");
    }


    public function testProvidedCHOSpatialNoSource() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NO_SPATIAL_SRC'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE_NO_SPATIAL']);
        $providedCHO = $this->resGraph['NO_SPATIAL_SRC']->get('edm:ProvidedCHO', '^rdf:type');

        //print($this->resGraph['NO_SPATIAL_SRC']->dump('ttl'));

        $this->assertNotNull($providedCHO);

        $spatials = $providedCHO->all($this->resGraph['NO_SPATIAL_SRC']->resource('http://purl.org/dc/terms/spatial'));

        $this->assertCount(1, $spatials, "Must have only one spatial node");
        $spatial = $spatials[0];
        $this->assertTrue($spatial->isBNode(),"spatial node must be blank");
        $this->assertEquals("edm:Place", $spatial->type(), "spatial node type must be edm:Place");

        $lats = $spatial->all('geo:lat');
        $this->assertCount(1, $lats, "One latitude");
        $this->assertInstanceOf("EasyRdf\Literal", $lats[0], "Latitude must be a litteral");
        $this->assertEquals(Literal::create("48.83975", null, 'xsd:float'), $lats[0], "lat must be '48.83975'^^xsd:float");

        $longs = $spatial->all('geo:long');
        $this->assertCount(1, $longs, "One longitude");
        $this->assertInstanceOf("EasyRdf\Literal", $longs[0], "Longitude must be a litteral");
        $this->assertEquals(Literal::create("2.3542", null, 'xsd:float'), $longs[0], "long must be '2.3542'^^xsd:float");

        $notes = $spatial->all('skos:note');
        $this->assertCount(2, $notes, "2 notes");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Literal", $notes, "Notes mus be only literals");
        $this->assertEquals(Literal::create("FR", null, "dc:ISO3166"), $notes[0], "notes contains 'FR'^^dc:ISO3166");
        $this->assertEquals(Literal::create("France, Paris, Université Sorbonne Nouvelle Paris 3, site Censier", "fr", null), $notes[1], "notes contains 'France, Centre, Loiret, Orléans'@fr");

        $sameas = $spatial->all('owl:sameAs');
        $this->assertCount(1, $sameas, "1 same as");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Resource", $sameas, "Notes mus be only resources");
        $this->assertEquals('http://vocab.getty.edu/tgn/7008038', $sameas[0]->getUri(), "uri must be http://vocab.getty.edu/tgn/7008038");
    }

    public function testProvidedCHOSpatialNoBase() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NO_SPATIAL_BASE'] = $merger->mergeGraph($this->inputGraphes['BASE_NO_SPATIAL'], $this->inputGraphes['SOURCE']);

        $providedCHO = $this->resGraph['NO_SPATIAL_BASE']->get('edm:ProvidedCHO', '^rdf:type');

        //print($this->resGraph['NO_SPATIAL_SRC']->dump('ttl'));

        $this->assertNotNull($providedCHO);

        $spatials = $providedCHO->all($this->resGraph['NO_SPATIAL_BASE']->resource('http://purl.org/dc/terms/spatial'));

        $this->assertCount(1, $spatials, "Must have only one spatial node");
        $spatial = $spatials[0];
        $this->assertTrue($spatial->isBNode(),"spatial node must be blank");
        $this->assertEquals("edm:Place", $spatial->type(), "spatial node type must be edm:Place");

        $lats = $spatial->all('geo:lat');
        $this->assertCount(1, $lats, "One latitude");
        $this->assertInstanceOf("EasyRdf\Literal", $lats[0], "Latitude must be a litteral");
        $this->assertEquals(Literal::create("99.83975", null, 'xsd:float'), $lats[0], "lat must be '99.83975'^^xsd:float");

        $longs = $spatial->all('geo:long');
        $this->assertCount(1, $longs, "One longitude");
        $this->assertInstanceOf("EasyRdf\Literal", $longs[0], "Longitude must be a litteral");
        $this->assertEquals(Literal::create("25.3542", null, 'xsd:float'), $longs[0], "long must be '25.3542'^^xsd:float");

        $notes = $spatial->all('skos:note');
        $this->assertCount(3, $notes, "3 notes");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Literal", $notes, "Notes mus be only literals");
        $this->assertEquals(Literal::create("FR", null, "dc:ISO3166"), $notes[0], "notes contains 'FR'^^dc:ISO3166");
        $this->assertEquals(Literal::create("France, Paris, Université Sorbonne Nouvelle Paris 3, site Censier", "fr", null), $notes[1], "notes contains 'France, Centre, Loiret, Orléans'@fr");
        $this->assertEquals(Literal::create("Other place very important", "fr", null), $notes[2], "notes contains 'Other place very important'@fr");

        $sameas = $spatial->all('owl:sameAs');
        $this->assertCount(1, $sameas, "1 same as");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Resource", $sameas, "Notes mus be only resources");
        $this->assertEquals('http://vocab.getty.edu/tgn/1234567890', $sameas[0]->getUri(), "uri must be http://vocab.getty.edu/tgn/1234567890");
    }


    /**
     * Test one to one mapping
     *
     * @return void
     */
    public function testProvidedCHOSingleproperty() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE']);

        $properties = [
            "http://purl.org/dc/elements/1.1/title",
            "http://purl.org/dc/elements/1.1/identifier",
            "http://purl.org/dc/terms/extent",
            "http://purl.org/dc/terms/available",
            "http://purl.org/dc/terms/created",
            "http://purl.org/dc/terms/issued"
        ];

        $providedCHO = $this->resGraph['NORMAL']->get('edm:ProvidedCHO', '^rdf:type');
        $baseNode = $this->inputGraphes['BASE']->get('edm:ProvidedCHO', '^rdf:type');

        $this->assertNotNull($providedCHO);
        $this->assertNotNull($baseNode);

        foreach ($properties as $prop) {
            $outputValuesStr = [];
            foreach($providedCHO->all($this->resGraph['NORMAL']->resource($prop)) as $outputValue) {
                array_push($outputValuesStr, strval($outputValue));
            }
            $this->assertNotEmpty($outputValuesStr, "we must found some values to test $prop");
            foreach ($baseNode->all($this->inputGraphes['BASE']->resource($prop)) as $value) {
                $this->assertContains(strval($value), $outputValuesStr, "$prop not found in output graph");
            }
        }

    }

    public function testRightsWebResource() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE']);

        $properties = [
            "http://purl.org/dc/elements/1.1/rights",
            'http://purl.org/dc/terms/license',
            'http://purl.org/dc/terms/accessRights',
        ];

        $webResourceList = $this->resGraph['NORMAL']->all('edm:WebResource', '^rdf:type');

        //just check that they have all these resources.
        //TODO: check that the same rights are kept (difficult)
        foreach ($webResourceList as $webResource) {
            foreach ($properties as $prop) {
                $outputValuesStr = [];
                foreach($webResource->all($this->resGraph['NORMAL']->resource($prop)) as $outputValue) {
                    array_push($outputValuesStr, strval($outputValue));
                }
                $this->assertNotEmpty($outputValuesStr, "we must found some values to test $prop");
            }
        }
    }

    /**
     * Test rights merge
     *
     * @return void
     */
    public function testRightsDifferentsAggregatedCHO() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['NORMAL'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE_RIGHTS']);


        $aggregationResList = $this->resGraph['NORMAL']->allOfType('http://www.openarchives.org/ore/terms/Aggregation');
        $this->assertCount(1, $aggregationResList, "Only one Aggregation node");
        $aggregationRes = $aggregationResList[0];

        $resList = $aggregationRes->all($this->inputGraphes['BASE']->resource('http://www.europeana.eu/schemas/edm/rights'));
        $this->assertCount(1, $resList, "'http://www.europeana.eu/schemas/edm/rights' list size 1");
        $this->assertEquals($this->resGraph['NORMAL']->resource("http://creativecommons.org/licenses/by-nc-sa/4.0/"), $resList[0], "'http://www.europeana.eu/schemas/edm/rights' size one not equals");

    }


    /**
     * Test one to one mapping spatial info
     *
     * @return void
     */

    public function testProvidedCHOSpatialMore() {
        $merger = new CocoonSoundRdfMerger();
        $this->resGraph['MORE_SPATIAL_NOTES'] = $merger->mergeGraph($this->inputGraphes['BASE'], $this->inputGraphes['SOURCE_MORE_SPATIAL_NOTES']);

        $providedCHO = $this->resGraph['MORE_SPATIAL_NOTES']->get('edm:ProvidedCHO', '^rdf:type');

        $this->assertNotNull($providedCHO);

        $spatials = $providedCHO->all($this->resGraph['MORE_SPATIAL_NOTES']->resource('http://purl.org/dc/terms/spatial'));

        $this->assertCount(1, $spatials, "Must have only one spatial node");
        $spatial = $spatials[0];
        $this->assertTrue($spatial->isBNode(),"spatial node must be blank");
        $this->assertEquals("edm:Place", $spatial->type(), "spatial node type must be edm:Place");

        $lats = $spatial->all('geo:lat');
        $this->assertCount(1, $lats, "One latitude");
        $this->assertInstanceOf("EasyRdf\Literal", $lats[0], "Latitude must be a litteral");
        $this->assertEquals(Literal::create("48.83975", null, 'xsd:float'), $lats[0], "lat must be '48.83975'^^xsd:float");

        $longs = $spatial->all('geo:long');
        $this->assertCount(1, $longs, "One longitude");
        $this->assertInstanceOf("EasyRdf\Literal", $longs[0], "Longitude must be a litteral");
        $this->assertEquals(Literal::create("2.3542", null, 'xsd:float'), $longs[0], "long must be '2.3542'^^xsd:float");

        $notes = $spatial->all('skos:note');
        $this->assertCount(4, $notes, "4 notes");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Literal", $notes, "Notes must be only literals");
        $this->assertEquals(Literal::create("FR", null, "dc:ISO3166"), $notes[0], "notes contains 'FR'^^dc:ISO3166");
        $this->assertEquals(Literal::create("France, Paris, Université Sorbonne Nouvelle Paris 3, site Censier", "fr", null), $notes[1], "notes contains 'France, Centre, Loiret, Orléans'@fr");
        $this->assertEquals(Literal::create("Other place very important", "en", null), $notes[2], "notes contains 'Other place very important'@en");
        $this->assertEquals(Literal::create("Autre place importante", "fr", null), $notes[3], "notes contains 'Autre place importante'@fr");

        $sameas = $spatial->all('owl:sameAs');
        $this->assertCount(2, $sameas, "2 same as");
        $this->assertContainsOnlyInstancesOf("EasyRdf\Resource", $sameas, "Notes mus be only resources");
        $this->assertEquals('http://vocab.getty.edu/tgn/7008038', $sameas[0]->getUri(), "uri must be http://vocab.getty.edu/tgn/7008038");
        $this->assertEquals('http://vocab.getty.edu/tgn/1234567890', $sameas[1]->getUri(), "uri must be http://vocab.getty.edu/tgn/1234567890");
    }


}
