@prefix sh:   <http://www.w3.org/ns/shacl#> .
@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .
@prefix owl:  <http://www.w3.org/2002/07/owl#> .

@prefix car:        <http://ontology.rys.one/project/vocabulary/car#> .
@prefix car-shapes: <http://ontology.rys.one/project/vocabulary/car-shapes#> .


# SHACL-SPARQL supports writing SPARQL by separating two parts:
# 1) the SPARQL prologue corresponding to instances of sh:declare individuals available in the sh:prefixes ontology.
#    See: https://www.w3.org/TR/sparql11-query/#rPrologue 
# 2) the SPARQL query statement: select or ask. 
#    See: https://www.w3.org/TR/sparql11-query/#rSelectQuery
#    See: https://www.w3.org/TR/sparql11-query/#rAskQuery

# The following defines `car-shapes` as the ontology for sh:declare prefixes to be added to each SPARQL query.
car-shapes:
  a owl:Ontology ;
  owl:imports sh: ;
  sh:declare [
    sh:prefix "car" ;
    sh:namespace "http://ontology.rys.one/project/vocabulary/car#"^^xsd:anyURI ;
  ] ;
  sh:declare [
    sh:prefix "rdf" ;
    sh:namespace "http://www.w3.org/1999/02/22-rdf-syntax-ns#"^^xsd:anyURI ;
  ] .
  
# -------------------------------------------------------------
# Constraint 1 (SHACL-CORE): A car:Car must car:hasModel some (1) car:Model.
# -------------------------------------------------------------

car-shapes:Car_HasModel_some_Model_shacl
  a sh:NodeShape ;
  sh:targetClass car:Car ;
  sh:property [
    sh:path car:hasModel ;
    sh:class car:Model ;
    #sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
	sh:maxCount 1 ;
  ] ;
  sh:closed false .
  
car-shapes:Wheel_has_mass_shacl
  a sh:NodeShape ;
  sh:targetClass car:Wheel ;
  sh:property [
    sh:path car:hasMass ;
    #sh:class xsd:decimal ;
    #sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
	sh:maxCount 1 ;
  ] ;
  sh:closed false .
  
# -------------------------------------------------------------
# Constraint 1 (SHACL-SPARQL): A car:Car must car:hasModel some car:Model.
# -------------------------------------------------------------

car-shapes:Car_HasModel_some_Model_sparql
  a sh:NodeShape ;
  sh:targetClass car:Car ;
  sh:sparql [
    a sh:SPARQLConstraint ;
    sh:message "{$this}, a car:Car, must have a model of some car:Model." ;
    sh:prefixes car-shapes: ;
    sh:select """
      SELECT DISTINCT $this ?model
      WHERE {
        $this rdf:type car:Car .
        FILTER NOT EXISTS { 
          $this car:hasModel ?model .
          ?model rdf:type car:Model .
        }
      }
      """ ;
  ] ;
  sh:closed false .
  
  
# -------------------------------------------------------------
# Constraint 2 (SHACL-CORE): A car:Wheel must be car:isWheelOnCar exactly 1 car:Car.
# -------------------------------------------------------------

#car-shapes:Wheel_is_wheel_on_car_by_one_Car_shacl
#  a sh:NodeShape ;
#  sh:targetClass car:Wheel ;
#  sh:property [
#    sh:path car:isWheelOnCar ;
#    sh:class car:Car ;
#    sh:nodeKind sh:IRI ;
#    sh:minCount 1 ;
#    sh:maxCount 1 ;
#  ] ;
#  sh:closed false .


car-shapes:Test_shacl
  a sh:NodeShape ;
  sh:targetClass car:Car ;
  sh:property [
    sh:path car:hasModel ;
    sh:class car:Model ;
    #sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
	sh:maxCount 1 ;
  ] ;
  sh:closed false .
  

# -------------------------------------------------------------
# Constraint 2 (SHACL-SPARQL): A car:Wheel must be car:isWheelOnCar exactly 1 car:Car.
# -------------------------------------------------------------
  
#car-shapes:Wheel_is_wheel_on_car_by_one_Car_sparql
#  a sh:NodeShape ;
#  sh:targetClass car:Wheel ;
#  sh:sparql [
#    a sh:SPARQLConstraint ;
#    sh:message "{$this}, an car:Wheel, must be on exactly 1 car:Car instead of {?c}." ;
#    sh:prefixes car-shapes: ;
#    sh:select """
#      SELECT DISTINCT $this (COUNT(?woc) AS ?c)
#      WHERE {
#        $this rdf:type car:Wheel .
#        OPTIONAL {
#          $this car:isWheelOnCar ?woc .
#        }
#      }
#      GROUP BY $this
#      HAVING (COUNT(?woc) != 1)
#      """ ;
#  ] ;
#  sh:closed false .

# -------------------------------------------------------------
# Constraint 3 (SHACL-SPARQL): A car:Car must have n car:Wheel with a total mass equal to 4.0.
# -------------------------------------------------------------

car-shapes:Wheels_have_4.0_mass 
  a sh:NodeShape ;
  sh:targetClass car:Car ;
  sh:property [
    sh:path ( car:hasWheel car:hasMass ) ;
    sh:severity sh:Warning ;
    sh:sparql [
      a sh:SPARQLConstraint ;
      sh:message "Total car:Wheel mass should sum to 4.0." ;
      sh:prefixes car: ;
      sh:select """
        SELECT $this (sum(?mass) as ?value) {
          $this $PATH ?mass .
        }
        GROUP BY $this
        HAVING (sum(?mass) != 4.0)
        """
    ]
  ] ;
  sh:closed false .