# -*- encoding : utf-8 -*-
require 'rsolr'
##
##
# = Introduction
# Blacklight::Solr::Document is the module with logic for a class representing
# an individual document returned from Solr results.  It can be added in to any
# local class you want, but in default Blacklight a SolrDocument class is
# provided for you which is pretty much a blank class "include"ing
# Blacklight::Solr::Document.
#
# Blacklight::Solr::Document provides some DefaultFinders.
#
# It also provides support for Document Extensions, which advertise supported
# transformation formats.
#

module Blacklight::Solr::Document
  extend Deprecation
  self.deprecation_horizon = 'Blacklight 5.x'

  autoload :Marc, 'blacklight/solr/document/marc'
  autoload :MarcExport, 'blacklight/solr/document/marc_export'
  autoload :DublinCore, 'blacklight/solr/document/dublin_core'
  autoload :Email, 'blacklight/solr/document/email'
  autoload :Sms, 'blacklight/solr/document/sms'
  autoload :Extensions, 'blacklight/solr/document/extensions'
  autoload :Export, 'blacklight/solr/document/export'
  autoload :MoreLikeThis, 'blacklight/solr/document/more_like_this'

  extend ActiveSupport::Concern
  include Blacklight::Solr::Document::Export
  include Blacklight::Solr::Document::MoreLikeThis

  included do
    extend ActiveModel::Naming
    include Blacklight::Solr::Document::Extensions
  end    

  attr_reader :solr_response

  def initialize(source_doc={}, solr_response=nil)
    @_source = source_doc.with_indifferent_access
    @solr_response = solr_response
    apply_extensions
  end

  # the wrapper method to the @_source object.
  # If a method is missing, it gets sent to @_source
  # with all of the original params and block
  def method_missing(m, *args, &b)
    @_source.send(m, *args, &b)
  end

  def [] *args
    @_source.send :[], *args
  end

  # Helper method to check if value/multi-values exist for a given key.
  # The value can be a string, or a RegExp
  # Multiple "values" can be given; only one needs to match.
  # 
  # Example:
  # doc.has?(:location_facet)
  # doc.has?(:location_facet, 'Clemons')
  # doc.has?(:id, 'h009', /^u/i)
  def has?(k, *values)
    return true if key?(k) and values.empty?
    return false if self[k].nil?
    target = self[k]
    if target.is_a?(Array)
      values.each do |val|
        return target.any?{|tv| val.is_a?(Regexp) ? (tv =~ val) : (tv==val)}
      end
    else
      return values.any? {|val| val.is_a?(Regexp) ? (target =~ val) : (target == val)}
    end
  end

  def key? k
    @_source.key? k
  end

  def has_highlight_field? k
    return false if @solr_response['highlighting'].blank? or @solr_response['highlighting'][self.id].blank?
    
    @solr_response['highlighting'][self.id].key? k.to_s
  end

  def highlight_field k
    return nil unless has_highlight_field? k
    @solr_response['highlighting'][self.id][k.to_s].map { |x| x.html_safe }

  end

  # helper
  # key is the name of the field
  # opts is a hash with the following valid keys:
  #  - :sep - a string used for joining multivalued field values
  #  - :default - a value to return when the key doesn't exist
  # if :sep is nil and the field is a multivalued field, the array is returned
  def get(key, opts={:sep=>', ', :default=>nil})
    if key? key
      val = self[key]
      (val.is_a?(Array) and opts[:sep]) ? val.join(opts[:sep]) : val
    else
      opts[:default]
    end
  end

  def first key
    Array(self[key]).first
  end

  def id
    self[self.class.unique_key]
  end

  def to_param
    id
  end

  def as_json(options = nil)
    @_source.as_json(options)
  end

  def to_partial_path
    'catalog/document'
  end

   
  # Returns a hash keyed by semantic tokens (see ExtendableClassMethods#semantic_fields), value is an array of
  # strings. (Array to handle multi-value fields). If no value(s)
  # available, empty array is returned. 
  #
  # Default implementation here uses ExtendableClassMethods#semantic_fields
  # to just take values from Solr stored fields. 
  # Extensions can over-ride this method to provide better/different lookup,
  # but extensions should call super and modify hash returned, to avoid
  # unintentionally erasing values provided by other extensions. 
  def to_semantic_values
    unless @semantic_value_hash
      @semantic_value_hash = Hash.new([]) # default to empty array   
      self.class.field_semantics.each_pair do |key, solr_field|
        value = self[solr_field]
        # Make single and multi-values all arrays, so clients
        # don't have to know.
        unless value.nil?
          value = [value] unless value.kind_of?(Array)      
          @semantic_value_hash[key] = value
        end
      end
    end
    return @semantic_value_hash
  end
  
  
  # Certain class-level methods needed for the document-specific
  # extendability architecture
  module ClassMethods

    attr_writer :unique_key
    def unique_key
      # XXX Blacklight.config[:unique_key] should be deprecated soon
      if Blacklight.respond_to?(:config) and Blacklight.config[:unique_key]
        Deprecation.warn(self, "Setting the unique key using Blacklight.config[:unique_key] has been deprecated. Use the SolrDocument.unique_key= setter instead")
        @unique_key ||= Blacklight.config[:unique_key] 
      end
      @unique_key ||= 'id' 

      @unique_key
    end

     def connection
       Deprecation.warn(self, "Document.connection is deprecated and will be removed in Blacklight 5.0")
       @connection ||= Blacklight.solr
     end
    
    # Returns array of hashes of registered extensions. Each hash
    # has a :module_obj key and a :condition_proc key. Usually this
    # method is only used internally in #apply_extensions, but if you
 
    # Class-level method for accessing/setting semantic mappings
    # for solr stored fields. Can be set by local app, key is
    # a symbol for a semantic, value is a solr _stored_ field.
    #
    # Stored field can be single or multi-value. In some cases
    # clients may only use the first value from a multi-value field.
    #
    # Currently documented semantic tokens, not all may be
    # used by core BL, but some may be used by plugins present
    # or future. 
    # :title, :author, :year, :language => User-presentable strings. 
    def field_semantics
      @field_semantics ||= {}
    end    
  end
  
 
  
end