require 'logger'


class MashTournamentBuilder
  attr_accessor :tournament, :tournament_model, :round_model
  attr_accessor :map_model, :mash_model

  def initialize(tournament, tournament_model, round_model,
                 map_model, mash_model)
    @tournament = tournament
    @tournament_model = tournament_model
    @round_model = round_model
    @map_model = map_model
    @mash_model = mash_model
    @logger = Logger.new(
      File.expand_path(
        '../log/mash-tournament-builder.log', File.dirname(__FILE__)
      )
    )
  end

  def valid_number_of_contenders?(n_contenders)
    [8, 16, 32, 64, 128].include?(n_contenders)
  end

  def create_rounds_for_contenders(n_contenders)
    if not valid_number_of_contenders?(n_contenders)
      raise StandardError.new(
        "The number of contenders must be 8, 16, 32, 64, or 128!  " +
        "Got '#{n_contenders}'"
      )
    end

    round = 1

    while n_contenders > 1
      create_round(round, n_contenders)
      n_contenders = n_contenders / 2
      round += 1
    end

    @tournament.total_rounds = round - 1
  end

  def fill_in_next_round
    @tournament.rounds.sort{ |a,b| a.number <=> b.number}.each do |round|
      if not round.done?
        return assign_maps_for_round(round)
      end
    end
  end

  private
  def create_round(round_number, n_contenders)
    round_options = {
      :mash_tournament_id => @tournament.id,
      :number => round_number,
      :mash_count => n_contenders / 2
    }
    @logger.info("Creating round #{round_number} of #{n_contenders} contenders " +
                 "with options: #{round_options.inspect}")

    round = @round_model.new(round_options)
    round.save!

    round.mash_count.times do
      @mash_model.new(
        :mash_tournament_id => @tournament.id,
        :mash_tournament_round_id => round.id
      ).save!
    end
  end

  def assign_maps_for_round(round)
    if round.number == 1
      return assign_maps_for_round_one(round)
    end

    previous = @round_model.find_by_mash_tournament_id_and_number(
                  @tournament.id, round.number - 1
                )
    previous_winners = previous.mashes.collect(&:winner_id)
    pool = @map_model.all(
      :order => 'RANDOM()',
      :conditions => ['id in ?', previous_winners]
    )

    filled_in = []

    round.mashes.each do |mash|
      mash.map_a = pool.pop.id
      mash.map_b = pool.pop.id
      mash.save!

      filled_in << mash
    end

    filled_in
  end

  def assign_maps_for_round_one(round)
    pool_options = {
      :order => 'RANDOM()',
      :limit => round.mash_count * 2
    }
    @logger.info("Allocating pool with options: #{pool_options.inspect}")
    pool = @map_model.all(pool_options)

    @logger.info("Populating mashes from pool: #{pool.inspect}")

    filled_in = []

    round.mashes.each do |mash|
      map_a = pool.pop
      map_b = pool.pop
      @logger.info("Assigning `map_a` from #{map_a.inspect}, " +
                  "`map_b` from #{map_b.inspect} to mash #{mash.inspect}")

      mash.map_a = map_a
      mash.map_b = map_b
      mash.save!

      filled_in << mash
    end

    filled_in
  end
end
