# coding: utf-8
require_relative 'spec_helper'
require 'rdf/spec/writer'

describe JSON::LD::API do
  let(:logger) {RDF::Spec.logger}

  describe ".fromRdf" do
    context "simple tests" do
      it "One subject IRI object" do
        input = %(<http://a/b> <http://a/c> <http://a/d> .)
        expect(serialize(input)).to produce_jsonld([
        {
          '@id'         => "http://a/b",
          "http://a/c"  => [{"@id" => "http://a/d"}]
        }
        ], logger)
      end

      it "should generate object list" do
        input = %(@prefix : <http://example.com/> . :b :c :d, :e .)
        expect(serialize(input)).
        to produce_jsonld([{
          '@id'                         => "http://example.com/b",
          "http://example.com/c" => [
            {"@id" => "http://example.com/d"},
            {"@id" => "http://example.com/e"}
          ]
        }
        ], logger)
      end
    
      it "should generate property list" do
        input = %(@prefix : <http://example.com/> . :b :c :d; :e :f .)
        expect(serialize(input)).
        to produce_jsonld([{
          '@id'   => "http://example.com/b",
          "http://example.com/c"      => [{"@id" => "http://example.com/d"}],
          "http://example.com/e"      => [{"@id" => "http://example.com/f"}]
        }
        ], logger)
      end
    
      it "serializes multiple subjects" do
        input = %q(
          @prefix : <http://www.w3.org/2006/03/test-description#> .
          @prefix dc: <http://purl.org/dc/elements/1.1/> .
          <test-cases/0001> a :TestCase .
          <test-cases/0002> a :TestCase .
        )
        expect(serialize(input)).
        to produce_jsonld([
          {'@id'  => "test-cases/0001", '@type' => ["http://www.w3.org/2006/03/test-description#TestCase"]},
          {'@id'  => "test-cases/0002", '@type' => ["http://www.w3.org/2006/03/test-description#TestCase"]},
        ], logger)
      end
    end
  
    context "literals" do
      context "coercion" do
        it "typed literal" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b "foo"^^ex:d .)
          expect(serialize(input)).to produce_jsonld([
            {
              '@id'   => "http://example.com/a",
              "http://example.com/b"    => [{"@value" => "foo", "@type" => "http://example.com/d"}]
            }
          ], logger)
        end

        it "integer" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b 1 .)
          expect(serialize(input, useNativeTypes: true)).to produce_jsonld([{
            '@id'   => "http://example.com/a",
            "http://example.com/b"    => [{"@value" => 1}]
          }], logger)
        end

        it "integer (non-native)" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b 1 .)
          expect(serialize(input, useNativeTypes: false)).to produce_jsonld([{
            '@id'   => "http://example.com/a",
            "http://example.com/b"    => [{"@value" => "1","@type" => "http://www.w3.org/2001/XMLSchema#integer"}]
          }], logger)
        end

        it "boolean" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b true .)
          expect(serialize(input, useNativeTypes: true)).to produce_jsonld([{
            '@id'   => "http://example.com/a",
            "http://example.com/b"    => [{"@value" => true}]
          }], logger)
        end

        it "boolean (non-native)" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b true .)
          expect(serialize(input, useNativeTypes: false)).to produce_jsonld([{
            '@id'   => "http://example.com/a",
            "http://example.com/b"    => [{"@value" => "true","@type" => "http://www.w3.org/2001/XMLSchema#boolean"}]
          }], logger)
        end

        it "decmal" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b 1.0 .)
          expect(serialize(input, useNativeTypes: true)).to produce_jsonld([{
            '@id'   => "http://example.com/a",
            "http://example.com/b"    => [{"@value" => "1.0", "@type" => "http://www.w3.org/2001/XMLSchema#decimal"}]
          }], logger)
        end

        it "double" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b 1.0e0 .)
          expect(serialize(input, useNativeTypes: true)).to produce_jsonld([{
            '@id'   => "http://example.com/a",
            "http://example.com/b"    => [{"@value" => 1.0E0}]
          }], logger)
        end

        it "double (non-native)" do
          input = %(@prefix ex: <http://example.com/> . ex:a ex:b 1.0e0 .)
          expect(serialize(input, useNativeTypes: false)).to produce_jsonld([{
            '@id'   => "http://example.com/a",
            "http://example.com/b"    => [{"@value" => "1.0E0","@type" => "http://www.w3.org/2001/XMLSchema#double"}]
          }], logger)
        end
      end

      context "datatyped (non-native)" do
        {
          integer:            1,
          unsignedInteger:    1,
          nonNegativeInteger: 1,
          float:              1,
          nonPositiveInteger: -1,
          negativeInteger:    -1,
        }.each do |t, v|
          it "#{t}" do
            input = %(
              @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
              @prefix ex: <http://example.com/> .
              ex:a ex:b "#{v}"^^xsd:#{t} .
            )
            expect(serialize(input, useNativeTypes: false)).to produce_jsonld([{
              '@id'   => "http://example.com/a",
              "http://example.com/b"    => [{"@value" => "#{v}","@type" => "http://www.w3.org/2001/XMLSchema##{t}"}]
            }], logger)
          end
        end
      end

      it "encodes language literal" do
        input = %(@prefix ex: <http://example.com/> . ex:a ex:b "foo"@en-us .)
        expect(serialize(input)).to produce_jsonld([{
          '@id'   => "http://example.com/a",
          "http://example.com/b"    => [{"@value" => "foo", "@language" => "en-us"}]
        }], logger)
      end

      context "with @type: @json" do
        {
          "true": {
            output: %([{
               "@id": "http://example.org/vocab#id",
               "http://example.org/vocab#bool": [{"@value": true, "@type": "@json"}]
             }]),
            input:%(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:bool "true"^^rdf:JSON .
            )
          },
          "false": {
            output: %([{
               "@id": "http://example.org/vocab#id",
               "http://example.org/vocab#bool": [{"@value": false, "@type": "@json"}]
             }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:bool "false"^^rdf:JSON .
            )
          },
          "double": {
            output: %([{
               "@id": "http://example.org/vocab#id",
               "http://example.org/vocab#double": [{"@value": 1.23E0, "@type": "@json"}]
             }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:double "1.23E0"^^rdf:JSON .
            )
          },
          "double-zero": {
            output: %([{
               "@id": "http://example.org/vocab#id",
               "http://example.org/vocab#double": [{"@value": 0, "@type": "@json"}]
             }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:double "0.0E0"^^rdf:JSON .
            )
          },
          "integer": {
            output: %([{
               "@id": "http://example.org/vocab#id",
               "http://example.org/vocab#integer": [{"@value": 123, "@type": "@json"}]
             }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:integer "123"^^rdf:JSON .
            )
          },
          "string": {
            output: %([{
              "@id": "http://example.org/vocab#id",
              "http://example.org/vocab#string": [{
                "@value": "string",
                "@type": "@json"
              }]
            }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:string "\\"string\\""^^rdf:JSON .
            )
          },
          "null": {
            output: %([{
              "@id": "http://example.org/vocab#id",
              "http://example.org/vocab#null": [{
                "@value": null,
                "@type": "@json"
              }]
            }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:null "null"^^rdf:JSON .
            )
          },
          "object": {
            output: %([{
               "@id": "http://example.org/vocab#id",
               "http://example.org/vocab#object": [{"@value": {"foo": "bar"}, "@type": "@json"}]
             }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:object """{"foo":"bar"}"""^^rdf:JSON .
            )
          },
          "array": {
            output: %([{
               "@id": "http://example.org/vocab#id",
               "http://example.org/vocab#array": [{"@value": [{"foo": "bar"}], "@type": "@json"}]
             }]),
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:array """[{"foo":"bar"}]"""^^rdf:JSON .
            )
          },
        }.each do |title, params|
          params[:input] = RDF::Graph.new << RDF::Turtle::Reader.new(params[:input])
          it(title) {do_fromRdf(processingMode: "json-ld-1.1", **params)}
        end
      end

      context "extendedRepresentation: true" do
        {
          "true": {
            output: [{
               "@id" => "http://example.org/vocab#id",
               "http://example.org/vocab#bool" => [{"@value" => RDF::Literal(true)}]
             }],
            input:%(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:bool true .
            )
          },
          "false": {
            output: [{
               "@id" => "http://example.org/vocab#id",
               "http://example.org/vocab#bool" => [{"@value" => RDF::Literal(false)}]
             }],
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:bool false .
            )
          },
          "double": {
            output: [{
               "@id" => "http://example.org/vocab#id",
               "http://example.org/vocab#double" => [{"@value" => RDF::Literal(1.23E0)}]
             }],
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:double 1.23E0 .
            )
          },
          "double-zero": {
            output: [{
               "@id" => "http://example.org/vocab#id",
               "http://example.org/vocab#double" => [{"@value" => RDF::Literal(0, datatype: RDF::XSD.double)}]
             }],
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:double 0.0E0 .
            )
          },
          "integer": {
            output: [{
               "@id" => "http://example.org/vocab#id",
               "http://example.org/vocab#integer" => [{"@value" => RDF::Literal(123)}]
             }],
            input: %(
              @prefix ex: <http://example.org/vocab#> .
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              ex:id ex:integer 123 .
            )
          },
        }.each do |title, params|
          params[:input] = RDF::Graph.new << RDF::Turtle::Reader.new(params[:input])
          it(title) {
            do_fromRdf(processingMode: "json-ld-1.1",
            useNativeTypes: true,
            extendedRepresentation: true,
            **params)}
        end
      end
    end

    context "anons" do
      it "should generate bare anon" do
        input = %(@prefix : <http://example.com/> . _:a :a :b .)
        expect(serialize(input)).to produce_jsonld([
        {
          "@id" => "_:a",
          "http://example.com/a"  => [{"@id" => "http://example.com/b"}]
        }
        ], logger)
      end
    
      it "should generate anon as object" do
        input = %(@prefix : <http://example.com/> . :a :b _:a . _:a :c :d .)
        expect(serialize(input)).to produce_jsonld([
          {
            "@id" => "_:a",
            "http://example.com/c"  => [{"@id" => "http://example.com/d"}]
          },
          {
            "@id" => "http://example.com/a",
            "http://example.com/b"  => [{"@id" => "_:a"}]
          }
        ], logger)
      end
    end

    context "lists" do
      {
        "literal list" => {
          input: %q(
            @prefix : <http://example.com/> .
            @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
            :a :b ("apple" "banana")  .
          ),
          output: [{
            '@id'   => "http://example.com/a",
            "http://example.com/b"  => [{
              "@list" => [
                {"@value" => "apple"},
                {"@value" => "banana"}
              ]
            }]
          }]
        },
        "iri list" => {
          input: %q(@prefix : <http://example.com/> . :a :b (:c) .),
          output: [{
            '@id'   => "http://example.com/a",
            "http://example.com/b"  => [{
              "@list" => [
                {"@id" => "http://example.com/c"}
              ]
            }]
          }]
        },
        "empty list" => {
          input: %q(@prefix : <http://example.com/> . :a :b () .),
          output: [{
            '@id'   => "http://example.com/a",
            "http://example.com/b"  => [{"@list" => []}]
          }]
        },
        "single element list" => {
          input: %q(@prefix : <http://example.com/> . :a :b ( "apple" ) .),
          output: [{
            '@id'   => "http://example.com/a",
            "http://example.com/b"  => [{"@list" => [{"@value" => "apple"}]}]
          }]
        },
        "single element list without @type" => {
          input: %q(@prefix : <http://example.com/> . :a :b ( _:a ) . _:a :b "foo" .),
          output: [
            {
              '@id'   => "_:a",
              "http://example.com/b"  => [{"@value" => "foo"}]
            },
            {
              '@id'   => "http://example.com/a",
              "http://example.com/b"  => [{"@list" => [{"@id" => "_:a"}]}]
            },
          ]
        },
        "multiple graphs with shared BNode" => {
          input: %q(
            <http://www.example.com/z> <http://www.example.com/q> _:z0 <http://www.example.com/G> .
            _:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-A" <http://www.example.com/G> .
            _:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:z1 <http://www.example.com/G> .
            _:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-B" <http://www.example.com/G> .
            _:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://www.example.com/G> .
            <http://www.example.com/x> <http://www.example.com/p> _:z1 <http://www.example.com/G1> .
          ),
          output: [{
            "@id" => "http://www.example.com/G",
            "@graph" => [{
              "@id" => "_:z0",
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#first" => [{"@value" => "cell-A"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest" => [{"@id" => "_:z1"}]
            }, {
              "@id" => "_:z1",
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#first" => [{"@value" => "cell-B"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest" => [{"@list" => []}]
            }, {
              "@id" => "http://www.example.com/z",
              "http://www.example.com/q" => [{"@id" => "_:z0"}]
            }]
          },
          {
            "@id" => "http://www.example.com/G1",
            "@graph" => [{
              "@id" => "http://www.example.com/x",
              "http://www.example.com/p" => [{"@id" => "_:z1"}]
            }]
          }],
          reader: RDF::NQuads::Reader
        },
        "multiple graphs with shared BNode (at head)" => {
          input: %q(
            <http://www.example.com/z> <http://www.example.com/q> _:z0 <http://www.example.com/G> .
            _:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-A" <http://www.example.com/G> .
            _:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:z1 <http://www.example.com/G> .
            _:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-B" <http://www.example.com/G> .
            _:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://www.example.com/G> .
            <http://www.example.com/z> <http://www.example.com/q> _:z0 <http://www.example.com/G1> .
          ),
          output: [{
            "@id" => "http://www.example.com/G",
            "@graph" => [{
              "@id" => "_:z0",
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#first" => [{"@value" => "cell-A"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest" => [{"@list" => [{ "@value" => "cell-B" }]}]
            }, {
              "@id" => "http://www.example.com/z",
              "http://www.example.com/q" => [{"@id" => "_:z0"}]
            }]
          },
          {
            "@id" => "http://www.example.com/G1",
            "@graph" => [{
              "@id" => "http://www.example.com/z",
              "http://www.example.com/q" => [{"@id" => "_:z0"}]
            }]
          }],
          reader: RDF::NQuads::Reader
        },
        "@list containing empty @list" => {
          input: %(
            <http://example.com/a> <http://example.com/property> (()) .
          ),
          output: %([{
            "@id": "http://example.com/a",
            "http://example.com/property": [{"@list": [{"@list": []}]}]
          }]),
          reader: RDF::Turtle::Reader
        },
        "@list containing multiple lists" => {
          input: %(
            <http://example.com/a> <http://example.com/property> (("a") ("b")) .
          ),
          output: %([{
            "@id": "http://example.com/a",
            "http://example.com/property": [{"@list": [
              {"@list": [{"@value": "a"}]},
              {"@list": [{"@value": "b"}]}
            ]}]
          }]),
          reader: RDF::Turtle::Reader
        },
        "0008a" => {
          input: %(
          <http://example.com> <http://example.com/property> _:outerlist .
          _:outerlist <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> _:lista .
          _:outerlist <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b0 .

          _:lista <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "a1" .
          _:lista <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:a2 .
          _:a2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "a2" .
          _:a2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:a3 .
          _:a3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "a3" .
          _:a3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .

          _:c0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> _:c1 .
          _:c0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
          _:c1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "c1" .
          _:c1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:c2 .
          _:c2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "c2" .
          _:c2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:c3 .
          _:c3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "c3" .
          _:c3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .

          _:b0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> _:b1 .
          _:b0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:c0 .
          _:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "b1" .
          _:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b2 .
          _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "b2" .
          _:b2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b3 .
          _:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "b3" .
          _:b3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
          ),
          output: JSON.parse(%([
            {
              "@id": "http://example.com",
              "http://example.com/property": [
                {
                  "@list": [
                    {"@list": [{"@value": "a1"}, {"@value": "a2"}, {"@value": "a3"}]},
                    {"@list": [{"@value": "b1"}, {"@value": "b2"}, {"@value": "b3"}]},
                    {"@list": [{"@value": "c1"}, {"@value": "c2"}, {"@value": "c3"}]}
                  ]
                }
              ]
            }
          ])),
          reader: RDF::NQuads::Reader
        }
      }.each do |name, params|
        it "#{name}" do
          do_fromRdf(params)
        end
      end
    end
    
    context "quads" do
      {
        "simple named graph" => {
          input: %(
            <http://example.com/a> <http://example.com/b> <http://example.com/c> <http://example.com/U> .
          ),
          output: [
            {
              "@id" => "http://example.com/U",
              "@graph" => [{
                "@id" => "http://example.com/a",
                "http://example.com/b" => [{"@id" => "http://example.com/c"}]
              }]
            },
          ]
        },
        "with properties" => {
          input: %(
            <http://example.com/a> <http://example.com/b> <http://example.com/c> <http://example.com/U> .
            <http://example.com/U> <http://example.com/d> <http://example.com/e> .
          ),
          output: [
            {
              "@id" => "http://example.com/U",
              "@graph" => [{
                "@id" => "http://example.com/a",
                "http://example.com/b" => [{"@id" => "http://example.com/c"}]
              }],
              "http://example.com/d" => [{"@id" => "http://example.com/e"}]
            }
          ]
        },
        "with lists" => {
          input: %(
            <http://example.com/a> <http://example.com/b> _:a <http://example.com/U> .
            _:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <http://example.com/c> <http://example.com/U> .
            _:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.com/U> .
            <http://example.com/U> <http://example.com/d> _:b .
            _:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <http://example.com/e> .
            _:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
          ),
          output: [
            {
              "@id" => "http://example.com/U",
              "@graph" => [{
                "@id" => "http://example.com/a",
                "http://example.com/b" => [{"@list" => [{"@id" => "http://example.com/c"}]}]
              }],
              "http://example.com/d" => [{"@list" => [{"@id" => "http://example.com/e"}]}]
            }
          ]
        },
        "Two Graphs with same subject and lists" => {
          input: %(
            <http://example.com/a> <http://example.com/b> _:a <http://example.com/U> .
            _:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <http://example.com/c> <http://example.com/U> .
            _:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.com/U> .
            <http://example.com/a> <http://example.com/b> _:b <http://example.com/V> .
            _:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <http://example.com/e> <http://example.com/V> .
            _:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://example.com/V> .
          ),
          output: [
            {
              "@id" => "http://example.com/U",
              "@graph" => [
                {
                  "@id" => "http://example.com/a",
                  "http://example.com/b" => [{
                    "@list" => [{"@id" => "http://example.com/c"}]
                  }]
                }
              ]
            },
            {
              "@id" => "http://example.com/V",
              "@graph" => [
                {
                  "@id" => "http://example.com/a",
                  "http://example.com/b" => [{
                    "@list" => [{"@id" => "http://example.com/e"}]
                  }]
                }
              ]
            }
          ]
        },
      }.each_pair do |name, params|
        it "#{name}" do
          do_fromRdf(params.merge(reader: RDF::NQuads::Reader))
        end
      end
    end

    context "@direction" do
      context "rdfDirection: null" do
        {
          "no language rtl datatype": {
            input: %q(
              <http://example.com/a> <http://example.org/label> "no language"^^<https://www.w3.org/ns/i18n#_rtl> .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "no language", "@type": "https://www.w3.org/ns/i18n#_rtl"}]
            }]),
          },
          "no language rtl compound-literal": {
            input: %q(
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              <http://example.com/a> <http://example.org/label> _:cl1 .

              _:cl1 rdf:value "no language";
                rdf:direction "rtl" .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@id": "_:cl1"}]
            }, {
              "@id": "_:cl1",
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#value": [{"@value": "no language"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#direction": [{"@value": "rtl"}]
            }]),
          },
          "en-US rtl datatype": {
            input: %q(
              <http://example.com/a> <http://example.org/label> "en-US"^^<https://www.w3.org/ns/i18n#en-us_rtl> .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "en-US", "@type": "https://www.w3.org/ns/i18n#en-us_rtl"}]
            }]),
          },
          "en-US rtl compound-literal": {
            input: %q(
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              <http://example.com/a> <http://example.org/label> _:cl1 .

              _:cl1 rdf:value "en-US";
                rdf:language "en-us";
                rdf:direction "rtl" .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@id": "_:cl1"}]
            }, {
              "@id": "_:cl1",
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#value": [{"@value": "en-US"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#language": [{"@value": "en-us"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#direction": [{"@value": "rtl"}]
            }]),
          }
        }.each_pair do |name, params|
          it name do
            do_fromRdf(params.merge(reader: RDF::Turtle::Reader, rdfDirection: nil))
          end
        end
      end

      context "rdfDirection: i18n-datatype" do
        {
          "no language rtl datatype": {
            input: %q(
              <http://example.com/a> <http://example.org/label> "no language"^^<https://www.w3.org/ns/i18n#_rtl> .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "no language", "@direction": "rtl"}]
            }]),
          },
          "no language rtl compound-literal": {
            input: %q(
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              <http://example.com/a> <http://example.org/label> _:cl1 .

              _:cl1 rdf:value "no language";
                rdf:direction "rtl" .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@id": "_:cl1"}]
            }, {
              "@id": "_:cl1",
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#value": [{"@value": "no language"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#direction": [{"@value": "rtl"}]
            }]),
          },
          "en-US rtl datatype": {
            input: %q(
              <http://example.com/a> <http://example.org/label> "en-US"^^<https://www.w3.org/ns/i18n#en-US_rtl> .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "en-US", "@language": "en-US", "@direction": "rtl"}]
            }]),
          },
          "en-US rtl compound-literal": {
            input: %q(
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              <http://example.com/a> <http://example.org/label> _:cl1 .

              _:cl1 rdf:value "en-US";
                rdf:language "en-US";
                rdf:direction "rtl" .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@id": "_:cl1"}]
            }, {
              "@id": "_:cl1",
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#value": [{"@value": "en-US"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#language": [{"@value": "en-US"}],
              "http://www.w3.org/1999/02/22-rdf-syntax-ns#direction": [{"@value": "rtl"}]
            }]),
          }
        }.each_pair do |name, params|
          it name do
            do_fromRdf(params.merge(reader: RDF::Turtle::Reader, rdfDirection: 'i18n-datatype', processingMode: 'json-ld-1.1'))
          end
        end
      end

      context "rdfDirection: compound-literal" do
        {
          "no language rtl datatype": {
            input: %q(
              <http://example.com/a> <http://example.org/label> "no language"^^<https://www.w3.org/ns/i18n#_rtl> .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "no language", "@type": "https://www.w3.org/ns/i18n#_rtl"}]
            }]),
          },
          "no language rtl compound-literal": {
            input: %q(
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              <http://example.com/a> <http://example.org/label> _:cl1 .

              _:cl1 rdf:value "no language";
                rdf:direction "rtl" .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "no language", "@direction": "rtl"}]
            }]),
          },
          "en-US rtl datatype": {
            input: %q(
              <http://example.com/a> <http://example.org/label> "en-US"^^<https://www.w3.org/ns/i18n#en-us_rtl> .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "en-US", "@type": "https://www.w3.org/ns/i18n#en-us_rtl"}]
            }]),
          },
          "en-US rtl compound-literal": {
            input: %q(
              @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
              <http://example.com/a> <http://example.org/label> _:cl1 .

              _:cl1 rdf:value "en-US";
                rdf:language "en-us";
                rdf:direction "rtl" .
            ),
            output: %q([{
              "@id": "http://example.com/a",
              "http://example.org/label": [{"@value": "en-US", "@language": "en-us", "@direction": "rtl"}]
            }]),
          }
        }.each_pair do |name, params|
          it name do
            do_fromRdf(params.merge(reader: RDF::Turtle::Reader, rdfDirection: 'compound-literal', processingMode: 'json-ld-1.1'))
          end
        end
      end
    end

    context "RDF-star" do
      {
        "subject-iii": {
          input: RDF::Statement(
            RDF::Statement(
              RDF::URI('http://example/s1'),
              RDF::URI('http://example/p1'),
              RDF::URI('http://example/o1')),
            RDF::URI('http://example/p'),
            RDF::URI('http://example/o')),
          output: %([{
            "@id": {
              "@id": "http://example/s1",
              "http://example/p1": [{"@id": "http://example/o1"}]
            },
            "http://example/p": [{"@id": "http://example/o"}]
          }])
        },
        "subject-iib": {
          input: RDF::Statement(
            RDF::Statement(
              RDF::URI('http://example/s1'),
              RDF::URI('http://example/p1'),
              RDF::Node.new('o1')),
            RDF::URI('http://example/p'),
            RDF::URI('http://example/o')),
          output: %([{
            "@id": {
              "@id": "http://example/s1",
              "http://example/p1": [{"@id": "_:o1"}]
            },
            "http://example/p": [{"@id": "http://example/o"}]
          }])
        },
        "subject-iil": {
          input: RDF::Statement(
            RDF::Statement(
              RDF::URI('http://example/s1'),
              RDF::URI('http://example/p1'),
              RDF::Literal('o1')),
            RDF::URI('http://example/p'),
            RDF::URI('http://example/o')),
          output: %([{
            "@id": {
              "@id": "http://example/s1",
              "http://example/p1": [{"@value": "o1"}]
            },
            "http://example/p": [{"@id": "http://example/o"}]
          }])
        },
        "subject-bii": {
          input: RDF::Statement(
            RDF::Statement(
              RDF::Node('s1'),
              RDF::URI('http://example/p1'),
              RDF::URI('http://example/o1')),
            RDF::URI('http://example/p'),
            RDF::URI('http://example/o')),
          output: %([{
            "@id": {
              "@id": "_:s1",
              "http://example/p1": [{"@id": "http://example/o1"}]
            },
            "http://example/p": [{"@id": "http://example/o"}]
          }])
        },
        "subject-bib": {
          input: RDF::Statement(
            RDF::Statement(
              RDF::Node('s1'),
              RDF::URI('http://example/p1'),
              RDF::Node.new('o1')),
            RDF::URI('http://example/p'), RDF::URI('http://example/o')),
          output: %([{
            "@id": {
              "@id": "_:s1",
              "http://example/p1": [{"@id": "_:o1"}]
            },
            "http://example/p": [{"@id": "http://example/o"}]
          }])
        },
        "subject-bil": {
          input: RDF::Statement(
            RDF::Statement(
              RDF::Node('s1'),
              RDF::URI('http://example/p1'),
              RDF::Literal('o1')),
            RDF::URI('http://example/p'),
            RDF::URI('http://example/o')),
          output: %([{
            "@id": {
              "@id": "_:s1",
              "http://example/p1": [{"@value": "o1"}]
            },
            "http://example/p": [{"@id": "http://example/o"}]
          }])
        },
        "object-iii":  {
          input: RDF::Statement(
            RDF::URI('http://example/s'),
            RDF::URI('http://example/p'),
            RDF::Statement(
              RDF::URI('http://example/s1'),
              RDF::URI('http://example/p1'),
              RDF::URI('http://example/o1'))),
          output: %([{
            "@id": "http://example/s",
            "http://example/p": [{
              "@id": {
                "@id": "http://example/s1",
                "http://example/p1": [{"@id": "http://example/o1"}]
              }
            }]
          }])
        },
        "object-iib":  {
          input: RDF::Statement(
            RDF::URI('http://example/s'),
            RDF::URI('http://example/p'),
            RDF::Statement(
              RDF::URI('http://example/s1'),
              RDF::URI('http://example/p1'),
              RDF::Node.new('o1'))),
          output: %([{
            "@id": "http://example/s",
            "http://example/p": [{
              "@id": {
                "@id": "http://example/s1",
                "http://example/p1": [{"@id": "_:o1"}]
              }
            }]
          }])
        },
        "object-iil":  {
          input: RDF::Statement(
            RDF::URI('http://example/s'),
            RDF::URI('http://example/p'),
            RDF::Statement(
              RDF::URI('http://example/s1'),
              RDF::URI('http://example/p1'),
              RDF::Literal('o1'))),
          output: %([{
            "@id": "http://example/s",
            "http://example/p": [{
              "@id": {
                "@id": "http://example/s1",
                "http://example/p1": [{"@value": "o1"}]
              }
            }]
          }])
        },
        "recursive-subject": {
          input: RDF::Statement(
            RDF::Statement(
              RDF::Statement(
                RDF::URI('http://example/s2'),
                RDF::URI('http://example/p2'),
                RDF::URI('http://example/o2')),
              RDF::URI('http://example/p1'),
              RDF::URI('http://example/o1')),
            RDF::URI('http://example/p'),
            RDF::URI('http://example/o')),
          output: %([{
            "@id": {
              "@id": {
                "@id": "http://example/s2",
                "http://example/p2": [{"@id": "http://example/o2"}]
              },
              "http://example/p1": [{"@id": "http://example/o1"}]
            },
            "http://example/p": [{"@id": "http://example/o"}]
          }])
        },
      }.each do |name, params|
        it name do
          graph = RDF::Graph.new {|g| g << params[:input]}
          do_fromRdf(params.merge(input: graph, prefixes: {ex: 'http://example/'}))
        end
      end
    end

    context "problems" do
      {
        "xsd:boolean as value" => {
          input: %(
            @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
            @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

            <http://data.wikia.com/terms#playable> rdfs:range xsd:boolean .
          ),
          output: [{
            "@id" => "http://data.wikia.com/terms#playable",
            "http://www.w3.org/2000/01/rdf-schema#range" => [
              { "@id" => "http://www.w3.org/2001/XMLSchema#boolean" }
            ]
          }]
        },
      }.each do |t, params|
        it "#{t}" do
          do_fromRdf(params)
        end
      end
    end
  end

  def parse(input, **options)
    reader = options[:reader] || RDF::TriG::Reader
    reader.new(input, **options, &:each_statement).to_a.extend(RDF::Enumerable)
  end

  # Serialize ntstr to a string and compare against regexps
  def serialize(ntstr, **options)
    logger.info ntstr if ntstr.is_a?(String)
    g = ntstr.is_a?(String) ? parse(ntstr, **options) : ntstr
    logger.info g.dump(:trig)
    statements = g.each_statement.to_a
    JSON::LD::API.fromRdf(statements, logger: logger, **options)
  end

  def do_fromRdf(params)
    begin
      input, output = params[:input], params[:output]
      output = ::JSON.parse(output) if output.is_a?(String)
      jld = nil
      if params[:write]
        expect{jld = serialize(input, **params)}.to write(params[:write]).to(:error)
      else
        expect{jld = serialize(input, **params)}.not_to write.to(:error)
      end
      expect(jld).to produce_jsonld(output, logger)
    rescue JSON::LD::JsonLdError => e
      fail("#{e.class}: #{e.message}\n" +
        "#{logger}\n" +
        "Backtrace:\n#{e.backtrace.join("\n")}")
    end
  end
end