# -*- coding: utf-8 -*-

module LORChess
  class RoundRobinTable

    require 'yaml'

    DATADIR = '2014/2-summertime'
    ROUNDS = 2

    dir = File.dirname(__FILE__)
    players_yaml = File.expand_path("../../#{DATADIR}/players.yml", dir)
    @@db_players = YAML.load_file players_yaml
    results_yaml = File.expand_path("../../#{DATADIR}/results.yml", dir)
    @@db_results = YAML.load_file results_yaml

    @@dim = @@db_players.length
    @@rounds = ROUNDS || 1

    # Sort players in numerical order
    @@db_players.sort! { |x,y| x['number'] <=> y['number'] }

    def initialize
      @players = @@db_players.map { |player| player['lor'] }
      @elo_points = @@db_players.map { |player| player['elo'].to_s }
      @game_scores = Array.new(@@rounds) {
        Array.new(@@dim) { Array.new(@@dim) }
      }
      @player_games = Array.new(@@dim, 0)
      @total_scores = Array.new(@@dim, 0.0)
      @player_places = Array.new(@@dim)
      @berger_coefs = Array.new(@@dim, 0.0)
      @buffer = ''

      # Players withdrew from tournament
      @players_withdrew = @@db_players
        .select { |player| player['status'] == 'withdrew' }
        .map { |player| player['lor'] }

      # Correlate the player with his number
      @player_numbers = {}
      @players.each_with_index { |player, i| @player_numbers[player] = i }

      fill_results
      calculate
      results_to_s

      # Withdraw players from tournament (seppuku)
      @players_withdrew.each do |player|
        num = @player_numbers[player]
        @players[num] = "<del>#{player}</del>"
      end
    end

    def fill_results
      @@db_results.each do |tour|
        if tour['games']
          # zero-based round
          round = (tour['number'] - 1).div(@@dim - 1)

          tour['games'].each do |game|
            import game, round
          end
        end
      end
    end

    def import game, round
      num_white = @player_numbers[game['white']]
      num_black = @player_numbers[game['black']]
      score = game['result'].split ':'

      @game_scores[round][num_white][num_black] = score[0].to_f
      @game_scores[round][num_black][num_white] = score[1].to_f
    end

    def calculate
      @game_scores.each do |round|
        round.each_with_index do |row, index|
          games = 0
          sum = 0.0
          row.each do |score|
            unless score.nil?
              games += 1
              sum += score
            end
          end
          @player_games[index] += games
          @total_scores[index] += sum
        end
      end

      calculate_berger

      player_data = []
      @@dim.times do |i|
        player_data << { :number => i,
                         :total => @total_scores[i],
                         :berger => @berger_coefs[i] }
      end

      # Sort players in the reverse order to Berger coefficient
      player_data.sort! { |x,y| y[:berger] <=> x[:berger] }

      # Sort players in the reverse order to total score by the bubble
      # sorting, keeping the order of Berger coefficients for equal
      # total scores
      (@@dim - 1).times do |i|
        (@@dim - 2).downto(i) do |j|
          if player_data[j][:total] < player_data[j+1][:total]
            data = player_data[j]
            player_data[j] = player_data[j+1]
            player_data[j+1] = data
          end
        end
      end

      player_data.each_with_index do |data, i|
        @player_places[data[:number]] = (i + 1).to_s
      end
    end

    def calculate_berger
      @game_scores.each do |round|
        round.each_with_index do |row, index|
          berger = 0.0
          row.each_with_index do |score, i|
            berger += score * @total_scores[i] unless score.nil?
          end
          @berger_coefs[index] += berger
        end
      end
    end

    def results_to_s
      @game_scores.map! do |round|
        round.map do |row|
          row.map { |cell| stylize_score cell }
        end
      end
      @player_games.map! { |num| num.to_s }
      @total_scores.map! { |score| stylize_score score }
      @berger_coefs.map! { |coef| coef.to_s }
    end

    # Replace the fractional part `0.5' by ½
    def stylize_score score
      return '' if score.nil?
      frac = score.to_s.split '.'
      unless frac[0] == '0'
        str = frac[0]
        str += '½' if frac[1] == '5'
      else
        str = (frac[1] == '5') ? '½' : '0'
      end
      str
    end

    def to_html

      @buffer << "<table class=\"table tournament tablesorter\">\n"
      @buffer << "  <caption><strong>Таблица результатов</strong><caption>\n"
      @buffer << "  <thead>\n"
      @buffer << "    <tr>\n"
      @buffer << "      <th class=\"number\">№</th>\n"
      @buffer << "      <th class=\"player\">Участник</th>\n"
      @buffer << "      <th class=\"elo\">elo*</th>\n"

      @@dim.times do |i|
        @buffer << "      <th class=\"score\" colspan=\"#{@@rounds.to_s}\">#{(i + 1).to_s}</th>\n"
      end

      @buffer << "      <th class=\"games\">Игры</th>\n"
      @buffer << "      <th class=\"total\">Очки</th>\n"
      @buffer << "      <th class=\"place\">Место</th>\n"
      @buffer << "      <th class=\"berger\">Бергер</th>\n"
      @buffer << "    </tr>\n"
      @buffer << "  </thead>\n"
      @buffer << "  <tfoot>\n"
      @buffer << "    <tr>\n"
      @buffer << "      <td colspan=\"#{(@@dim * @@rounds + 7).to_s}\">* Средний elo на начало турнира</td>\n"
      @buffer << "    </tr>\n"
      @buffer << "  </tfoot>\n"
      @buffer << "  <tbody>\n"

      @@dim.times do |i|

        @buffer << "    <tr class=\"place-#{@player_places[i]}\">\n"
        @buffer << "      <td class=\"number\">#{(i + 1).to_s}</td>\n"
        @buffer << "      <td class=\"player\"><strong>#{@players[i]}</strong></td>\n"
        @buffer << "      <td class=\"elo\">#{@elo_points[i]}</td>\n"

        @@dim.times do |j|
          unless j == i
            @@rounds.times do |round|
              @buffer << "      <td class=\"score\">#{@game_scores[round][i][j]}</td>\n"
            end
          else
            @buffer << "      <td class=\"diagonal\"></td>\n" * @@rounds
          end
        end

        @buffer << "      <td class=\"games\">#{@player_games[i]}</td>\n"
        @buffer << "      <td class=\"total\">#{@total_scores[i]}</td>\n"
        @buffer << "      <td class=\"place\">#{@player_places[i]}</td>\n"
        @buffer << "      <td class=\"berger\">#{@berger_coefs[i]}</td>\n"
        @buffer << "    </tr>\n"
      end

      @buffer << "  </tbody>\n"
      @buffer << "</table>\n"
      @buffer
    end

  end
end
