package org.ggp.base.apps.server.leaderboard;

import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.ScrollPaneConstants;
import javax.swing.SortOrder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import org.ggp.base.server.event.ServerMatchUpdatedEvent;
import org.ggp.base.util.match.Match;
import org.ggp.base.util.observer.Event;
import org.ggp.base.util.observer.Observer;
import org.ggp.base.util.ui.JLabelBold;

@SuppressWarnings("serial")
public final class LeaderboardPanel extends JPanel implements Observer
{
    private final JTable leaderTable;
    private final TableRowSorter<TableModel> sorter;

    public LeaderboardPanel()
    {
        super(new BorderLayout());

        DefaultTableModel model = new DefaultTableModel();
        model.addColumn("Player");
        model.addColumn("Score");
        model.addColumn("Errors");

        leaderTable = new JTable(model)
        {
            @Override
            public boolean isCellEditable(int rowIndex, int colIndex)
            {
                return false;
            }
            @Override
            public Class<?> getColumnClass(int colIndex) {
                if (colIndex == 0) return String.class;
                if (colIndex == 1) return Integer.class;
                if (colIndex == 2) return Integer.class;
                return Object.class;
            }
        };
        leaderTable.setShowHorizontalLines(true);
        leaderTable.setShowVerticalLines(true);
        leaderTable.getColumnModel().getColumn(0).setPreferredWidth(40);
        leaderTable.getColumnModel().getColumn(1).setPreferredWidth(10);
        leaderTable.getColumnModel().getColumn(2).setPreferredWidth(10);
        sorter = new TableRowSorter<TableModel>(model);
        sorter.setComparator(1, new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return a-b;
            }
        });
        sorter.setSortKeys(Arrays.asList(new SortKey[]{new SortKey(1, SortOrder.DESCENDING)}));
        leaderTable.setRowSorter(sorter);

        add(new JLabelBold("Leaderboard"), BorderLayout.NORTH);
        add(new JScrollPane(leaderTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER);
    }

    @Override
    public void observe(Event event)
    {
        if (!(event instanceof ServerMatchUpdatedEvent)) return;
        Match match = ((ServerMatchUpdatedEvent) event).getMatch();

        if (!match.isCompleted()) return;
        if (match.getMatchId().startsWith("Test")) return;

        List<Integer> goals = match.getGoalValues();
        List<Integer> errors = getErrorCounts(match.getErrorHistory());
        List<String> players = match.getPlayerNamesFromHost();
        for (int i = 0; i < players.size(); i++) { if (players.get(i)==null) { players.set(i, "?"); } }

        Set<String> playersToAdd = new HashSet<String>(players);
        DefaultTableModel model = (DefaultTableModel) leaderTable.getModel();
        for (int i = 0; i < model.getRowCount(); i++) {
            String rowPlayer = model.getValueAt(i, 0).toString();
            int playerIndex = players.indexOf(rowPlayer);
            if (playerIndex != -1) {
                int oldScore = (Integer)model.getValueAt(i, 1);
                int oldErrors = (Integer)model.getValueAt(i, 2);
                model.setValueAt(oldScore + goals.get(playerIndex), i, 1);
                model.setValueAt(oldErrors + errors.get(playerIndex), i, 2);
                playersToAdd.remove(rowPlayer);
            }
        }
        for (String playerToAdd : playersToAdd) {
            model.addRow(new Object[]{playerToAdd, goals.get(players.indexOf(playerToAdd)), errors.get(players.indexOf(playerToAdd))});
        }
        sorter.sort();
    }

    public static List<Integer> getErrorCounts(List<List<String>> errorHistory) {
        List<Integer> errorCounts = new ArrayList<Integer>();
        for (int i = 0; i < errorHistory.get(0).size(); i++) {
            errorCounts.add(0);
        }
        for (List<String> errorHistoryEntry : errorHistory) {
            for (int i = 0; i < errorHistoryEntry.size(); i++) {
                if (!errorHistoryEntry.get(i).isEmpty()) {
                    errorCounts.set(i,1+errorCounts.get(i));
                }
            }
        }
        return errorCounts;
    }
}
