/*
Dotty V1.0 - The Dots Game (Les Petits Carreaux)
Copyright (C) 1998 François Pessaux (francois.pessaux@inria.fr)


This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


#include <Common.h>
#include <System/SysAll.h>
#include <UI/UIAll.h>

#include "pilrc.h"
#include "bool.h"
#include "dots.h"
#include "protos.h"


extern unsigned char game_width ;
extern unsigned char game_height ;
extern unsigned char n_vertices, n_edges, n_cells ;
extern struct vertex *vertices ;
extern struct cell *cells ;
extern struct edge *edges ;
extern unsigned char remaining_cells ;
extern unsigned char nb_horizontal_edges ;
extern unsigned char human_turn ;

extern short x_offset ;
extern short y_offset ;

extern unsigned char human_score ;
extern unsigned char pilot_score ;




void merge_group (struct edge *edge)
{
  unsigned char grp0, grp1 ;
  unsigned char v ;

  /* Get edge's vertices groups by side effect */
  edge_get_groups (edge, &grp0, &grp1) ;
  for (v = 0; v < n_vertices; v++) {
    if (vertices[v].group == grp0) vertices[v].group = grp1 ;
  }
}





void scan_grid (void)
{
  unsigned char x, y ;
  unsigned char old_remaining_cells ;
  struct cell *cell ;
  /* Pointer to the score variable according to current player */
  unsigned char *score ;
  /* Owner for won cells according to current player */
  unsigned owner_flag ;

  old_remaining_cells = remaining_cells ;
  if (human_turn) {
    score = &human_score ;
    owner_flag = OWNER_HUMAN ;
  }
  else {
    score = &pilot_score ;
    owner_flag = OWNER_PILOT ;
  }

  for (y = 0; y < game_height; y++) {
    for (x = 0; x < game_width; x++) {
      cell = &cells[y * game_width + x] ;
      if ((cell_weight (cell) == 4) && (! cell->owner)) {
	/* This cell has just been won */
	cell->owner = owner_flag ;
	/* When 0 : no more move */
	remaining_cells-- ;
	(*score)++ ;
	cell_turn_on (x, y, owner_flag) ;
      }
    }
  }

  /* Update score */
  display_score () ;

  /* Check game over */
  if (remaining_cells == 0) {
    FrmCustomAlert (standardInfoAlert, "Game over !", "", "") ;
    return ;
  }

  /* Check whether the same player must play again */
  if (old_remaining_cells == remaining_cells) {
    /* Player didn't won any cell so next player to play */
    if (human_turn) {
      /* If human played, now its computer's turn */
      human_turn = FALSE ;
      /* Tells Pilot to play */
      send_computer_play_event () ;
    }
    else {
      /* Otherwise, it's human's turn */
      human_turn = TRUE ;
    }
  }
  else {
    /* Current player won cells, so it's still his turn */
    if (! human_turn) {
      /* If the computer played, it's still its turn */
      send_computer_play_event () ;
    }
  }
}





void handle_computer_think (void)
{
  unsigned char m ;
  unsigned char best ;
  unsigned char *connect ;

  /* Take cells if possible */
  for (m = 0; m<n_cells; m++) {
    if (cell_weight (&cells[m]) == 3) {
      unsigned char edge_num ;

      edge_num = cell_open_side (&cells[m]) ;
      /* Turns on the flag in the data structure */
      edges[edge_num].drawn = TRUE ;
      /* Draw the graphics part */
      edge_turn_on (edge_num) ;
      merge_group (&edges[edge_num]) ;
      scan_grid () ;
      return ;
    }
  }

  /* Count group connections */
  connect = (unsigned char*) MemPtrNew (n_vertices * n_vertices *
					sizeof (unsigned char)) ;
  /* Init connection array to 0 */
  { unsigned char v, w ;
  for (v=0 ; v<n_vertices ; v++) {
    for (w=0 ; w<=v ; w++) connect[w*n_vertices + v] = 0 ;
  }}

  { unsigned char grp0, grp1 ;
  for (m = 0; m < n_edges; m++) {
    edge_get_groups (&edges[m], &grp0, &grp1) ;
    connect[(grp1 * n_vertices + grp0)]++ ;
  }}

  /* Find how many cells we have to concede */
  best = n_vertices ;
  { unsigned char v, w ;
  for (v = 1; v < n_vertices; v++) {
    for (w = 0; w < v; w++) {
      if ((connect[w * n_vertices + v] > 0)
	  && (best > connect[w * n_vertices + v]))
	best = connect[w * n_vertices + v] ;
    }
  }}

  /* Pick a move that concedes the minimum */
  { unsigned char r, grp0, grp1 ;
  r = SysRandom (0) % n_edges ;
  for (m = 0; m < n_edges; m++) {
    if (! edges[r].drawn) {
      edge_get_groups (&edges[r], &grp0, &grp1) ;
      if (connect[grp1 * n_vertices + grp0] == best) {
	/* Turns on the flag in the data structure */
	edges[r].drawn = TRUE ;
	/* Draw the graphics part */
        edge_turn_on (r) ;
        merge_group (&edges[r]) ;
	/* Don't forget to free the array connect */
	MemPtrFree (connect) ;
	scan_grid () ;
	return ;
      }
    }
    r++ ;
    if (r >= n_edges) r = 0 ;
  }}
  /* There no more move to do. Should not occurs because we must */
  /* check the game over before making the computer playing.     */
}




void handle_human_play (short x_coord, short y_coord)
{
  unsigned char edge_num ;
  unsigned char mod_x, mod_y ;

#ifdef DEBUG
  char debug[30] ;
  RectangleType rect = { 20, 130, 140, 30 } ;
  WinEraseRectangle (&rect, 0) ;
#endif

  /* Compensate screen centering */
  x_coord -= x_offset ;
  y_coord -= y_offset ;

#ifdef DEBUG
  StrPrintF (debug, "x=%d y=%d", x_coord, y_coord) ;
  WinDrawChars (debug, StrLen (debug), 20, 130) ;
#endif

 if ((x_coord >= 0) && (x_coord < (BLOCK_SIZE_X * game_width)) &&
     (y_coord >= 0) && (y_coord < (BLOCK_SIZE_Y * game_height))) {

   mod_x = (unsigned char) (x_coord % BLOCK_SIZE_X) ;
   mod_y = (unsigned char) (y_coord % BLOCK_SIZE_Y) ;

#ifdef DEBUG
   StrPrintF (debug, "xmod=%d ymod=%d", mod_x, mod_y) ;
   WinDrawChars (debug, StrLen (debug), 70, 130) ;
#endif

   if (mod_x < POINT_TOLERANCE) {
     /* Vertical edge pointed too right */
#ifdef DEBUG
     StrCopy (debug, "V too right") ;
     WinDrawChars (debug, StrLen (debug), 20, 140) ;
#endif
     x_coord -= mod_x ;
     edge_num = nb_horizontal_edges + (y_coord / BLOCK_SIZE_Y)
                * (game_width+1) + (x_coord / BLOCK_SIZE_X) ;
   }
   else if (mod_x > (BLOCK_SIZE_X - POINT_TOLERANCE)) {
     /* Vertical edge pointed too left */
#ifdef DEBUG
     StrCopy (debug, "V too left") ;
     WinDrawChars (debug, StrLen (debug), 20, 140) ;
#endif
     x_coord += (BLOCK_SIZE_X - mod_x) ;
     edge_num = nb_horizontal_edges + (y_coord / BLOCK_SIZE_Y)
       * (game_width+1) + (x_coord / BLOCK_SIZE_X) ;
   }
   else if (mod_y < POINT_TOLERANCE) {
     /* Horizontal edge too bottom */
#ifdef DEBUG
     StrCopy (debug, "H too bottom") ;
     WinDrawChars (debug, StrLen (debug), 20, 140) ;
#endif
     y_coord -= mod_y ;
     edge_num = (y_coord / BLOCK_SIZE_Y) * game_width
                + (x_coord / BLOCK_SIZE_X) ;
   }
   else if (mod_y > (BLOCK_SIZE_Y - POINT_TOLERANCE)) {
     /* Horizontal edge too top */
#ifdef DEBUG
     StrCopy (debug, "H too top") ;
     WinDrawChars (debug, StrLen (debug), 20, 140) ;
#endif
     y_coord += (BLOCK_SIZE_Y - mod_y) ;
     edge_num = (y_coord / BLOCK_SIZE_Y) * game_width
       + (x_coord / BLOCK_SIZE_X) ;
   }
   else {
     /* Undeterminate so do nothing */
#ifdef DEBUG
     StrCopy (debug, "Undeterminate") ;
     WinDrawChars (debug, StrLen (debug), 20, 140) ;
#endif
     return ;
   }

#ifdef DEBUG
   StrPrintF (debug, "edge=%d", edge_num) ;
   WinDrawChars (debug, StrLen (debug), 20, 150) ;
#endif
   /* Do something if the edge is not already drawn */
   if (! edges[edge_num].drawn) {
#ifdef DEBUG
     StrCopy (debug, "Ok to draw") ;
     WinDrawChars (debug, StrLen (debug), 80, 150) ;
#endif
     /* Turns on the flag in the data structure */
     edges[edge_num].drawn = TRUE ;
     /* Now the graphics part */
     edge_turn_on (edge_num) ;
     merge_group (&edges[edge_num]) ;
     scan_grid () ;
   }
   else {
#ifdef DEBUG
     StrCopy (debug, "Bad : already") ;
     WinDrawChars (debug, StrLen (debug), 80, 150) ;
#endif
   }
 }
#ifdef DEBUG
 else {
   StrCopy (debug, "Out of grid") ;
   WinDrawChars (debug, StrLen (debug), 20, 140) ;
 }
#endif
}



void send_computer_play_event (void)
{
  EventType event;

  MemSet (&event, sizeof(EventType), 0) ;

  event.eType = dotComputerMustPlay ;
  EvtAddEventToQueue (&event) ;
}


void display_score (void)
{
  /* Typical form: "Pilot:12  Human:12" */
  char score_buffer[19] ;
  RectangleType rect = { 80, 1, 80, 12 } ;

  WinEraseRectangle (&rect, 0) ;
  StrPrintF (score_buffer, "Pilot:%d  Human:%d", pilot_score, human_score) ;
  WinDrawChars (score_buffer, StrLen (score_buffer), 80, 1) ;
}
