class ListController < ApplicationController

  before_filter :ensure_logged_in, except: [:latest, :hot, :category, :category_feed, :latest_feed, :hot_feed, :topics_by]
  before_filter :set_category, only: [:category, :category_feed]
  skip_before_filter :check_xhr

  # Create our filters
  [:latest, :hot, :favorited, :read, :posted, :unread, :new].each do |filter|
    define_method(filter) do
      list_opts = build_topic_list_options
      list = TopicQuery.new(current_user, list_opts).public_send("list_#{filter}")
      list.more_topics_url = url_for(self.public_send "#{filter}_path".to_sym, list_opts.merge(format: 'json', page: next_page))

      respond(list)
    end
  end

  [:latest, :hot].each do |filter|
    define_method("#{filter}_feed") do
      anonymous_etag(@category) do
        @title = "#{filter.capitalize} Topics"
        @link = "#{Discourse.base_url}/#{filter}"
        @description = I18n.t("rss_description.#{filter}")
        @atom_link = "#{Discourse.base_url}/#{filter}.rss"
        @topic_list = TopicQuery.new(current_user).public_send("list_#{filter}")
        render 'list', formats: [:rss]
      end
    end
  end

  def topics_by
    list_opts = build_topic_list_options
    list = TopicQuery.new(current_user, list_opts).list_topics_by(fetch_user_from_params)
    list.more_topics_url = url_for(topics_by_path(list_opts.merge(format: 'json', page: next_page)))

    respond(list)
  end

  def category
    query = TopicQuery.new(current_user, page: params[:page])

    # If they choose uncategorized, return topics NOT in a category
    if request_is_for_uncategorized?
      list = query.list_uncategorized
    else
      if !@category
        raise Discourse::NotFound
        return
      end
      guardian.ensure_can_see!(@category)
      list = query.list_category(@category)
    end

    list.more_topics_url = url_for(category_list_path(params[:category], page: next_page, format: "json"))
    respond(list)
  end

  def category_feed
    raise Discourse::InvalidParameters.new('Category RSS of "uncategorized"') if request_is_for_uncategorized?

    guardian.ensure_can_see!(@category)

    anonymous_etag(@category) do
      @title = @category.name
      @link = "#{Discourse.base_url}/category/#{@category.slug}"
      @description = "#{I18n.t('topics_in_category', category: @category.name)} #{@category.description}"
      @atom_link = "#{Discourse.base_url}/category/#{@category.slug}.rss"
      @topic_list = TopicQuery.new.list_new_in_category(@category)
      render 'list', formats: [:rss]
    end
  end

  def popular_redirect
    # We've renamed popular to latest. Use a redirect until we're sure we can
    # safely remove this.
    redirect_to latest_path, :status => 301
  end

  protected

  def respond(list)

    list.draft_key = Draft::NEW_TOPIC
    list.draft_sequence = DraftSequence.current(current_user, Draft::NEW_TOPIC)

    draft = Draft.get(current_user, list.draft_key, list.draft_sequence) if current_user
    list.draft = draft

    discourse_expires_in 1.minute

    respond_to do |format|
      format.html do
        @list = list
        store_preloaded('topic_list', MultiJson.dump(TopicListSerializer.new(list, scope: guardian)))
        render 'list'
      end
      format.json do
        render_serialized(list, TopicListSerializer)
      end
    end
  end

  def next_page
    params[:page].to_i + 1
  end

  private

  def set_category
    category_slug = params.fetch(:category)
    @category = Category.where("slug = ? or id = ?", category_slug, category_slug.to_i).includes(:featured_users).first
  end

  def request_is_for_uncategorized?
    params[:category] == Slug.for(SiteSetting.uncategorized_name) ||
      params[:category] == SiteSetting.uncategorized_name ||
      params[:category] == 'uncategorized'
  end

  def build_topic_list_options
    # html format means we need to parse exclude category (aka filter) from the site options top menu
    menu_items = SiteSetting.top_menu_items
    menu_item = menu_items.select { |item| item.query_should_exclude_category?(action_name, params[:format]) }.first

    # exclude_category = 1. from params / 2. parsed from top menu / 3. nil
    return {
      page: params[:page],
      topic_ids: param_to_integer_list(:topic_ids),
      exclude_category: (params[:exclude_category] || menu_item.try(:filter))
    }
  end
end
