module MightyGrid

  class Base

    attr_reader :klass, :name, :relation, :options, :mg_params
    attr_accessor :output_buffer, :filters

    def initialize(klass_or_relation, controller, opts = {})  #:nodoc:
      @controller = controller

      @filters = {}

      @options = {
        :page       => 1,
        :per_page   => MightyGrid.config.per_page,
        :name       => MightyGrid.config.grid_name,
        :include    => nil,
        :joins      => nil,
        :conditions => nil,
        :group      => nil
      }

      opts.assert_valid_keys(@options.keys)
      @options.merge!(opts)

      @name = @options[:name].to_s

      @relation = klass_or_relation

      @klass = klass_or_relation.is_a?(ActiveRecord::Relation) ? klass_or_relation.klass : klass_or_relation

      load_grid_params
    end

    def read
      apply_filters
      @relation = @relation.order("#{@mg_params[:order]} #{current_order_direction.to_sym}") if @mg_params[:order].present? && current_order_direction.present?
      @relation = @relation.
                    page(@mg_params[:page]).
                    per(@mg_params[:per_page]).
                    includes(@options[:include]).
                    joins(@options[:joins]).
                    where(@options[:conditions]).
                    group(@options[:group])
    end

    # Apply filters
    def apply_filters
      filter_params.each do |filter_name, filter_value|
        name, table_name = filter_name.split('.').reverse

        if table_name && Object.const_defined?(table_name.classify)
          model = table_name.classify.constantize
        else
          model = klass
        end

        next if filter_value.blank? || !model.column_names.include?(name)
        
        if model && model.superclass == ActiveRecord::Base
          filter_type = model.columns_hash[name].type
        else
          next
        end

        field_type ||= model.columns_hash[name].type
        table_name = model.table_name
        
        if @filters.has_key?(filter_name.to_sym) && @filters[filter_name.to_sym].is_a?(Array)
          @relation = @relation.where(table_name => { filter_name => filter_value })
        elsif field_type == :boolean
          value = ['true', '1', 't'].include?(filter_value) ? true : false
          @relation = @relation.where(table_name => {filter_name => value})
        elsif [:string, :text].include?(field_type)
          @relation = @relation.where("#{table_name}.#{name} #{like_operator} ?", "%#{filter_value}%")
        end
      end
    end

    # Get controller parameters
    def params
      @controller.params
    end

    # Load grid parameters
    def load_grid_params
      @mg_params = {}
      @mg_params.merge!(@options)
      if current_grid_params
        @mg_params.merge!(current_grid_params.symbolize_keys)
        if @mg_params[:order].present? && !@mg_params[:order].to_s.include?('.')
          @mg_params[:order] = "#{klass.table_name}.#{@mg_params[:order]}" 
        end
      end
    end

    # Get current grid parameter by name
    def get_current_grid_param(name)
      current_grid_params.has_key?(name) ? current_grid_params[name] : nil
    end

    # Get filter parameters
    def filter_params
      get_current_grid_param(filter_param_name) || {}
    end

    # Get filter parameter name
    def filter_param_name; 'f' end

    # Get filter name by field
    def get_filter_name(field, model = nil)
      field_name = model.present? ? "#{model.table_name}.#{field}" : field
      "#{name}[#{filter_param_name}][#{field_name}]"
    end

    # Get current grid parameters
    def current_grid_params
      params[name] || {}
    end

    # Get order parameters
    def order_params(attribute, model = nil)
      order = model.present? ? "#{model.table_name}.#{attribute}" : attribute.to_s
      direction = order == current_grid_params['order'] ? another_order_direction : 'asc'
      {@name => {order: order, order_direction: direction}}
    end

    # Get current order direction
    def current_order_direction
      direction = nil
      if current_grid_params.has_key?('order_direction') && ['asc', 'desc'].include?(current_grid_params['order_direction'].downcase)
        direction = current_grid_params['order_direction'].downcase
      end
      direction
    end

    # Get another order direction
    def another_order_direction
      (current_grid_params.has_key?('order_direction')) ? (['asc', 'desc'] - [current_grid_params['order_direction'].to_s]).first : MightyGrid.config.order_direction
    end

    # Get <tt>like</tt> or <tt>ilike</tt> operator depending on the database adapter 
    def like_operator
      if ActiveRecord::ConnectionAdapters.const_defined?(:PostgreSQLAdapter) && ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
        'ILIKE'
      else
        'LIKE'  
      end
    end

  end
end