/* MAIN.C - XTron main file

   Rhett D. Jacobs   <rhett@jaemol.cbr.fidonet.org>

   Version 1.0

  Last Modified: 2/1/95
*/

#ifndef MAIN_H
#include "main.h"
#endif


void ButtonEvent(XButtonEvent *pEvent)
{  
  /* player 1 options check */
  if (pEvent->subwindow == plr1_human)
    /* if in computer mode, switch to human */ 
    if (p[0].plr_type == computer) {
      p[0].plr_type = human;
      XCopyPlane(display, human_rev, plr1_human, gc, 0, 0,
		 human_width, human_height, 0, 0, 0x01);
      XCopyPlane(display, computer_pic, plr1_computer, gc, 0, 0,
		 computer_width, computer_height, 0, 0, 0x01);
    }

  if (pEvent->subwindow == plr1_computer)
    /* if in human mode, switch to computer */ 
    if (p[0].plr_type == human) {
      p[0].plr_type = computer;
      XCopyPlane(display, human_pic, plr1_human, gc, 0, 0,
		 human_width, human_height, 0, 0, 0x01);
      XCopyPlane(display, computer_rev, plr1_computer, gc, 0, 0,
		 computer_width, computer_height, 0, 0, 0x01);
    }

  /* player 2 options check */
  if (pEvent->subwindow == plr2_human)
    /* if in computer mode, switch to human */ 
    if (p[1].plr_type == computer) {
      p[1].plr_type = human;
      XCopyPlane(display, human_rev, plr2_human, gc, 0, 0,
		 human_width, human_height, 0, 0, 0x01);
      XCopyPlane(display, computer_pic, plr2_computer, gc, 0, 0,
		 computer_width, computer_height, 0, 0, 0x01);
    }

  if (pEvent->subwindow == plr2_computer)
    /* if in human mode, switch to computer */ 
    if (p[1].plr_type == human) {
      p[1].plr_type = computer;
      XCopyPlane(display, human_pic, plr2_human, gc, 0, 0,
		 human_width, human_height, 0, 0, 0x01);
      XCopyPlane(display, computer_rev, plr2_computer, gc, 0, 0,
		 computer_width, computer_height, 0, 0, 0x01);
    }

  /* play again */
  if(pEvent->subwindow == again_win)
    restart_game();

  /* pause button */
  if(pEvent->subwindow == pause_win)
    if (p_state)
      p_state = 0;
    else
      p_state = 1;
  
  /* quit */
  if (pEvent->subwindow == quit_win)
    win_shutdown();
}


void KeyEvent(XKeyEvent *pEvent)
{
  int key_buffer_size = 64;
  char key_buffer[65];
  XComposeStatus compose_status;
  KeySym key_sym;

  XLookupString(pEvent, key_buffer, key_buffer_size,
		&key_sym, &compose_status);
  
  if (p[0].plr_type == human)
    switch (key_sym) {
    case XK_w:
    case XK_W:
      plr_turn(0,up);
      break;
    case XK_x:
    case XK_X:
    case XK_s:
    case XK_S:
      plr_turn(0,down);
      break;
    case XK_a:
    case XK_A:
      plr_turn(0,left);
      break;
    case XK_d:
    case XK_D:
      plr_turn(0,right);
      break;
    }
  if (p[1].plr_type == human)
    switch (key_sym) {
    case XK_Up:
    case XK_KP_8:
      plr_turn(1,up);
      break;
    case XK_Down:
    case XK_KP_5:
    case XK_KP_2:
      plr_turn(1,down);
      break;
    case XK_Left:
    case XK_KP_4:
      plr_turn(1,left);
      break;
    case XK_Right:
    case XK_KP_6:
      plr_turn(1,right);
      break;
    } 
}


void ExposeEvent(XExposeEvent *pEvent)
{
  int i, ic, j, jc;
  char score1[28], score2[28];

  /* remove unused events etc... */
  XSync(display,1);

  /* background */
  for(i = 0; i < TOTAL_WIDTH; i+=back_width)
    for(j = 0; j < TOTAL_HEIGHT; j+=back_height)
      XCopyPlane(display, board_im, game_port, gc, 0, 0,
		 back_width, back_height, i, j, 0x01);
		
  /* refresh board */
  for (i = 0, ic = 0; i < TOTAL_WIDTH; i+=p1_width, ic++)
    for(j = 0, jc = 0; j < TOTAL_WIDTH; j+=p1_height, jc++)
      switch (b.contents[ic][jc]) {
      case 1:
	/* player 1 */
	XCopyPlane(display, p_im[0], game_port, gc, 0, 0,
		   p1_width, p1_height, i, j, 0x01);
	break;
      case 2:
	/* player 2 */
	XCopyPlane(display, p_im[1], game_port, gc, 0, 0,
		   p2_width, p2_height, i, j, 0x01);
	break;
      }
  
  /* human and computer icons */
  /* check player setting to highlight appropriate window */
  if(p[0].plr_type == human) {
    XCopyPlane(display, human_rev, plr1_human, gc, 0, 0,
	       human_width, human_height, 0, 0, 0x01);
    XCopyPlane(display, computer_pic, plr1_computer, gc, 0, 0,
	       computer_width, computer_height, 0, 0, 0x01);
  } else {
    XCopyPlane(display, human_pic, plr1_human, gc, 0, 0,
	       human_width, human_height, 0, 0, 0x01);
    XCopyPlane(display, computer_rev, plr1_computer, gc, 0, 0,
	       computer_width, computer_height, 0, 0, 0x01);
  }
  
  /* check player setting to highlight appropriate window */
  if(p[1].plr_type == human) {
    XCopyPlane(display, human_rev, plr2_human, gc, 0, 0,
	       human_width, human_height, 0, 0, 0x01);
    XCopyPlane(display, computer_pic, plr2_computer, gc, 0, 0,
	       computer_width, computer_height, 0, 0, 0x01);
  } else {
    XCopyPlane(display, human_pic, plr2_human, gc, 0, 0,
	       human_width, human_height, 0, 0, 0x01);
    XCopyPlane(display, computer_rev, plr2_computer, gc, 0, 0,
	       computer_width, computer_height, 0, 0, 0x01);
  }
  
  /* again icon */
  XCopyPlane(display, again_pic, again_win, gc, 0, 0,
	     again_width, again_height, 0, 0, 0x01);
  
  /* pause icon */
  XCopyPlane(display, pause_pic, pause_win, gc, 0, 0,
	     pause_width, pause_height, 0, 0, 0x01);
  
  /* quit icon */
  XCopyPlane(display, quit_pic, quit_win, gc, 0, 0,
	     quit_width, quit_height, 0, 0, 0x01);  

  /* output current scores */
  sprintf(score1,"Player 1 - Score: %d",p[0].score);
  sprintf(score2,"Player 2 - Score: %d",p[1].score);

  XDrawImageString(display, plr1_win, gc, 10, 20, score1, strlen(score1));
  XDrawImageString(display, plr2_win, gc, 10, 20, score2, strlen(score2));
}


Window set_window(int x, int y, int width, int height)
{
  return(win_open(x, y, width, height, 1, main_window, 0, 0, 
		  NULL, NULL));
}


void mapwindows(void)
{
  XMapWindow(display,main_window);
  XMapWindow(display,game_port);
  XMapWindow(display,plr1_win);
  XMapWindow(display,plr2_win);
  XMapWindow(display,again_win);
  XMapWindow(display,pause_win);
  XMapWindow(display,quit_win);
  XMapWindow(display,plr1_human);
  XMapWindow(display,plr2_human);
  XMapWindow(display,plr1_computer);
  XMapWindow(display,plr2_computer);
}


Pixmap set_icon(char *bits, unsigned int width, unsigned int height)
{
  return(XCreateBitmapFromData(display, main_window, bits, width, height));
}


int check_valid(int p_num, int x_inc, int y_inc)
{
  if (b.contents[p[p_num].co_ords[0]+x_inc][p[p_num].co_ords[1]+y_inc] != 0)
    return (0);
  
  if (y_inc != 0) {
    if ((p[p_num].co_ords[1]+y_inc) < MAXVERT &&
	(p[p_num].co_ords[1]+y_inc)  >= MINVERT)  
      return (1);
  } else {
    if (x_inc != 0)
      if ((p[p_num].co_ords[0]+x_inc) < MAXHORZ &&
	  (p[p_num].co_ords[0]+x_inc) >= MINHORZ)
	return (1);
  }
  return(0);
}  


/* artificial intelligence routines for computer player */
void think(int p_num)
{
  enum directions sides[2];
  int flags[6] = {0,0,0,0,0,0};
  int index[2];
  int dis_forward,  dis_left, dis_right;
 
  dis_forward = dis_left = dis_right = 1;

  switch (p[p_num].plr_dir) {
  case left:
    /* forward flags */
    flags[0] = -1;
    flags[1] = 0;

    /* left flags */
    flags[2] = 0;
    flags[3] = 1;

    /* right flags */
    flags[4] = 0;
    flags[5] = -1;

    /* turns to either side */
    sides[0] = down;
    sides[1] = up;
    break;
  case right:
    flags[0] = 1;
    flags[1] = 0;
    flags[2] = 0;
    flags[3] = -1;
    flags[4] = 0;
    flags[5] = 1;
    sides[0] = up;
    sides[1] = down;
    break;
  case up:
    flags[0] = 0;
    flags[1] = -1;
    flags[2] = -1;
    flags[3] = 0;
    flags[4] = 1;
    flags[5] = 0;
    sides[0] = left;
    sides[1] = right;
    break;
  case down:
    flags[0] = 0;
    flags[1] = 1;
    flags[2] = 1;
    flags[3] = 0;
    flags[4] = -1;
    flags[5] = 0;
    sides[0] = right;
    sides[1] = left;
    break;
  }

  /* check forward */
  index[0] = p[p_num].co_ords[0]+flags[0];
  index[1] = p[p_num].co_ords[1]+flags[1];
  while (index[0] < MAXHORZ && index[0] >= MINHORZ &&
	 index[1] < MAXVERT && index[1] >= MINVERT &&
	 b.contents[index[0]][index[1]] == 0) {
    dis_forward++;
    index[0] += flags[0];
    index[1] += flags[1];
  }
 
  /* check left */
  index[0] = p[p_num].co_ords[0]+flags[2];
  index[1] = p[p_num].co_ords[1]+flags[3];
  while (index[0] < MAXHORZ && index[0] >= MINHORZ &&
	 index[1] < MAXVERT && index[1] >= MINVERT &&
	 b.contents[index[0]][index[1]] == 0) {
    dis_left++;
    index[0] += flags[2];
    index[1] += flags[3];
  } 

  /* check right */
  index[0] = p[p_num].co_ords[0]+flags[4];
  index[1] = p[p_num].co_ords[1]+flags[5];
  while (index[0] < MAXHORZ && index[0] >= MINHORZ &&
	 index[1] < MAXVERT && index[1] >= MINVERT &&
	 b.contents[index[0]][index[1]] == 0) {
    dis_right++;
    index[0] += flags[4];
    index[1] += flags[5];
  } 
 
  if (dis_forward < LOOK_AHEAD) {
    dis_forward = 100 - 100/dis_forward;
    if(!(dis_left == 1 && dis_right == 1))
      if ((int)rand()%100 >= dis_forward || dis_forward == 0) {
       
	/* change direction */
	if ((int)rand()%100 <= (100*dis_left)/(dis_left+dis_right))
	  if (dis_left != 1)
	    /* turn to the left */
	    p[p_num].plr_dir = sides[0];
	  else
	    /* turn to the right */
	    p[p_num].plr_dir = sides[1];
	else
	  if (dis_right != 1)
	    /*  turn to the right */
	    p[p_num].plr_dir = sides[1];
	  else
	    /* turn to the left */
	    p[p_num].plr_dir = sides[0];
      }
  }
}


int game_update(void)
{
  int i, ic, j , jc;
  int x_inc = 0, y_inc = 0;

  for (i = 0; i< 2; i++) {
    if (p[i].plr_type == computer)
      think(i);
    switch (p[i].plr_dir) {
    case left:
      if (check_valid(i,-1,0))
	p[i].co_ords[0]--;
      else
	p[i].alive = 0;
      break;
    case right:
      if (check_valid(i,1,0))
	p[i].co_ords[0]++;
      else
	p[i].alive = 0;
      break;
    case up:
      if (check_valid(i,0,-1))
	p[i].co_ords[1]--;
      else
	p[i].alive = 0;
      break;
    case down:
      if (check_valid(i,0,1))
	p[i].co_ords[1]++;
      else
	p[i].alive = 0;
      break;
    }
    b.contents[p[i].co_ords[0]][p[i].co_ords[1]] = i+1;
  }
  XCopyPlane(display, p_im[0], game_port, gc, 0, 0, p1_width, p1_height, 
	     p[0].co_ords[0]*p1_width, p[0].co_ords[1]*p1_height, 0x01);  
  XCopyPlane(display, p_im[1], game_port, gc, 0, 0, p2_width, p2_height, 
	     p[1].co_ords[0]*p2_width, p[1].co_ords[1]*p2_height, 0x01);

  /* player collision check */
  if(!p[1].alive) { 
    switch(p[1].plr_dir) {
    case left:
      x_inc = -1; break;
    case right:
      x_inc = 1; break;
    case up:
      y_inc = -1; break;
    case down:
      y_inc = 1; break;
    }
    if ((p[1].co_ords[0]+x_inc) == p[0].co_ords[0])   
      if ((p[1].co_ords[1]+y_inc) == p[0].co_ords[1]) {
	for(i = 0, ic = 0; i < TOTAL_WIDTH; i+=p1_width, ic++)
	  for(j = 0, jc = 0; j < TOTAL_WIDTH; j+=p1_height, jc++)
	    if (b.contents[ic][jc])
	      XCopyPlane(display, p_im[2], game_port, gc, 0, 0,
			 p3_width, p3_height, i, j, 0x01);
	return(0);
      }
  }

  if(!p[0].alive && !p[1].alive) {
    for(i = 0, ic = 0; i < TOTAL_WIDTH; i+=p1_width, ic++)
      for(j = 0, jc = 0; j < TOTAL_WIDTH; j+=p1_height, jc++)
	if (b.contents[ic][jc])
	  XCopyPlane(display, p_im[2], game_port, gc, 0, 0,
		     p3_width, p3_height, i, j, 0x01);
    return(0);
  }
  
  if (!p[0].alive) {
    /* player 1 dead */
    p[1].score++;
    for(i = 0, ic = 0; i < TOTAL_WIDTH; i+=p1_width, ic++)
      for(j = 0, jc = 0; j < TOTAL_WIDTH; j+=p1_height, jc++)
	if (b.contents[ic][jc] == 1)
	  XCopyPlane(display, p_im[1], game_port, gc, 0, 0,
		     p1_width, p1_height, i, j, 0x01);
    return(0);
  }
  if (!p[1].alive) {
    /* player 2 dead */
    p[0].score++;
    for(i = 0, ic = 0; i < TOTAL_WIDTH; i+=p1_width, ic++)
      for(j = 0, jc = 0; j < TOTAL_WIDTH; j+=p1_height, jc++)
	if (b.contents[ic][jc] == 2)
	  XCopyPlane(display, p_im[0], game_port, gc, 0, 0,
		     p1_width, p1_height, i, j, 0x01);
    return(0);
  }		       
  return(1);
}


void restart_game(void)
{
  int i;
  XEvent event;

  p[0].plr_dir = left;
  p[1].plr_dir = right;
  for (i=0; i<2; i++) {
    p[i].alive = 1;
    p[i].co_ords[1] = MAXVERT/2;
  }
  p[0].co_ords[0] = (MAXHORZ/2)-3;
  p[1].co_ords[0] = (MAXHORZ/2)+3;
  brd_setup();
  ExposeEvent(&event.xexpose);
}


void open_windows(int argc, char **argv)
{
  main_window = win_open(0, 0, TOTAL_WIDTH, TOTAL_HEIGHT, 5,
 			 DefaultRootWindow(display), 1, argc, argv,
			 "XTron - Version 1.0");
  game_port = set_window(0, 1, TOTAL_WIDTH, TOTAL_WIDTH);
  plr1_win = set_window(0, TOTAL_WIDTH, (TOTAL_WIDTH/2),
			((TOTAL_HEIGHT-TOTAL_WIDTH)/2));
  plr2_win = set_window((TOTAL_WIDTH/2), TOTAL_WIDTH,
			(TOTAL_WIDTH/2), ((TOTAL_HEIGHT-TOTAL_WIDTH)/2));
  again_win = set_window(0, (TOTAL_WIDTH+((TOTAL_HEIGHT-TOTAL_WIDTH)/2)),
			 TOTAL_WIDTH, (TOTAL_HEIGHT/16));
  pause_win = set_window(0, (TOTAL_HEIGHT-(TOTAL_HEIGHT/16)),
			 (TOTAL_WIDTH/2), (TOTAL_HEIGHT/16));
  quit_win = set_window((TOTAL_WIDTH/2),	
			(TOTAL_HEIGHT-(TOTAL_HEIGHT/16)),
			(TOTAL_WIDTH/2), (TOTAL_HEIGHT/16));
  plr1_human = set_window(0, (TOTAL_WIDTH+TOTAL_HEIGHT/16),
			  (TOTAL_WIDTH/4), (TOTAL_HEIGHT/16));
  plr1_computer = set_window((TOTAL_WIDTH/4), (TOTAL_WIDTH+TOTAL_HEIGHT/16),
			     (TOTAL_WIDTH/4), (TOTAL_HEIGHT/16));
  plr2_human = set_window((TOTAL_WIDTH/2), (TOTAL_WIDTH+TOTAL_HEIGHT/16),
			  (TOTAL_WIDTH/4), (TOTAL_HEIGHT/16));
  plr2_computer = set_window((TOTAL_WIDTH-TOTAL_WIDTH/4),
			     (TOTAL_WIDTH+TOTAL_HEIGHT/16),
			     (TOTAL_WIDTH/4),(TOTAL_HEIGHT/16));
}


void assign_bitmaps(void) 
{
  /* assign bitmaps used for user interface */
  human_pic = set_icon(human_bits, human_width, human_height);
  computer_pic = set_icon(computer_bits, computer_width, computer_height);
  human_rev = set_icon(humanr_bits, humanr_width, humanr_height);
  computer_rev = set_icon(comprr_bits, comprr_width, comprr_height);
  again_pic = set_icon(again_bits, again_width, again_height);
  pause_pic = set_icon(pause_bits, pause_width, pause_height);
  quit_pic = set_icon(quit_bits, quit_width, quit_height);
  
  /* assign bitmaps used for player pieces */
  p_im[0] = set_icon(p1_bits, p1_width, p1_height);
  p_im[1] = set_icon(p2_bits, p2_width, p2_height);
  p_im[2] = set_icon(p3_bits, p3_width, p3_height);
  board_im = set_icon(back_bits, back_width, back_height);
}


void delay(int len)
{
  struct timeval tm;
  
  tm.tv_sec  = len/1000000;
  tm.tv_usec = len%1000000;
  select(0,0,0,0,&tm);
}


int main (int argc, char *argv[])    
{    
  int i = 0;
  XEvent event; 
  
  win_setup();
  plr_setup();
  brd_setup();
  srand(time(0));
 
  open_windows(argc, argv);
  gc = win_getGC();
  mapwindows();
  assign_bitmaps();

  XNextEvent(display, &event);
  if (event.type == Expose)
    ExposeEvent(&event.xexpose);

  /* game loop */
  for(;;) {
    delay(5000);
    if (p_state)
      if (i++ == 4) {
	if(!(game_update())) {
	  XFlush(display);
	  delay(1500000);
	  restart_game();
	}
	i = 0;
      }
    
    if (XCheckWindowEvent(display,main_window,
			  ExposureMask | KeyPressMask| ButtonPressMask,
			  &event))
      switch (event.type) {
      case Expose:
	ExposeEvent(&event.xexpose); break;
      case KeyPress:
	KeyEvent(&event.xkey); break;
      case ButtonPress:
	ButtonEvent(&event.xbutton); break;
      case MappingNotify:
	XRefreshKeyboardMapping(&event.xmapping); break;
      }
  }
}
