/*
 * Atom-4 game engine
 * Header file
 * ---------------------------------------------------------------------------
 * $Id: game.h,v 1.20 2003/04/15 00:24:21 hsteoh Exp hsteoh $
 *
 * DESIGN
 *
 * This game engine is supposed to be UI-driven; so it is basically designed
 * for an event-driven interface. The UI, whether the testing ncurses-based
 * UI or the anticipated X11 UI, will be sending events to class atom4 and
 * checking its state to know what to do next, etc.. Basically, this class
 * implements the game rules.
 */

#ifndef GAME_H
#define GAME_H

#include <list.h>			// MUST be prog/lib version!
#include "board4.h"
#include "color4.h"
#include "triboard.h"


#define STALEMATE		3	// returned by atom4::winner()

#define NUM_COLORS		8
#define NUM_PLAYERS		2

#define WIN_ROW			4	// number of pieces in a row to win

// Internal stuff
#define COLORSEQ_LEN		6


// Callback class for handling allowing async notification of game state
// changes
class atom4;				// forward decl
class atom4notifier {
public:
  // Notify of all game state changes
  virtual void notify_move(atom4 *src, int plr, elist<boardchange> &chg)=0;
  virtual void notify_clear(atom4 *src)=0;
};

// Abstract base class
class atom4 {
  elist<atom4notifier*> notifylist;
protected:
  // Send notification to all registered notifiers. Derived classes must
  // call this every time a game state change is detected.
  virtual void notify_move(int player, elist<boardchange> &changes);
  virtual void notify_clear();
public:
  enum mode_t {
    DUAL,				// both players connected to interface
    PEER,				// one player connected to interface
    WATCH				//  not actively participating
  };

  virtual ~atom4();
  virtual void reset()=0;		// restart game

  virtual mode_t game_mode()=0;		// return current mode
  virtual int local_playernum()=0;	// assigned player number if in PEER
					// mode; current_player() if DUAL mode,
					// -1 if WATCH mode.
  virtual int is_local_turn()=0;
  void add_notifier(atom4notifier *callback);		// [R]
  void remove_notifier(atom4notifier *callback);	// [R]

  virtual int current_player()=0;
  virtual celltype current_tile()=0;
  
  // Returns false if move is illegal (not player's turn, bad position, game
  // already over, etc.), true otherwise.
  virtual int move(int player, int x, int y)=0;
  
  // Checks if move is legal (note: this does NOT guarantee a subsequent
  // call to move() will be successful, since this only checks the board
  // not the current state of the game)
  virtual int check_legal(int x, int y)=0;

  virtual int round_over()=0;
  virtual int winner()=0;

  // Multi-round support
  virtual int current_round()=0;
  virtual int newround()=0;
  virtual int score(int player)=0;

  // FIXME: what we REALLY want is to have a way to register an update
  // callback with class triboard, that will get called every time a cell
  // changes. (This is the REAL answer to the whole mess in the X11 refresh()
  // code right now...)
  virtual board4 *get_board()=0;
  virtual unsigned int board_width()=0;
  virtual unsigned int board_height()=0;
};

class atom4local : public atom4 {
  board4 *board;			// [O] game board

  int win_player;			// player who won the game; if not -1,
					// game is over.
  int cur_player;			// player to make the next move
  int cur_color;			// which color is being played
  static celltype colorseq[COLORSEQ_LEN];

  // Multi-round stuff
  int cur_round;			// current round
  int start_player;			// player to move first
  int scores[NUM_PLAYERS];

  // Internal convenience stuff
  inline char plchar(int player) { return 'a' + player-1; }
  inline int next_player(int player) { return player%NUM_PLAYERS + 1; }
  inline int valid_player(int pl) { return pl>0 && pl<=NUM_PLAYERS; }

  inline int next_color(int color) { return (color+1)%COLORSEQ_LEN; }

  int splash(int bx, int by, color4 actor, elist<boardchange> &changes);
  void reset_scores();
  void reset_board();
public:
  atom4local(unsigned int width, unsigned int height);
  virtual ~atom4local();
  virtual void reset();			// restart game

  virtual mode_t game_mode();		// return current mode
  virtual int local_playernum();
  virtual int is_local_turn();

  // For UI to know who's supposed to move next. UI should not need to keep
  // track of this independently anyway.
  int current_player();

  // Current marble (which color) being played
  celltype current_tile();

  // Returns false if move is illegal (not player's turn, bad position, game
  // already over, etc.), true otherwise.
  int move(int player, int x, int y);

  // Checks if move is legal (note: this does NOT guarantee a subsequent
  // call to move() will be successful, since this only checks the board
  // not the current state of the game)
  int check_legal(int x, int y);

  int round_over();
  int winner();

  // Multi-round support
  int current_round();
  virtual int newround();
  int score(int player);

  // FIXME: what we REALLY want is to have a way to register an update
  // callback with class triboard, that will get called every time a cell
  // changes. (This is the REAL answer to the whole mess in the X11 refresh()
  // code right now...)
  board4 *get_board();
  unsigned int board_width();
  unsigned int board_height();
};


#endif // GAME_H
