/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.riot.system;

import static org.apache.jena.riot.RDFLanguages.JSONLD;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Iterator;
import java.util.Map;

import org.junit.jupiter.api.Test;

import org.apache.jena.atlas.lib.Creator;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.sparql.sse.SSE;
import org.apache.jena.sparql.util.IsoMatcher;

/** tests : JSONLD->RDF; JSONLD->RDF->JSONLD */
public class TestJsonLDReadWrite
{
    private static String DIR = "testing/RIOT/jsonld/";

    @Test public void read_g01() { graphJ2R("graph1.jsonld", "graph1.ttl"); }

    @Test public void read_g02() { graphJ2R("graph2.jsonld", "graph2.ttl"); }

    @Test public void read_ds01() { datasetJ2R("graph1.jsonld", "graph1.ttl"); }

    @Test public void read_ds02() { datasetJ2R("graph2.jsonld", "graph2.ttl"); }

    @Test public void read_ds03() { datasetJ2R("dataset1.jsonld", "dataset1.trig"); }

    private void graphJ2R(String inFile, String outFile)
    {
        inFile = DIR+inFile;
        outFile = DIR+outFile;
        Model model1 = RDFDataMgr.loadModel(inFile);
        Model model2 = RDFDataMgr.loadModel(outFile);
        assertTrue(model1.isIsomorphicWith(model2));
    }

    private void datasetJ2R(String inFile, String outFile)
    {
        inFile = DIR+inFile;
        outFile = DIR+outFile;
        RDFDataMgr.loadDataset(inFile);
        RDFDataMgr.loadDataset(outFile);
    }


    @Test public void roundtrip_01() { rtRJRg("graph1.ttl"); }

    @Test public void roundtrip_02() { rtRJRds("graph1.ttl"); }

    @Test public void roundtrip_03() { rtRJRds("dataset1.trig"); }

    static void rtRJRg(String filename)
    {
        filename = DIR+filename;
        // Read in
        Model model = RDFDataMgr.loadModel(filename);

        // Write a JSON-LD
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        RDFDataMgr.write(out, model, JSONLD);
        ByteArrayInputStream r = new ByteArrayInputStream(out.toByteArray());

//        System.out.println();
//        System.out.println(new String(out.toByteArray()));
//        System.out.println();

        // Read as JSON-LD
        Model model2 = ModelFactory.createDefaultModel();
        RDFDataMgr.read(model2, r, null, JSONLD);

        // Compare
        if ( ! model.isIsomorphicWith(model2) )
            System.out.println("## ---- DIFFERENT");

        assertTrue(model.isIsomorphicWith(model2));

        // Check namespaces in parsed graph match the original data
        checkNamespaces(model2, model.getNsPrefixMap());
    }

    static void rtRJRds(String filename)
    {
        //Creator<Dataset> creator = ()->DatasetFactory.createTxnMem();
        Creator<Dataset> creator = ()->DatasetFactory.createGeneral();

        filename = DIR+filename;
        Dataset ds1 = creator.create();
        RDFDataMgr.read(ds1, filename);

        // Write a JSON-LD
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        RDFDataMgr.write(out, ds1, JSONLD);
        ByteArrayInputStream r = new ByteArrayInputStream(out.toByteArray());

        // Read as JSON-LD
        // This must be the same kind of dataset as ds1 due to treatment of prefixes
        // on individual graphs for legacy.
        Dataset ds2 = creator.create();
        RDFDataMgr.read(ds2, r, null, JSONLD);

        if ( ! isIsomorphic(ds1, ds2) )
        {
            SSE.write(ds1);
            SSE.write(ds2);
        }

        assertTrue(isIsomorphic(ds1, ds2) );

        // Check namespaces in the parsed dataset match those in the original data
        checkNamespaces(ds2.getDefaultModel(), ds1.getDefaultModel().getNsPrefixMap());
        Iterator<String> graphNames = ds2.listNames();
        while (graphNames.hasNext()) {
            String gn = graphNames.next();
            checkNamespaces(ds2.getNamedModel(gn), ds1.getNamedModel(gn).getNsPrefixMap());
        }
    }

    private static boolean isIsomorphic(Dataset ds1, Dataset ds2) {
        return IsoMatcher.isomorphic(ds1.asDatasetGraph(), ds2.asDatasetGraph());
    }

    private static void checkNamespaces(Model m, Map<String, String> namespaces) {
        if ( namespaces == null )
            return;

        for ( String prefix : namespaces.keySet() ) {
            if ( !prefix.isEmpty() )
                assertEquals(namespaces.get(prefix), m.getNsPrefixURI(prefix),
                             ()->"Model does not contain expected namespace " + prefix + ": <" + namespaces.get(prefix) + ">");
        }
    }
}

