#!/usr/bin/env ruby

$LOAD_PATH.unshift '../../lib/' # because we're testing dev version

# Exploring a pastiche of the Cwm RDF API
# taking http://www.w3.org/2000/10/swap/pim/toIcal.py as basis
# $Id: pastiche.rb,v 1.1 2003/04/21 02:48:01 danbri Exp $ 
# Dan Brickley <danbri@w3.org>
#
#      Current Cwm-like RDF API (llyn.py/thing.py) functionality:
#
#      def each(st)       # iterator for simple statement matcher
#      def statementsMatching(st) # ditto but returns immutable statements
#      def add(st)	  # works for RDFGraph, no impl yet in SimpleSQLGraph 
#      def any(st)        # returns the missing bit for a blanked out node
#      def the(st)        # ditto but complains if multiple answers
#      def Namespace(n)   # haven't sorted out interning, scopes etc though
# 
# TODO: currently all supporting code is here w/ the tests, maybe -> ../lib/ ?
#
# see http://www.w3.org/2000/10/swap/llyn.py and thing.py
# 
# note that the SQL/stored tests require a local DBI rdfstore, see tiny.rb
#
# The Cwm-ish api explored below echoes this from Cwm:
#  for cal in sts.each(pred = RDF.type, obj = ICAL.Vcalendar):
#  still TODO:
#  if sts.statementsMatching(RDF.type, comp, ICAL.Vevent):
#  sts.any(subj, ICAL.sym(pn)) -> ???, returns at most one?
#
# NOTE: we have *lots* of issues, eg. which things should be class vs
# instance vs factoried, interned, etc etc. Ignoring all that for now...
#
# we annotate the SimpleSQLGraph class with mixin
# so all our SimpleSQLGraph instances have Cwm-ish behaviours.
# also instances (see below). See further below for health warning and 
# ideas for a different approach. 
#
###################################################################

#require 'test/unit'     # unit testing


# tests see tests/scutter/sqlgraph.rb amongst others

require 'basicrdf'      # main RubyRdf impl and api
require 'squish' 	# for SimpleSQLGraph impl

module RDF4R
  module Pastiche
    module SWAP
      class TheWorld
        attr_accessor :namespaces
        @@ns=[]
        def initialize
          @@ns=[]
        end
        def TheWorld.namespaces
          return @@ns
        end

      end
      
      # load some RDF/XML from URI, returning a Cwm Store
      # load() isn't a property of a Cwm instance.
      # ...though we allow it there as a static too.
      # how about as an instance method? would that be an add() variant?
      def load(uri)
        return Formula.new(Loader.get_rdf_from_uri(uri,uri))
      end

      def Namespace(n)
          # puts "NS:"+@namespaces.inspect
        TheWorld.namespaces.push n
        return n.to_s # what is a 'Namespace' in cwm? string?
        # all manner of interning would normally happen here
        # however we've not decided how to deal with that yet.
        # Oh, and it should be a class method.
      end

      # A Formula is a set of triples.
      class Formula                      # bad name? Llyn? Formula? Cwm?
        
        # This is functionality we attach to RDFGraph and SimpleSQLGraph
        # the @kb field works for locally held data, not encapsulated 
        # services like SQL-backed stores, SOAP etc. The each() iterator
        # allows data to be accessed regardless. Need to think about 
        # cleaner design for allowing different types of stores, and the
        # classes they fall into.
        
        attr_accessor :namespaces
        
        def initialize(graph)
          #puts "Cwm pastiche initializing with graph "+graph.to_s
          @kb=graph
        end
        
        def to_s
          return @kb.toNtriples # for now...
        end

        def add(st)
          g=@kb.tell(Statement.new(st['subj'], st['pred'], st['obj'])) if st.class==Hash
          raise("Unknown invocation of add()") # can we add graphs?
        end      
        

        # return the blanked out bit that matches
        def any(st)
          one(st,false)
        end

        def the(st)
          return one(st,true)
        end

        # return the blanked out bit that matches
        def one(st,the_mode=false)
          results=[]
          self.each(st) do |r|
          results.push r
          end
          return nil if results==nil
          if ((results.size>1) & the_mode==true)
            raise("There should only be one match. got: #{results.inspect}") 
          end
          return results[0].subject.to_s if st['subj']==nil
          return results[0].predicate.to_s if st['pred']==nil
          return results[0].object.to_s if st['obj']==nil
          raise ("You must give one wildcard")
        end
        
        # we use 'yield', unlike Cwm (though there is an @@todo in llyn.py)
        def each(st)
          #puts "Query was: "+st.inspect+" type: "+st.class.to_s
          g=@kb.ask st if st.class==Statement
          g=@kb.ask(Statement.new(st['subj'], st['pred'], st['obj'])) if st.class==Hash
          g=Graph.new([]) if g==nil
          g.statements.each do |s|
            yield s
          end
        end

        # like each() but READ ONLY results
        # todo: test this!
        def statementsMatching(st)
          g=@kb.ask st if st.class==Statement
          g=@kb.ask(Statement.new(st['subj'], st['pred'], st['obj'])) if st.class==Hash
          g=Graph.new([]) if g==nil
          g.statements.each do |s|
            s=s.clone.freeze # copy and freeze each statement (deep? Nodes?)
            yield s
          end
        end
      end
    end
  end
end

###########################################################################
# Experimental class annotation; see ../../lib/squish.rb for core class def.
#
class SimpleSQLGraph
  require 'dbi'
  include RDF4R::Pastiche::SWAP 
  def kb #needed for Cwm-ish stuff only
    #puts "Help, someone wants to dump the KB. (SimpleSQLGraph.kb())"
    return kb_from_sql
  end
  def kb_from_sql
    dump = Formula.new self.ask(Statement.new(nil,nil,nil))
    #puts "Got dump: #{dump.type}"
    return dump
  end
  #  TODO: implement tell() in this class via store_graph()
end

# same again re ../../lib/basicrdf.rb
#
class Graph
  include RDF4R::Pastiche::SWAP
end



## NOTES:
#
#
# Should Node subclass String (see timbl/eikeon discussion?)
#
# todo: puts "My local db is: "+local.kb.kb_from_sql 
# OK lots of issues here. Mixings / multi-inheritance confusion w 'kb', 
# which is also an attribute of the SimpleSQL class.
# local.kb calls SimpleSQLGraph's kb method

# we basically are splashing about in an is-a vs has-a mess.
# sometimes RDF4R::Pastiche::Cwm has as instances RDFGraph,
# SimpleSQLGraph etc., other times it sorta wraps them, via the 'kb' field.
# Maybe drop the inheritance thing and do it all with wrapper, less 
# scope for confusion?

