module RTM::AR
  class Topic < TopicMapConstruct
    include RTM::Topic
    wrapper_cache

    parent :topic_map
    property_set :subject_identifiers, :aka => [:si,:sid], :type => :SubjectIdentifier, :wrap => true,
      #:create => :subject_identifier, :create_aka => [:csi,:csid],
      #:create_args => [{:name => :reference, :type => :String}]
      :add => true, :remove => true
    property_set :subject_locators, :aka => [:sl,:slo], :type => :SubjectLocator, :wrap => true,
      #:create => :subject_locator, :create_aka => [:csl, :cslo],
      #:create_args => [{:name => :reference, :type => :String}]
      :add => true, :remove => true

    property :reified, :computable => true, :type => :Reifiable, :wrap => true

    property_set :topic_names, :aka => [:n, :names], :type => :TopicName, :wrap => true,
      :create => :topic_name, :create_aka => [:create_name, :cn], 
      :create_args => [ {:name => :value, :type => :String}, {:name => :type, :type => :Topic, :optional => true}, {:name => :scope, :type => :Collection} ]

    property_set :occurrences, :aka => :o, :type => :Occurrence, :wrap => true,
      :create => :occurrence, :create_aka => :co,
      :create_args => [ {:name => :value, :type => [:String, :Locator]}, {:name => :type, :type => :Topic}, {:name => :scope, :type => :Collection} ]

    property_set :roles, :aka => [:r, :roles_played, :association_roles], :computable => true, :type => :AssociationRole, :wrap => true

    property_set :associations, :aka => [:a, :associations_played], :type => :Association, :wrap => true

    property_set :scoped_objects, :type => :TopicMapConstruct, :wrap => true
    property_set :scoped_associations, :type => :Association, :wrap => true
    property_set :scoped_topic_names, :aka => :scoped_names, :type => :TopicName, :wrap => true
    property_set :scoped_variants, :type => :Variant, :wrap => true
    property_set :scoped_occurrences, :type => :Occurrence, :wrap => true

    property_set :associations_typed, :aka => :at, :type => :Association, :wrap => true
    property_set :association_roles_typed, :aka => [:roles_typed,:art,:rt], :type => :AssociationRole, :wrap => true
    property_set :topic_names_typed, :aka => [:names_typed,:tnt,:nt], :type => :TopicName, :wrap => true
    property_set :occurrences_typed, :aka => :ot, :type => :Occurrence, :wrap => true
    
    require 'rtm/sugar/topic/characteristics'
    include RTM::Sugar::Topic::Characteristics
    require 'rtm/sugar/topic/counterparts'
    include RTM::Sugar::Topic::Counterparts
    require 'rtm/sugar/topic/identifier_direct'
    include RTM::Sugar::Topic::IdentifierDirect
    require 'rtm/sugar/topic/hash_access'
    include RTM::Sugar::Topic::HashAccess
    require 'rtm/sugar/topic/predefined_associations'
    include RTM::Sugar::Topic::PredefinedAssociations

    # class:Topic equality, http://www.isotopicmaps.org/sam/sam-model/#d0e1029
    #
    # This method is all crap. These ActiveRecord Arrays can't be intersected,
    # so I map the identifiers to their reference which seems long winded.
    # Still I find it better than using their ID in the long.
    # 
    def ==(o)
      return false unless o
      # Two topic items are equal if they have:
      # * at least one equal string in their [subject identifiers] properties,
      # -> test if intersection are > 0
      my_si = self.subject_identifiers.map{|si| si.reference}
      ot_si = o.subject_identifiers.map{|si| si.reference}
      return true if ( my_si & ot_si).size > 0 

      # * at least one equal string in their [item identifiers] properties,
      my_ii = self.item_identifiers.map{|ii| ii.reference}
      ot_ii = o.item_identifiers.map{|ii| ii.reference}
      return true if (my_ii & ot_ii).size > 0 

      # * at least one equal string in their [subject locators] properties,
      my_sl = self.subject_locators.map{|sl| sl.reference}
      ot_sl = o.subject_locators.map{|sl| sl.reference}
      return true if (my_sl & ot_sl).size > 0 

      # * an equal string in the [subject identifiers] property of the one topic item and the [item identifiers] property of the other, or
      return true if (my_si & ot_ii).size > 0 
      return true if (my_ii & ot_si).size > 0 

      # * the same information item in their [reified] properties.
      return true if self.reified != nil && o.reified != nil && (self.reified == o.reified)
      # ... otherwise
      false
    end
  end
end