require 'ebsco/eds/jsonable'

module EBSCO

  module EDS

    class Options
      include JSONable
      attr_accessor :SearchCriteria, :RetrievalCriteria, :Actions, :Comment
      def initialize(options = {}, info)
  
        @SearchCriteria = EBSCO::EDS::SearchCriteria.new(options, info)

        @RetrievalCriteria = EBSCO::EDS::RetrievalCriteria.new(options, info)
  
        @Actions = []

        @Comment = ''

        # add DefaultOn=y Type=select limiters
        # info.available_limiters.each do |limiter|
        #   if limiter['DefaultOn'] == 'n' and limiter['Type'] == 'select'
        #     @Actions.push "addLimiter(#{limiter['Id']}:y)"
        #   end
        # end

        # add page default of 1
        unless options.has_key?('page') || options.has_key?('page_number')
          options['page'] = 1
        end

        options.each do |key, value|

          case key

            when :actions
              add_actions(options[:actions], info)

            # SOLR: Need to add page actions whenever other actions are present since the other actions
            # will always reset the page to 1 even though a PageNumber is present in RetrievalCriteria.
            when 'page', 'page_number'
              @Actions.push "GoToPage(#{value.to_i})"

          end
        end
  
      end
  
      def add_actions(actions, info)
        if actions.kind_of?(Array) && actions.count > 0
          actions.each do |item|
            #if is_valid_action(item, info)
            @Actions.push item
            #end
          end
        else
          #if is_valid_action(actions, info)
          @Actions = [actions]
          #else
          #end
        end
      end

      # def is_valid_action(action, info)
      #   # actions in info that require an enumerated value (e.g., addlimiter(LA99:Bulgarian))
      #   _available_actions = info.available_actions
      #   _defined_action = _available_actions.include? action
      #   # actions not enumerated in info (e.g., GoToPage(3))
      #   _available_standard_actions = ['GoToPage']
      #   _standard_action = _available_standard_actions.any? { |std| action.include? std }
      #   # actions in info that require a user supplied value (e.g., addlimiter(TI:value))
      #   _available_value_actions = %w{PG4 CS1 FM FT FR RV DT1 SO}
      #   _value_action = _available_value_actions.any? { |type| action.include? type }
      #
      #   if _value_action || _defined_action || _standard_action
      #     true
      #   else
      #     false
      #   end
      # end


      # Caution: experimental, not ready for production
      # query-1=AND,volcano&sort=relevance&includefacets=y&searchmode=all&autosuggest=n&view=brief&resultsperpage=20&pagenumber=1&highlight=y
      def to_query_string
        qs = ''

        # SEARCH CRITERIA:

        # query
        #  if @SearchCriteria.Queries[0].has_key? :BooleanOperator
        #   qs << 'query=' + @SearchCriteria.Queries[0][:BooleanOperator]
        # else
        #   qs << 'query=AND'
        # end
        # if @SearchCriteria.Queries[0].has_key? :FieldCode
        #   qs << ',' + @SearchCriteria.Queries[0][:FieldCode]
        # end
        qs << 'query=' + @SearchCriteria.Queries[0][:Term]

        # mode
        qs << '&searchmode=' + @SearchCriteria.SearchMode

        # facets
        qs << '&includefacets=' + @SearchCriteria.IncludeFacets

        # sort
        qs << '&sort=' + @SearchCriteria.Sort

        # auto-suggest
        qs << '&autosuggest=' + @SearchCriteria.AutoSuggest

        # auto-correct
        qs << '&autocorrect=' + @SearchCriteria.AutoCorrect

        # limiters
        unless @SearchCriteria.Limiters.nil?
          qs << '&limiter=' + @SearchCriteria.Limiters.join(',')
        end

        # expanders
        qs << '&expander=' + @SearchCriteria.Expanders.join(',')

        # facet filters
        unless @SearchCriteria.FacetFilters.nil?
          qs << '&facetfilter=1,' + @SearchCriteria.FacetFilters.join(',')
        end

        # related content
        unless @SearchCriteria.RelatedContent.nil?
          qs << '&relatedcontent=' + @SearchCriteria.RelatedContent.join(',')
        end

        # Retrieval Criteria
        unless @RetrievalCriteria.View.nil?
          qs << '&view=' + @RetrievalCriteria.View
        end
        unless @RetrievalCriteria.ResultsPerPage.nil?
          qs << '&resultsperpage=' + @RetrievalCriteria.ResultsPerPage.to_s
        end
        unless @RetrievalCriteria.PageNumber.nil?
          qs << '&pagenumber=' + @RetrievalCriteria.PageNumber.to_s
        end
        unless @RetrievalCriteria.Highlight.nil?
          qs << '&highlight=' + @RetrievalCriteria.Highlight.to_s
        end

        unless @Actions.nil?
          @Actions.each do |action|
            qs << '&action=' + action
          end
        end
        qs

      end

      end
  
    class SearchCriteria
      include JSONable
      attr_accessor :Queries, :SearchMode, :IncludeFacets, :FacetFilters, :Limiters, :Sort, :PublicationId,
                    :RelatedContent, :AutoSuggest, :Expanders, :AutoCorrect

      def initialize(options = {}, info)

        # defaults
        @SearchMode = info.default_search_mode
        @IncludeFacets = 'y'
        @Sort = 'relevance'
        @AutoSuggest = info.default_auto_suggest
        @AutoCorrect = info.default_auto_correct
        _has_query = false

        @Expanders = info.default_expander_ids
        _my_expanders = []
        _available_expander_ids = info.available_expander_ids

        @Limiters = nil
        _my_limiters = []

        @FacetFilters = []
        filter_id = 1

        @RelatedContent = info.default_related_content_types
        _my_related_content = []
        _available_related_content_types = info.available_related_content_types

        # blacklight year range slider input
        # "range"=>{"pub_year_tisim"=>{"begin"=>"1970", "end"=>"1980"}}
        if options.has_key?('range')
          if options['range'].has_key?('pub_year_tisim')
            begin_year = nil
            end_year = nil
            if options['range']['pub_year_tisim'].has_key?('begin')
              begin_year = options['range']['pub_year_tisim']['begin']
            end
            if options['range']['pub_year_tisim'].has_key?('end')
              end_year = options['range']['pub_year_tisim']['end']
            end
            unless begin_year.nil? or end_year.nil?
              pub_year_tisim_range = begin_year + '-01/' + end_year + '-01'
              _my_limiters.push({:Id => 'DT1', :Values => [pub_year_tisim_range]})
            end
          end
        end

        options.each do |key, value|

          case key

            # ====================================================================================
            # query
            # ====================================================================================
            when :query, 'q'

              # add blacklight search_fields
              _field_code = ''
              if options.has_key? 'search_field'
                _field = options['search_field']
                case _field
                  when 'author'
                    _field_code = 'AU'
                  when 'subject'
                    _field_code = 'SU'
                  when 'title'
                    _field_code = 'TI'
                  when 'text'
                    _field_code = 'TX'
                  when 'abstract'
                    _field_code = 'AB'
                  when 'source'
                    _field_code = 'SO'
                  when 'issn'
                    _field_code = 'IS'
                  when 'isbn'
                    _field_code = 'IB'
                  when 'descriptor'
                    _field_code = 'DE'
                  when 'series'
                    _field_code = 'SE'
                  when 'subject_heading'
                    _field_code = 'SH'
                  when 'keywords'
                    _field_code = 'KW'
                  when /[A-Z]{2}/
                    _field_code = _field
                end
              end

              if not _field_code == ''
                @Queries =  [{:FieldCode => _field_code, :Term => value}]
              else
                @Queries =  [{:Term => value}]
              end
              _has_query = true

            # ====================================================================================
            # mode
            # ====================================================================================
            when :mode
              if info.available_search_mode_types.include? value.downcase
                @SearchMode = value.downcase
              else
                @SearchMode = info.default_search_mode
              end

            # ====================================================================================
            # facets
            # ====================================================================================
            when :include_facets
              @IncludeFacets = value ? 'y' : 'n'

            when :facet_filters
              @FacetFilters = value

            # ====================================================================================
            # sort
            # ====================================================================================
            when :sort, 'sort'
              if info.available_sorts(value.downcase).empty?
                if value.downcase == 'newest' || value.downcase == 'pub_date_sort desc'
                  @Sort = 'date'
                elsif value.downcase == 'oldest' || value.downcase == 'pub_date_sort asc'
                  @Sort = 'date2'
                elsif value.downcase == 'score desc'
                  @Sort = 'relevance'
                else
                  @Sort = 'relevance'
                end
              else
                @Sort = value.downcase
              end

            # ====================================================================================
            # publication id
            # ====================================================================================
            when :publication_id
              @PublicationId = value

            # ====================================================================================
            # auto suggest & correct
            # ====================================================================================
            when :auto_suggest, 'auto_suggest'
              @AutoSuggest = value ? 'y' : 'n'

            when :auto_correct, 'auto_correct'
              @AutoCorrect = value ? 'y' : 'n'

            # ====================================================================================
            # expanders
            # ====================================================================================
            when :expanders
              value.each do |item|
                if _available_expander_ids.include? item.downcase
                  _my_expanders.push(item)
                end
              end
              if _my_expanders.empty?
                _my_expanders = info.default_expander_ids
              end
              @Expanders = _my_expanders

            # ====================================================================================
            # solr limiters & facets
            # ====================================================================================

            when 'f'
              _search_limiter_list = []
              if value.has_key?('eds_search_limiters_facet')
                _search_limiter_list = value['eds_search_limiters_facet']
              end
              info.available_limiters.each do |limiter|
                # only handle 'select' limiters (ones with values of 'y' or 'n')
                if ( _search_limiter_list.include? limiter['Label'] or _search_limiter_list.include? limiter['Id']) and limiter['Type'] == 'select'
                  _my_limiters.push({:Id => limiter['Id'], :Values => ['y']})
                end
              end

              # date limiters
              if value.has_key?('eds_publication_year_range_facet')
                _list = value['eds_publication_year_range_facet']
                _this_year = Date.today.year
                _this_month = Date.today.month
                _list.each do |item|
                  if item == 'This year'
                    _range = _this_year.to_s + '-01/' + _this_year.to_s + '-' + _this_month.to_s
                    _my_limiters.push({:Id => 'DT1', :Values => [_range]})
                  end
                  if item == 'Last 3 years'
                    _range = (_this_year-3).to_s + '-' + _this_month.to_s + '/' + _this_year.to_s + '-' + _this_month.to_s
                    _my_limiters.push({:Id => 'DT1', :Values => [_range]})
                  end
                  if item == 'Last 10 years'
                    _range = (_this_year-10).to_s + '-' + _this_month.to_s + '/' + _this_year.to_s + '-' + _this_month.to_s
                    _my_limiters.push({:Id => 'DT1', :Values => [_range]})
                  end
                  if item == 'Last 50 years'
                    _range = (_this_year-50).to_s + '-' + _this_month.to_s + '/' + _this_year.to_s + '-' + _this_month.to_s
                    _my_limiters.push({:Id => 'DT1', :Values => [_range]})
                  end
                  if item == 'More than 50 years ago'
                    _range = '0000-01/' + (_this_year-50).to_s + '-12'
                    _my_limiters.push({:Id => 'DT1', :Values => [_range]})
                  end
                end
              end

              # Language
              if value.has_key?('eds_language_facet')
                lang_list = value['eds_language_facet']
                lang_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'Language', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # SubjectEDS
              if value.has_key?('eds_subject_topic_facet')
                subj_list = value['eds_subject_topic_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'SubjectEDS', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # SubjectGeographic
              if value.has_key?('eds_subjects_geographic_facet')
                subj_list = value['eds_subjects_geographic_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'SubjectGeographic', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # Publisher
              if value.has_key?('eds_publisher_facet')
                subj_list = value['eds_publisher_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'Publisher', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # Journal
              if value.has_key?('eds_journal_facet')
                subj_list = value['eds_journal_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'Journal', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # Category
              if value.has_key?('eds_category_facet')
                subj_list = value['eds_category_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'Category', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # LocationLibrary
              if value.has_key?('eds_library_location_facet')
                subj_list = value['eds_library_location_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'LocationLibrary', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # CollectionLibrary
              if value.has_key?('eds_library_collection_facet')
                subj_list = value['eds_library_collection_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'CollectionLibrary', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # AuthorUniversity
              if value.has_key?('eds_author_university_facet')
                subj_list = value['eds_author_university_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'AuthorUniversity', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # PublicationYear
              if value.has_key?('eds_publication_year_facet')
                year_list = value['eds_publication_year_facet']
                year_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'PublicationYear', 'Value' => item}]})
                  filter_id += 1
                end
              end

              # Special Cases:

              # SourceType
              if value.has_key?('eds_publication_type_facet')
                f_list = value['eds_publication_type_facet']
                f_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'SourceType', 'Value' => item}]})
                  filter_id += 1
                end
              end
              # ContentProvider
              if value.has_key?('eds_content_provider_facet')
                subj_list = value['eds_content_provider_facet']
                subj_list.each do |item|
                  @FacetFilters.push({'FilterId' => filter_id, 'FacetValues' => [{'Id' => 'ContentProvider', 'Value' => item}]})
                  filter_id += 1
                end
              end

            # ====================================================================================
            # limiters
            # ====================================================================================

            when :limiters
              value.each do |item|
                _key = item.split(':',2).first.upcase
                _values = item.split(':',2).last
                # is limiter id available?
                if info.available_limiter_ids.include? _key
                  _limiter = info.available_limiters(_key)
                  # if multi-value, add the values if they're available
                  if _limiter['Type'] == 'multiselectvalue'
                    _available_values = info.available_limiter_values(_key)
                    _multi_values = []
                    _values.split(',').each do |val|
                      # todo: make case insensitive?
                      if _available_values.include? val
                        _multi_values.push(val)
                      end
                    end
                    if _multi_values.empty?
                      # do nothing, none of the values are available
                    else
                      _my_limiters.push({:Id => _key, :Values => _multi_values})
                    end
                    # single value, just pass it on
                  else
                    _my_limiters.push({:Id => _key, :Values => [_values]})
                  end
                end
              end
              @Limiters = _my_limiters

            # ====================================================================================
            # related content
            # ====================================================================================
            when :related_content
              value.each do |item|
                if _available_related_content_types.include? item.downcase
                  _available_related_content_types.push(item)
                else
                  # silently ignore
                end
              end
              if _my_related_content.empty?
                _my_related_content = info.default_related_content_types
              end
              @RelatedContent = _my_related_content

            # ====================================================================================
            # unsupported parameters, ignore
            # ====================================================================================
            else
              # ignore

          end

        end # end options parsing

        # set solr limiters, if any
        @Limiters = _my_limiters

      end

    end

    class RetrievalCriteria
      include JSONable
      attr_accessor :View, :ResultsPerPage, :PageNumber, :Highlight
      def initialize(options = {}, info)

        # defaults
        @View = info.default_result_list_view
        @ResultsPerPage = info.default_results_per_page
        @PageNumber = 1

        options.each do |key, value|

          case key

            # ====================================================================================
            # view
            # ====================================================================================
            when :view, 'view'
              if info.available_result_list_views.include? value.downcase
                @View = value.downcase
              else
                @View = info.default_result_list_view
              end

            # ====================================================================================
            # results per page
            # ====================================================================================
            when :results_per_page, 'results_per_page', 'rows', 'per_page'
              if value.to_i > info.max_results_per_page
                @ResultsPerPage = info.max_results_per_page
              else
                @ResultsPerPage = value.to_i
              end

            # ====================================================================================
            # page number
            # ====================================================================================
            when :page_number, 'page_number', 'page'
              @PageNumber = value.to_i
            # solr starts at page 0
            # when 'start'
            #  @PageNumber = value.to_i + 1

            # ====================================================================================
            # highlight
            # ====================================================================================
            when :highlight, 'highlight'
              @Highlight = value
            # solr/blacklight version
            when 'hl'
              if value == 'on'
                @Highlight = 'y'
              else
                @Highlight = 'y' # API bug: if set to 'n' you won't get research starter abstracts!
              end

            else

          end

        end

      end
  
    end

  end
end