module Zoy
	module Rdf
        # the Namespace class,
        class Ns
            attr_reader  :attributes, :sn, :fn, :dn
            def initialize full_name, symbolic_name, uri
                @sn = symbolic_name
                @dn = uri
                @fn = full_name
            
                @attributes = Array.new()
            end
            def << attribute
                @attributes << attribute
                attribute.ns = self
            end
        end
        class Attribute 
            attr_accessor :ns
            attr_reader :rn
            attr_reader :dn
            def initialize ruby_name, rdf_name, full_name 
                if ruby_name==nil || rdf_name==nil || full_name==nil
                    raise "Invalid parameter #{ruby_name} #{rdf_name} #{full_name}" 
                end
                @rn = ruby_name
                @dn = rdf_name
                @fn = full_name
            end
            def allowed_value?(value)
                return true
            end
                
        end
        class RadioAttribute < Attribute
            attr_accessor :ns
            attr_reader :allowed_values
            attr_reader :full_name

            def initialize ruby_name, rdf_name, full_name, values
                super( ruby_name, rdf_name, full_name )
            
                @allowed_values = values.keys
                @full_name = values
            end
            def allowed_value?(value)
                if @full_name.has_key?(value)
                    return true
                else
                    return false
                end
            end
        end
        class BooleanAttribute < Attribute
             attr_accessor :ns
            def initialize ruby_name, rdf_name, full_name
                 super( ruby_name, rdf_name, full_name )
            end
            def allowed_value?(value)
                if value == true || value == false
                    return true
                else
                    return false
                end
            end
        end
        DCNS = Ns.new("Dublin Core Namespace", "dc", "http://www.w3.org/2000/PhotoRDF/dc-1-0#" )
        DCNS << Attribute.new('title', 'title', 'Title')
        DCNS << Attribute.new('author', 'author', 'Author')
        DCNS << Attribute.new('creator', 'creator', 'Creator')
        DCNS << Attribute.new('description', 'description', 'Description')
        DCNS << Attribute.new('title', 'title', 'Title')
        DCNS << RadioAttribute.new('subject', 'subject', 'Subject', {
                                'portrait'          => 'Portrait',
                                'group portrait'    => 'Group portrait',
                                'landscape'         => 'Landscape',
                                'baby'              => 'Baby',
                                'architecture'      => 'Architecture',
                                'wedding'           => 'Wedding',
                                'macro'             => 'Macro',
                                'graphic'           => 'Graphic',
                                'panorama'          => 'Panorama',
                                'animal'            => 'Animal'
                            } )
        DCNS << Attribute.new( 'publisher' , 'publisher' ,'Publisher')
        DCNS << Attribute.new( 'contributor' , 'contributor' ,'Contributor')
        DCNS << Attribute.new( 'date' , 'date' ,'Date digitized')
        DCNS << Attribute.new( 'type' , 'type' ,'Type')
        DCNS << Attribute.new( 'format' , 'format' ,'Format')
        DCNS << Attribute.new( 'identifier' , 'identifier' ,'Identifier')
        DCNS << Attribute.new( 'coverage' , 'coverage' ,'Coverage')
        DCNS << Attribute.new( 'rights' , 'rights' ,'Rights')
        DCNS << Attribute.new( 'relation' , 'relation' ,'Relation')
       
        TENS = Ns.new( "Technical Namespace", "tc", "http://www.w3.org/2000/PhotoRDF/technical-1-0#" )
        TENS << Attribute.new( 'camera', 'camera', 'Camera' )
        TENS << Attribute.new( 'lens', 'lens', 'Lens')
        TENS << Attribute.new( 'film', 'film', 'Film')
        TENS << Attribute.new( 'exposure_time', 'exposure-time', 'Exposure Time')
        TENS << Attribute.new( 'devel_date', 'devel-date', 'Development Date')
       
        RDFPICLANG = Ns.new( "Rdf Picture Langage Namespace", "pl", "http://sophia.inria.fr/~enerbonn/rdfpiclang#" )
        RDFPICLANG << Attribute.new( 'xmllang', 'xmllang', 'Langage' )

        NS = [DCNS, TENS, RDFPICLANG]
      
        RESOLVER = Hash.new()
        
        NS.each do |namespace|
          namespace.attributes.each do |attribute|
            RESOLVER[namespace.dn+attribute.dn] = attribute
          end
        end
    end
end


module Zoy
    module Rdf
        module RDFPic
            def has_rdf?
                return false if comments == nil
                return true
            end
        end

        begin 
            require 'rubygems'
            require_gem 'rubyrdf'
        rescue LoadError=>e
        end
        require 'basicrdf'
        require 'RDF4R/Model/Standard'

        class InvalidValueException < Exception
        end

        class InvalidURIException < Exception
        end
        

        # this is a readable and writable Image
        class WritableImage
            def initialize()
                @uri = Hash.new()
            end
            def set_value(uri, value)
                if RESOLVER.has_key?(uri)
                    # check that this uri allow this value
                    if RESOLVER[uri].allowed_value?(value)
                        @uri[uri] = value
                        return value
                    else
                        raise InvalidValueException.new("#{uri} does not accept value: #{value}")
                    end
                else
                    raise InvalidURIException.new("URI \"#{uri}\" is not supported.")
                end
            end
        end
        class ReadableImageString
            VAR_PREFIX = "rv_"

            attr_reader :data, :statements
            def initialize string
                require 'tempfile'
                rdf_tempfile = Tempfile.new('rdf_file')
                rdf_tempfile.puts(string)
                rdf_tempfile.close
                rdf_tempfile_name = rdf_tempfile.path.gsub(/\\/,'/')
                content = Loader.get_rdf rdf_tempfile_name
                # content is a RDF4R::Graph
                @data = content
                @valid = true
                @statements = Array.new

                # if the content size is 0 then
                # this is supposed to be an invalid rdf element.
                if content == nil || content.size == 0
                    @valid = false
                    @data = string
                end
                return if @valid == false
                
                # parses the statements
                @data.statements.each do |statement|
                  @statements << rdf_sentence = RDFSentence.new(statement.predicate, statement.object)
                    # create the local variable and accessor for this variable...
                    
                    instance_eval( 
                    %Q<
                        @#{VAR_PREFIX+rdf_sentence.method_name} = rdf_sentence
                        def #{VAR_PREFIX+rdf_sentence.method_name}	
                            return @#{VAR_PREFIX+rdf_sentence.method_name}.object
                        end
                    >)
                end
            end
            def to_s
                return "Invalid RDF list" if !@valid
                s_out = "RDF List:\n"
                allowed_methods.each do |read_method|
                    sentence = self.instance_eval("@"+read_method)
                    s_out += "#{sentence.predicate} = #{sentence.object.inspect}\n"
                end
                #@data.statements.each do |statement|
                    # statement is a RDF4R::Model::Statement object
                    #    s_out = s_out + statement.to_s	
                #   end
                return s_out
            end
            def allowed_methods
                allowed = []
                self.methods.grep(/^#{VAR_PREFIX}.*[^=]$/).each do |method|
                    allowed << method
                end
                return allowed
            end
            def valid?
              @valid
            end
        end

        class RDFSentence
            include Enumerable
            
            attr_reader :ns, :attribute, :object, :predicate
            attr_writer :object
            def initialize predicate, object
                @predicate = predicate.to_s
                @object = object
                data  = @predicate.split(/#/)
                @ns = data[0]
                @attribute = data[1] 	
            end
            def <=>(other)
                nsc = @ns<=>other.ns
                atc = @attribute<=>other.attribute
                oc = @object.to_s<=>other.object.to_s
                return nsc #nsc!=0?nsc:(atc!=0?atc:oc)
            end
            def to_s
                if RDF_PREDICATES.has_key?(@ns) && RDF_PREDICATES[@ns].has_key?(@attribute)
                    s_out = "Statement #{RDF_PREDICATES[@ns][@attribute][1]}: #{@object}\n"
                else
                    s_out = "Statement #{@ns} #{@attribute}: #{@object}\n"
                end
                return s_out		
            end
            def method_name
                if RESOLVER.has_key?(@predicate)
                    s_out = "#{RESOLVER[@predicate].rn}"
                else
                    raise "No corresponding method name for #{@predicate}"
                end
            end
        end		
    end
end


if __FILE__ == $0

    require 'test/unit'


RDF_SAMPLE = <<EOF
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ns1="http://www.w3.org/2000/PhotoRDF/dc-1-0#" xmlns:ns2="http://www.w3.org/2000/PhotoRDF/technical-1-0#">
  <rdf:Description>
    <ns1:title>2004</ns1:title>
    <ns1:date>2004:02:10 00:54:28</ns1:date>
    <ns1:subject></ns1:subject>
    <ns1:description>Regard inquisiteur.</ns1:description>
    <ns2:lens>80/10</ns2:lens>
    <ns2:camera>E885</ns2:camera>
    <ns2:exposure-time>10/36</ns2:exposure-time>
  </rdf:Description>
</rdf:RDF>
EOF

    # interestingly the RDF parser behaves not so badly when parsing an empty stream
BROKEN_RDF_SAMPLE = <<EOF
ezdkzed
fjzefljf
zfjzefj
EOF



    class RDFPicEmpty_Test < Test::Unit::TestCase
        include Zoy::Rdf::RDFPic
        
        def test_has_rdf
            assert_nil(comments)
            assert_equal(false, has_rdf?)
        end
        
        
        def comments
            return nil
        end
    end

    class ReadableImageString_Test < Test::Unit::TestCase
        include Zoy::Rdf::RDFPic
        include Zoy::Rdf
        
        def test_default_constructor
            a = ReadableImageString.new(RDF_SAMPLE)
            assert_not_nil(a)
            assert_equal(a.valid, true)
            assert_not_nil(a.to_s)        
        end
        def test_broken_sample	
            a = ReadableImageString.new(BROKEN_RDF_SAMPLE)
            puts a.valid
            assert_equal(a.valid, false)
            
        end
    end

    class WritableImage_Test < Test::Unit::TestCase
        include Zoy::Rdf

        def test_default_constructor
            a = WritableImage.new()
            assert_not_nil(a)
        end
        def test_write
            a = WritableImage.new()
            assert_equal(a.set_value("http://www.w3.org/2000/PhotoRDF/dc-1-0#title","yoyo"),"yoyo" )
            assert_equal(a.set_value("http://www.w3.org/2000/PhotoRDF/dc-1-0#subject","portrait"),"portrait" )
            assert_raise(InvalidValueException) do || 
                a.set_value("http://www.w3.org/2000/PhotoRDF/dc-1-0#subject","fooo")
            end
            assert_raise(InvalidURIException) do ||
                a.set_value("http://www.w3.org/2000/PhotoRDF/dc-1-0#brokenuri",12)
            end
        end
    end
end
# Test::Unit::UI::GTK2::TestRunner.run(RDFPicEmpty_Test)
