old_verbose=$VERBOSE
$VERBOSE=false
require 'active_record'
$VERBOSE=old_verbose

module RTM::AR
  # The Active Record TMDM backend
  module TMDM
    # Moves all fields in a collection to another owner (for classes using belongs_to in AR::Base class)
    module Movable
      # Takes a list of [field,from, to] arguments.
      # RTM::AR::TMDM::ItemIdentifier.move_all(
      #      ["topic_map_construct_id", other.__getobj__.id, self.__getobj__.id],
      #      ["topic_map_construct_type", other.__getobj__.class.name, self.__getobj__.class.name]
      #    )
      # or
      # RTM::AR::TMDM::Variant.move_all(["topic_name_id", other.__getobj__.id, self.__getobj__.id])

      # 
      def move_all(*args)
        raise "All parameters must have a size of 3: [field_name, from, to]" unless args.all?{|a| a.size == 3}
        set_part = ""
        where_part = ""
        args.each do |field, from, to|
          set_part += ", " unless set_part.empty?
          where_part += " and " unless where_part.empty?
          from = "'#{from}'" unless from.is_a? Numeric
          to = "'#{to}'" unless to.is_a? Numeric
          where_part += "#{field} = #{from}"
          set_part += "#{field} = #{to}"
        end
        #puts "setpart: #{set_part.inspect}\nwherepart: #{where_part.inspect}"
        res = self.update_all(set_part, where_part)
        #puts "result: #{res.inspect}"
      end
    end

    class TopicMapConstruct < ActiveRecord::Base
      self.store_full_sti_class = false 
      extend Movable
      class << self
        def abstract_class?
          self == TopicMapConstruct
        end
        def alias_ar_relation(new,old)
          alias_method new, old
          alias_method "#{new}=", "#{old}="
        end
        alias :alias_belongs_to  :alias_ar_relation
        alias :alias_has_many :alias_ar_relation
        def belongs_to_with_alias(name, synonym)
          belongs_to name
          alias_belongs_to synonym, name
        end
        def has_many_with_alias(name, synonym, *args)
          has_many name, *args
          alias_has_many synonym, name
        end
        def belongs_to_parent(parent)
          belongs_to parent
          alias_belongs_to :parent, parent
        end
        def this_is_a_typed_object
          belongs_to :ttype, :class_name => "Topic", :foreign_key => :ttype_id
          module_eval(<<-EOS, "(__RTM_AR_TMDM_TYPED_OBJECT__)", 1)
          def type=(ptype)
            self.ttype=ptype
          end
          def type
            self.ttype
          end
          EOS
        end
      end
      has_many :item_identifiers, :as => :topic_map_construct, :dependent => :destroy
      def topic_map
        parent.topic_map
      end
      def move_to(new_parent)
        self.parent = new_parent
      end

    end
    class Reifiable < TopicMapConstruct
      def self.abstract_class?
        self == Reifiable
      end
      has_one :reifier, :class_name => "Topic", :as => :reified
    end
    class ScopedObject < Reifiable
      def self.abstract_class?
        self == ScopedObject
      end
      has_many :scoped_objects_topics, :as => :scoped_object, :dependent => :destroy
      has_many :scope, :through => :scoped_objects_topics, :source => :topic
#      def scope
#        self.scoped_objects_topics.map { |sot| sot.topic }
#      end
#      def add_scoping_topic(topic) # this is not ruby-ish but at least it works, would need a complete association proxy instead...
#        sot = ScopedObjectsTopic.create :topic => topic, :scoped_object => self
#        self
#        #      sot = ScopedObjectsTopic.new :topic => topic, :scoped_object => self
#        #      self.scoped_objects_topics << sot
#        #      self
#      end
#      def remove_scoping_topic(topic) # this is not ruby-ish but at least it works, would need a complete association proxy instead...
#        self.scoped_objects_topics.select { |sot| sot.topic == topic }.destroy
#        self
#      end
    end
    class ScopedObjectsTopic < ActiveRecord::Base
      self.store_full_sti_class = false 
      extend Movable
      belongs_to :topic
      belongs_to :scoped_object, :polymorphic => true
      belongs_to :association, :class_name => 'RTM::AR::TMDM::Association', :foreign_key => "scoped_object_id"
      belongs_to :topic_name, :class_name => 'RTM::AR::TMDM::TopicName', :foreign_key => "scoped_object_id"
      belongs_to :variant, :class_name => 'RTM::AR::TMDM::Variant', :foreign_key => "scoped_object_id"
      belongs_to :occurrence, :class_name => 'RTM::AR::TMDM::Occurrence', :foreign_key => "scoped_object_id"
    end
  
    class Locator < ActiveRecord::Base
      self.store_full_sti_class = false 
      extend Movable
      class << self
        def abstract_class?
          self == Locator
        end
      end
      belongs_to :topic_map
    end

    class ItemIdentifier < Locator
      belongs_to :topic_map_construct, :polymorphic => true
      def to_s
        self.reference
      end
      def move_to(new_tmc)
        #puts "\n\nmoving\n\t#{self.inspect}\n\nto\n\t#{new_tmc.inspect}"
        #self.update_all "topic_map_construct_id = #{new_tmc.id}, topic_map_construct_type = '#{new_tmc.class.name}'", 
        #puts "changing id"
        #puts self.topic_map_construct_id = new_tmc.id
        #puts "changing type"
        #puts self.topic_map_construct_type = new_tmc.class.name
        self.topic_map_construct = new_tmc
        #puts "saving"
        self.save
        #
        # das geht!!!
        #self.class.update( self.id, "topic_map_construct_id" => new_tmc.id)
      end
    end
    class SubjectLocator < Locator
      belongs_to :topic
      def to_s
        self.reference
      end
    end
    class SubjectIdentifier < Locator
      belongs_to :topic
      def to_s
        self.reference
      end
    end
    # http://www.isotopicmaps.org/sam/sam-model/#d0e736
    class Topic < TopicMapConstruct
      belongs_to :reified, :polymorphic => true
      belongs_to_parent :topic_map
      has_many :subject_locators, :dependent => :destroy
      has_many :subject_identifiers, :dependent => :destroy
      has_many :occurrences, :dependent => :destroy
      has_many_with_alias :topic_names, :names, :dependent => :destroy
      has_many_with_alias :association_roles, :roles, :include => :association
      has_many :associations, :through => :association_roles
      #has_many :played_types, :through => :association_roles, :source => :type
      
      has_many :associations_typed, :class_name => "Association", :foreign_key => :ttype_id
      has_many :association_roles_typed, :class_name => "AssociationRole", :foreign_key => :ttype_id
      has_many :topic_names_typed, :class_name => "TopicName", :foreign_key => :ttype_id
      has_many :occurrences_typed, :class_name => "Occurrence", :foreign_key => :ttype_id
    
      has_many :scoped_objects_topics
      has_many :scoped_associations, :through => :scoped_objects_topics, :source => :association,
        :conditions => "scoped_objects_topics.scoped_object_type = 'RTM::AR::TMDM::Association'"
      has_many :scoped_topic_names, :through => :scoped_objects_topics, :source => :topic_name,
        :conditions => "scoped_objects_topics.scoped_object_type = 'RTM::AR::TMDM::TopicName'"
      has_many :scoped_variants, :through => :scoped_objects_topics, :source => :variant,
        :conditions => "scoped_objects_topics.scoped_object_type = 'RTM::AR::TMDM::Variant'"
      has_many :scoped_occurrences, :through => :scoped_objects_topics, :source => :occurrence,
        :conditions => "scoped_objects_topics.scoped_object_type = 'RTM::AR::TMDM::Occurrence'"

#      def scoped_objects
#        self.scoped_objects_topics.collect { |a| a.scoped_object } # temporary hack polymorphic has_many_through
#      end
      def scoped_objects
        scoped_associations + scoped_topic_names + scoped_variants + scoped_occurrences
      end

    end

    class AssociationRole < Reifiable
      belongs_to_parent :association
      belongs_to_with_alias :topic, :player
      this_is_a_typed_object
    end

    class TopicName < ScopedObject
      belongs_to_parent :topic
      this_is_a_typed_object
      has_many :variants, :dependent => :destroy
    end

    class Association < ScopedObject
      belongs_to_parent :topic_map
      this_is_a_typed_object
      has_many_with_alias :association_roles, :roles, :dependent => :destroy
      def create_role(rplayer, ttype)
        AssociationRole.create :parent => self, :player => rplayer, :ttype => ttype
      end
      alias :create_association_role :create_role
      has_many :role_players, :through => :association_roles, :source => :topic
      has_many :role_types, :through => :association_roles, :source => :ttype
    end
    
    class AssociationCache < TopicMapConstruct
      set_table_name "associations_cache"
      belongs_to_parent :topic_map
      this_is_a_typed_object
      has_many_with_alias :association_roles, :roles
      belongs_to :role1, :class_name => "RTM::AR::TMDM::AssociationRole", :foreign_key => "role1_id"
      belongs_to :player1, :class_name => "RTM::AR::TMDM::Topic", :foreign_key => "player1_id"
      belongs_to :type1, :class_name => "RTM::AR::TMDM::Topic", :foreign_key => "type1_id"
      belongs_to :role2, :class_name => "RTM::AR::TMDM::AssociationRole", :foreign_key => "role2_id"
      belongs_to :player2, :class_name => "RTM::AR::TMDM::Topic", :foreign_key => "player2_id"
      belongs_to :type2, :class_name => "RTM::AR::TMDM::Topic", :foreign_key => "type2_id"
      # rolecount -> rcnt
    end

    class Occurrence < ScopedObject
      belongs_to_parent :topic
      this_is_a_typed_object
    end

    class Variant < ScopedObject
      belongs_to_parent :topic_name
    end

    class TopicMap < Reifiable
      # real properties
      has_many :topics, :dependent => :destroy, :include => [:subject_identifiers, :subject_locators]
      has_many :associations, :dependent => :destroy, :include => :association_roles
      
      # access to subproperties
      has_many :locators, :class_name => "ItemIdentifier", :dependent => :destroy
      has_many :subject_locators, :dependent => :destroy
      has_many :subject_identifiers, :dependent => :destroy

      has_many :topic_names, :through => :topics
      alias :names :topic_names
      has_many :occurrences, :through => :topics
      has_many :association_roles, :through => :associations
      alias :roles :association_roles
      
      %w[association association_role topic_name occurrence].each do |m|
        module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET__)", 1)
          def #{m}_types
            topics.select{|t| t.#{m}s_typed.size > 0}
          end
        EOS
      end
      
      #typing
#      has_many :association_types, :class_name => "Association", :foreign_key => :ttype_id
#      has_many :association_role_types, :class_name => "AssociationRole", :foreign_key => :ttype_id
#      has_many :topic_name_types, :class_name => "TopicName", :foreign_key => :ttype_id
#      has_many :occurrence_types, :class_name => "Occurrence", :foreign_key => :ttype_id

#      has_many :association_types, :aka => :at, :type => :Topic, :wrap => true
#      has_many :association_role_types, :aka => [:role_types,:art,:rt], :type => :Topic, :wrap => true
#      has_many :topic_name_types, :aka => [:name_types,:tnt,:nt], :type => :Topic, :wrap => true
#      has_many :occurrence_types, :aka => :ot, :type => :Topic, :wrap => true

      
      def topic_map
        self
      end

#      # Creates a new Locator in this TopicMap
#      def create_locator(reference, notation=nil)
#        if notation
#          ItemIdentifier.create :reference => reference, :notation => notation
#        else
#          ItemIdentifier.create :reference => reference # is there a better way to preserve the database default notation if none given?
#        end
#      end
    end
  end
end