# Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
# License:   Apache License, Version 2.0

module RTM::Sugar
  module Topic
    # This module implements methods for Hash-like access in Topics.
    module HashAccess

      # Returns an Array of Names or Occurrences of
      # this Topic depending on the argument given.
      #
      # If argument is a Topic, an Array of Occurrences of this Topic which
      # have the specified type is returned.
      #
      # Argument may be a String. Then the structure should resemble "-type @scope"
      # or "type @scope".
      #
      # If argument starts with a "-", an Array of Names of this
      # Topic, which have the specified type (trailing the "-"),
      # is returned.
      # 
      # If argument equals "-", Names that have the standard name type
      # are returned.
      #
      # If argument starts with a "*", both Names and Occurrences, which have the
      # specified type (trailing the "*") are returned as Array.
      #
      # If argument equals "*", all Names and Occurrences of this Topic are
      # returned. In this case this method equals the characteristics()-method.
      #
      # Else, if argument is a String, an Array of Occurrences
      # of this Topic, which have the specified type,
      # is returned.
      #
      # The argument may include an "@" after the type.
      # The String trailing the @ defines the
      # scope the selected Names and Occurrences should live in. Themes in the
      # scope may be c.
      #
      # Examples:
      #  topic[occtype_topic]
      #  topic["-"]
      #  topic["*"]
      #  topic["-nametype"]
      #  topic["-nametype @scope]
      #  topic["occtype @scope1, scope2"]
      #  topic["*type"]
      #  topic["*type @scope"]
      #
      # :call-seq:
      #   [argument=Topic]  -> Array of Occurrences
      #   [argument=String] -> Array of Names and/or Occurrences
      #
      def [](identifier = :any)
        # return occurrences by type if topic is given
        return [] unless identifier
        return occurrences(identifier).to_a if (identifier.is_a?(RTM::Topic) || identifier == :any)
        prefix, type_reference, scope_reference = resolve_characteristic(identifier)
        if prefix == :name
          type_reference = RTM::PSI[:name_type] if type_reference == :any
          ret = names(type_reference).to_a
        elsif prefix == :occurrence
          raise "No occurrence type given" if type_reference == :any
          ret = occurrences(type_reference).to_a
        elsif prefix == :both
          ret = names(type_reference).to_a + occurrences(type_reference).to_a # type_reference == :any or not :any
        else
          
        end
        # we now have a list of names or occurrences, now we have to select for scope
        return ret if scope_reference == :any
        if scope_reference == :ucs
          ret.select{|i| i.scope.size == 0}
        else
          ret = ret.select{|i| scope_reference.all?{|sr| i.scope.include? getParent.get(sr)}}
        end
        ret
      end

      # Creates a Name or Occurrence of this Topic depending on the
      # argument given and returns this Name/Occurrence.
      #
      # Argument may be a String whose structure should resemble "-type @scope"
      # or "type @scope".
      #
      # If argument starts with a "-", a Name that has the specified type
      # (trailing the "-") is created.
      #
      # If argument equals "-", a Name that hase the standard name type
      # is created.
      #
      # Else, an Occurrence that has the specified type is created.
      #
      # The argument may include an "@" after the type. The String trailing
      # the @ defines the optional scope the Name or Occurrence
      # should live in. Themes in the scope may be comma- and/or space-separated.
      #
      # The value should be a String.
      #
      # Example:
      #  topic[occtype]= "occvalue"
      #  topic["-"]= "name"
      #  topic["-nametype"]= "name"
      #  topic["-nametype @scope]= "name"
      #  topic["occtype @scope1, scope2"]= "occvalue"
      #
      # :call-seq:
      #   [argument]= value
      #
      def []=(identifier, value)
        prefix, type_reference, scope_reference = resolve_characteristic(identifier)
        if prefix == :name
          nametype = (type_reference==:any) ? topic_map.get!(RTM::PSI[:name_type]) : type_reference
          scope_reference = scope_reference==:any ? [] : scope_reference
          raise("Value is not a String") unless value.is_a?(String)
          ret = create_name(nametype, value, scope_reference)
        elsif prefix == :occurrence
          raise "No occurrence type given" if type_reference == :any
          ret = create_occurrence(type_reference, value)
        else
          return nil
        end
        # return object without scope if any or ucs
        return ret if [:any, :ucs].include? scope_reference
        # set scope
        ret.add_scope(scope_reference)
        ret
      end

      private

      def resolve_characteristic(identifier)
        # spaces(-|*)?spaces(anything)
        prefix = :occurrence
        identifier =~ /^\s*(-|\*)?\s*(.+)?$/ # - or * and then everything else
        if $1
          prefix = ($1 == "-") ? :name : :both
        end
        type, scope = resolve_type_and_scope($2)
        [prefix, type, scope]
      end

      def resolve_type_and_scope(ref)
        return [:any, :any] unless ref
        if ref[0] == ?@
          type = :any
          scope = ref[1..-1] #cut "@"
        else
          type, scope = ref.split(' @', -1) # -1 -> trailing null fields are not suppressed
          if (type == nil) || (type.length == 0)
            type = :any
          else
            type.strip! #\s removed
          end
        end
        
        if scope
          scope = scope.strip.split(/\s*,?\s+/) #divided at \s,\s
          scope = :ucs if (scope == nil) || (scope.length == 0)
        else
          scope = :any
        end
        [type, scope]
      end
    end
  end
end