require 'rtm'
# Yep, this is the easter egg I told of on the website.
# See the source code at the very end of this file for an explanation.
# Pleace note, this is not beta code, not even alpha - it's just an experiment.

module ActiveTopicMaps
  class Base
    def self.inherited(klass)
      klass.send(:include, ActiveTopicMaps::Topic)
    end
  end

  module Topic
    def self.included(klass)
      klass.extend(ClassMethods)
    end
    def initialize(topic)
      @topic = topic
    end
    def topic;@topic;end

    module ClassMethods
      def acts_as_topic
        self.class_eval <<-EOS
          def method_missing(name, *args)
            @topic.send(name, *args)
          end
          
          alias atm_old_respond_to? respond_to?
          def respond_to?(*args)
            atm_old_respond_to?(*args) || @topic.respond_to?(*args)
          end

          alias old_methods methods
          def methods
            @methods ||= (old_methods + @topic.methods).uniq
          end
        EOS
      end
      
#      def acts_smart
#        self.class_eval <<-EOS
#          alias atm_old_method_missing method_missing
#          def method_missing(name, *args)
#            if @topic.respond_to?(name)
#              @topic.send(name, *args)
#            else
#              begin
#                if name =~ /=$/
#                  smart_setter(name, *args)
#                else
#                  smart_getter(name, *args)
#                end
#              rescue
#                atm_old_method_missing(name, *args)
#              end
#            end
#          end
#
#          def smart_getter(name, *args)
#            val = instance_variable_set(("smart_prop_" + name.to_s).to_sym, smart_prop_name)
#            prefix = ""
#            smart_prop_name ||= (iname = @topic["#{prefix}#{name}"].first) ? iname.value : nil
#            instance_variable_set(name, smart_prop_name)
#          end
#
#          def smart_setter(name, *args)
#
#          end
#          
#          alias atm_old_respond_to? respond_to?
#          def respond_to?(*args)
#            atm_old_respond_to?(*args) || @topic.respond_to?(*args)
#          end
#          EOS
#      end

      def name(name, type=nil)
        type ||= name
        prop_getter(name, type, "-")
        prop_setter(name, type, "-")
      end
      
      def names(name, type=nil)
        type ||= name.singularize
        prop_getters(name, type, "-")
        prop_adderremover(name, type, "-")
      end
      
      def occurrence(name, type=nil)
        type ||= name
        prop_getter(name, type)
        prop_setter(name, type)
      end
      
      def occurrences(name, type=nil)
        type ||= name.singularize
        prop_getters(name, type)
        prop_adderremover(name, type)
      end
      
      def prop_getter(name, type, prefix="")
        self.class_eval <<-EOS
        def #{name}
          @#{name} ||= (iname = @topic["#{prefix}#{type}"].first) ? iname.value : nil
        end
        EOS
      end

      def prop_getters(name, type, prefix="")
        self.class_eval <<-EOS
        def #{name}
          @#{name} ||= @topic["#{prefix}#{type}"].map{|n| n.value}
        end
        EOS
      end
      
      def prop_setter(name, type, prefix="")
        self.class_eval <<-EOS
        def #{name}=(name)
          old = @topic["#{prefix}#{type}"].first
          @#{name} = if old
            old.value = name
          else
            @topic["#{prefix}#{type}"] = name
          end
        end
        EOS
      end
      
      def prop_adderremover(name, type, prefix="")
        self.class_eval <<-EOS
        def add_#{name.singularize}(name)
          @topic["#{prefix}#{type}"] = name
        end
        def remove_#{name.singularize}(name)
          @topic["#{prefix}#{type}"].each {|item| item.remove if item.value == name}
        end
        EOS
      end
            
      def has_many(name, rt1, at, rt2, klass)
          eval(<<-EOS)
          class RTM::AR::Topic
          index_property_set :#{name}, :type => :Topic, :rule => {
            :transitive => true,
            :role_type => "#{rt1}",
            :association_type => "#{at}",
            :association_arity => 2,
            :other_role_type => "#{rt2}",
            :add => "#{name.to_s.singularize}",
          }
          end
          EOS
          self.class_eval <<-EOS
          def #{name}
            @topic.#{name}.map{|o| #{klass}.new(o)}
          end
          def add_#{name.to_s.singularize}(o)
            @topic.add_#{name.to_s.singularize}(o.topic)
          end
          def remove_#{name.to_s.singularize}(o)
            @topic.remove_#{name.to_s.singularize}(o.topic)
          end
          EOS
      end
      
      def topic_map(base_uri_or_tm)
        @tm = base_uri_or_tm.is_a?(RTM::TopicMap) ? base_uri_or_tm : RTM.create(base_uri_or_tm)
      end
      
      def create(ref)
        t = @tm.get!(ref)
        if @psi
          @ty ||= @tm.get!(@psi)
          t.add_type @ty unless t.types.include? @ty
        end
        self.new(t)
      end
      alias get! create
      
      def find(ref)
        t = @tm.get(ref)
        self.new(t) if t
      end
      alias get find
      
      def find_by_type(ref=nil)
        ref ||= @psi
        ty = @tm.get(ref)
        ty.instances.map {|t| self.new(t)}
      end
      
      def psi(ref)
        @psi = ref
      end
    end
  end
end


#require 'activetopicmaps'
#RTM.connect
#
#class Person < ActiveTopicMaps::Base
#  topic_map "urn:/base"
#  psi "person"
#
#  name :name
#  occurrence :age
#  occurrence :shoesize, "http://rtm.rubyforge.org/psi/shoesize" # this tells ActiveTM to use this as occurrence type
#
#  def to_s
#    a = " (#{age})" if age
#    "#{name}#{a}"
#  end
#end
#
#class Country
#  include ActiveTopicMaps::Topic
#
#  topic_map "urn:/base"
#  psi "country"
#
#  acts_as_topic
#
#  name :name
#  occurrence :population
#  occurrence :size
#
#  has_many :inhabitants, "country", "country-inhabitant", "inhabitant", :Person
#
#  def to_s
#    "There is a country called #{name} with #{population} inhabitants and a size of #{size} km^2.\n" +
#      "These are some of the inhabitants: #{inhabitants.map{|i| i.to_s}.join(", ")}."
#  end
#end
#
## create a new object with a (new?) topic
#p = Person.create("max")
#
## set name, age, shoesize
#p.name = "Max Mustermann"
#p.age = 30
#p.shoesize = 44 # european ;)
#
## print out the name
#puts p.name
#
## create a new object, using a topic (might be queried before, ... you know)
#h = Person.new(RTM[0].get!("hans"))
#
## set name
#h.name = "Hans Maier"
## set another name (overwrites the old one! this is different from topic["-name"] !!!)
#h.name = "Hans Meyer"
#
## don't know what that does, maybe you?
#puts h.name
#
## Norway is the item identifier, here
#no=Country.create("Norway")
#
#no.name="Norway"
#no.population = "4.743.000"
#no.size = "385.199"
#
## use real person objects here
#no.add_inhabitant h
#no.add_inhabitant p
#
## use this nice to_s method we provided above
#puts no.to_s
#
## find hans
#puts "Trying to find Hans: #{Person.find("hans")}"
## won't find otto
#puts "Trying to find Otto: #{Person.find("otto")}"