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

module RTM::Sugar::Topic
  module TopicRef
      
    # Returns an Array including all identifiers of this Topic.
    #
    # This method takes a Hash as argument.
    #
    # The key :ouputstyle defines if the ctm style (value :ctm, this is
    # the default), the YAML
    # style (value :yaml) or no style (value :blank) is supported.
    #
    # In ctm style an item identifier starts with a "^",
    # a subject locator starts with a "=" and
    # a subject identifiers contains no prefix.
    #
    # In yaml style an item identifiers start with a "ii:",
    # a subject locator starts with a "sl:" and
    # a subject identifier start with a "si:".
    #
    # :call-seq:
    #     references -> Array of Strings
    #     references(params = {}) -> Array of Strings
    #
    def references(params = {})
      default_hash = {:outputstyle => :ctm, :resolve_qnames => :false, :resolve_base_iri => :false, :sort_by_length => :true}
      params = default_hash.merge(params) if params.is_a? Hash
      prefixes = topic_map.prefixes

      set_of_si = subject_identifiers.map{|si| si.reference}
      set_of_sl = subject_locators.map{|si| si.reference}
      set_of_ii = item_identifiers.map{|si| si.reference}

      if params[:resolve_qnames] == :true
        unless prefixes.empty?
          prefixes.each do |identifier, reference|
            set_of_si = set_of_si.map{|si| si.index(reference) == 0 ? si.sub(reference, identifier + ":") : si}
            set_of_sl = set_of_sl.map{|sl| sl.index(reference) == 0 ? sl.sub(reference, identifier + ":") : sl}
            set_of_ii = set_of_ii.map{|ii| ii.index(reference) == 0 ? ii.sub(reference, identifier + ":") : ii}
          end
        end
      end

      if params[:resolve_base_iri] == :true
        set_of_si = set_of_si.map{|si| unresolve(si)}
        set_of_sl = set_of_sl.map{|sl| unresolve(sl)}
        set_of_ii = set_of_ii.map{|ii| unresolve(ii)}
      end

      if params[:sort_by_length] == :true
        set_of_si = set_of_si.sort_by{|si| si.length}
        set_of_sl = set_of_sl.sort_by{|sl| sl.length}
        set_of_ii = set_of_ii.sort_by{|ii| ii.length}
      end

      case params[:outputstyle]
      when :yaml, :jtm
        identifiers = set_of_si.map{  |si| "si:#{si}"} +
          set_of_sl.map{  |sl| "sl:#{sl}"} +
          set_of_ii.map{  |ii| "ii:#{ii}"}
      when :blank
        identifiers = set_of_si +
          set_of_sl +
          set_of_ii
      else #:ctm default
        identifiers = set_of_si +
          set_of_sl.map{  |sl| "=#{sl}"} +
          set_of_ii.map{  |ii| "^#{ii}"}
      end
      return identifiers
    end

    # Returns one identifier of this topic. If several exist, the shortest one
    # is returned.
    #
    # This method takes a Hash as argument.
    #
    # The key :ouputstyle defines if the ctm style (value :ctm, this is
    # the default), the YAML
    # style (value :yaml) or no style (value :blank) is supported.
    #
    # In ctm style an item identifier starts with a "^",
    # a subject locator starts with a "=" and
    # a subject identifiers contains no prefix.
    #
    # In yaml style an item identifiers start with a "ii:",
    # a subject locator starts with a "sl:" and
    # a subject identifier start with a "si:".
    #
    #
    # :call-seq:
    #     reference -> String
    #     reference(params = {}) -> String
    #
    def reference(params = {})
      return references(params).first
    end

    # Returns all identifiers of this Topic as Array of Locators.
    #
    # :call-seq:
    #   references_as_locators -> Array of Locators
    #
    def references_as_locators
      subject_identifiers.to_a + subject_locators.to_a + item_identifiers.to_a
    end

    # Unresolves the identifier against the base_iri.
    def unresolve(identifier)
      raise("identifier must be a String") unless identifier.is_a?(String)
      base_iri = topic_map.base_iri
      if identifier.index(base_iri) == 0
        short_form = identifier.sub(base_iri, "")
        if topic_map.create_locator(short_form).reference == identifier
          return short_form
        end
        #        if short_form[0] == "/"
        #          short_form = short_form.sub("/", "")
        #
        #        end
      else
        return identifier #nothing happened
      end
    end

    # Returns true if this topic has a subject identifer equal to the
    # given identifier. Returns false otherwise.
    #
    # A relative identifier is resolved against the base_iri of the topic map.
    # QNames are resolved beforehand.
    #
    # Identifier may be a String or Locator.
    #
    # :call-seq:
    #   has_si?(identifier) -> true or false
    #
    def has_si?(identifier)
      identifier = topic_map.create_locator(identifier)
      return subject_identifiers.include?(identifier)
    end

    # Returns true if this topic has an item identifer equal to the
    # given identifier. Returns false otherwise.
    #
    # A relative identifier is resolved against the base_iri of the topic map.
    # QNames are resolved beforehand.
    #
    # Identifier may be a String or Locator.
    #
    # :call-seq:
    #   has_ii?(identifier) -> true or false
    #
    def has_ii?(identifier)
      identifier = topic_map.create_locator(identifier)
      return item_identifiers.include?(identifier)
    end

    # Returns true if this topic has a subject locator equal to the
    # given identifier. Returns false otherwise.
    #
    # A relative identifier is resolved against the base_iri of the topic map.
    # QNames are resolved beforehand.
    #
    # Identifier may be a String or Locator.
    #
    # :call-seq:
    #   has_sl?(identifier) -> true or false
    #
    def has_sl?(identifier)
      identifier = topic_map.create_locator(identifier)
      return subject_locators.include?(identifier)
    end

    # Returns true if this topic is instance of another topic,
    # that has a subject identifer equal to the
    # given identifier. Returns false otherwise.
    #
    # A relative identifier is resolved against the base_iri of the topic map.
    # QNames are resolved beforehand.
    #
    # Identifier may be a String or Locator.
    #
    # :call-seq:
    #   has_type_with_si?(identifier) -> true or false
    #
    def has_type_with_si?(identifier)
      types.each do |type|
        return true if type.has_si?(identifier)
      end
      return false
    end

    # Returns true if this topic is instance of another topic,
    # that has an item identifer equal to the
    # given identifier. Returns false otherwise.
    #
    # A relative identifier is resolved against the base_iri of the topic map.
    # QNames are resolved beforehand.
    #
    # Identifier may be a String or Locator.
    #
    # :call-seq:
    #   has_type_with_ii?(identifier) -> true or false
    #
    def has_type_with_ii?(identifier)
      types.each do |type|
        return true if type.has_ii?(identifier)
      end
      return false
    end

    # Returns true if this topic is instance of another topic,
    # that has a subject locator equal to the
    # given identifier. Returns false otherwise.
    #
    # A relative identifier is resolved against the base_iri of the topic map.
    # QNames are resolved beforehand.
    #
    # Identifier may be a String or Locator.
    #
    # :call-seq:
    #   has_type_with_sl?(identifier) -> true or false
    #
    def has_type_with_sl?(identifier)
      types.each do |type|
        return true if type.has_sl?(identifier)
      end
      return false
    end
  end
end