package org.ggp.base.player.gamer;

import java.util.ArrayList;
import java.util.List;

import org.ggp.base.apps.player.config.ConfigPanel;
import org.ggp.base.apps.player.config.EmptyConfigPanel;
import org.ggp.base.apps.player.detail.DetailPanel;
import org.ggp.base.apps.player.detail.EmptyDetailPanel;
import org.ggp.base.player.gamer.exception.AbortingException;
import org.ggp.base.player.gamer.exception.GamePreviewException;
import org.ggp.base.player.gamer.exception.MetaGamingException;
import org.ggp.base.player.gamer.exception.MoveSelectionException;
import org.ggp.base.player.gamer.exception.StoppingException;
import org.ggp.base.util.game.Game;
import org.ggp.base.util.gdl.grammar.GdlConstant;
import org.ggp.base.util.gdl.grammar.GdlTerm;
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.observer.Subject;


/**
 * The Gamer class defines methods for both meta-gaming and move selection in a
 * pre-specified amount of time. The Gamer class is based on the <i>algorithm</i>
 * design pattern.
 */
public abstract class Gamer implements Subject
{
    private Match match;
    private GdlConstant roleName;

    public Gamer()
    {
        observers = new ArrayList<Observer>();

        // When not playing a match, the variables 'match'
        // and 'roleName' should be NULL. This indicates that
        // the player is available for starting a new match.
        match = null;
        roleName = null;
    }

    /* The following values are recommendations to the implementations
     * for the minimum length of time to leave between the stated timeout
     * and when you actually return from metaGame and selectMove. They are
     * stored here so they can be shared amongst all Gamers. */
    public static final long PREFERRED_METAGAME_BUFFER = 3900;
    public static final long PREFERRED_PLAY_BUFFER = 1900;

    // ==== The Gaming Algorithms ====
    public abstract void metaGame(long timeout) throws MetaGamingException;

    public abstract GdlTerm selectMove(long timeout) throws MoveSelectionException;

    /* Note that the match's goal values will not necessarily be known when
     * stop() is called, as we only know the final set of moves and haven't
     * interpreted them yet. To get the final goal values, process the final
     * moves of the game.
     */
    public abstract void stop() throws StoppingException;  // Cleanly stop playing the match

    public abstract void abort() throws AbortingException;  // Abruptly stop playing the match

    public abstract void preview(Game g, long timeout) throws GamePreviewException;  // Preview a game

    // ==== Gamer Profile and Configuration ====
    public abstract String getName();
    public String getSpecies() { return null; }

    public boolean isComputerPlayer() {
        return true;
    }

    public ConfigPanel getConfigPanel() {
        return new EmptyConfigPanel();
    }

    public DetailPanel getDetailPanel() {
        return new EmptyDetailPanel();
    }

    // ==== Accessors ====
    public final Match getMatch() {
        return match;
    }

    public final void setMatch(Match match) {
        this.match = match;
    }

    public final GdlConstant getRoleName() {
        return roleName;
    }

    public final void setRoleName(GdlConstant roleName) {
        this.roleName = roleName;
    }

    // ==== Observer Stuff ====
    private final List<Observer> observers;
    @Override
    public final void addObserver(Observer observer)
    {
        observers.add(observer);
    }

    @Override
    public final void notifyObservers(Event event)
    {
        for (Observer observer : observers) {
            observer.observe(event);
        }
    }
}