#include<stdio.h>
#include<X11/Intrinsic.h>
#include<X11/StringDefs.h>
#include<X11/Shell.h>
#include<X11/Xaw/Form.h>
#include<X11/Xaw/Label.h>

#define MAP_W 64
#define MAP_H 48


typedef struct{
  int x;
  int y;
  int dir;
  int key;
} dot_t;

/* スピード */
#define SPEED 10

#define MD_TITLE 0
#define MD_READY 1
#define MD_GAME  2
#define MD_OVER  3


static int IntervalState;       /* タイマ処理中? */
static XtIntervalId IntervalId; /* タイマー処理のＩＤ */

static XtAppContext App;   /* アプリケーションコンテクスト */

/* ウィジット */
static Widget Top[2],Base[2],Scr[2],Mesg[2],Score[2][3];

static char* Myname;

static int Mode;

static Display *Disp[2];
static Window Scr_w[2];
static GC Gc_color[2][2];

static int Map[MAP_W][MAP_H];

static dot_t Dot[2];
static int Win[3];

static void set_timer( void );
static void reset_timer( void );

extern char *Res[];


/* 勝利数表示 */

static void show_win( void )
{
  char buf[32];

  sprintf( buf,"Yours : %03d",Win[0] );
  XtVaSetValues( Score[0][0],XtNlabel,buf,NULL );
  sprintf( buf,"His : %03d",Win[1] );
  XtVaSetValues( Score[0][1],XtNlabel,buf,NULL );

  sprintf( buf,"Yours : %03d",Win[1] );
  XtVaSetValues( Score[1][0],XtNlabel,buf,NULL );
  sprintf( buf,"His : %03d",Win[0] );
  XtVaSetValues( Score[1][1],XtNlabel,buf,NULL );

  sprintf( buf,"Draw : %03d",Win[2] );
  XtVaSetValues( Score[0][2],XtNlabel,buf,NULL );
  XtVaSetValues( Score[1][2],XtNlabel,buf,NULL );
}


/* ドット光らせる */

static void light( int x, int y, int p )
{
  Map[x][y] = p+1;
  XFillRectangle( Disp[0],Scr_w[0],Gc_color[0][p],x*8,y*8,8,8 );
  XFillRectangle( Disp[1],Scr_w[1],Gc_color[1][p],
		 (MAP_W-1-x)*8,(MAP_H-1-y)*8,8,8 );
}


/* 爆発 */

static int bomb( int ox, int oy, int p )
{
  int x,y;

  for( y = 0 ; y < MAP_H ; y++ )
    for( x =0 ; x < MAP_W ; x++ )
      if( (x-ox)*(x-ox) + (y-oy)*(y-oy) < 256 )
	if( p == 2 )
	  light( x,y,random()%2 );
	else
	  light( x,y,p );
}


/* 移動 */

static int game( int *win )
{
  static int p = 0;
  static int vx[4] = { 0,1,0,-1 };
  static int vy[4] = { -1,0,1,0 };
  dot_t *dp;

  p = 1-p;

  dp = Dot+p;

  if( dp->key != (dp->dir+2)%4 )
    dp->dir = dp->key;

  dp->x += vx[dp->dir];
  dp->y += vy[dp->dir];

  if( dp->x < 0 || dp->x >= MAP_W || dp->y < 0 || dp->y >= MAP_H
     || Map[dp->x][dp->y] ){
    if( Dot[0].x == Dot[1].x && Dot[0].y == Dot[1].y ){
      *win = 2;
      bomb( dp->x,dp->y,2 );
    }else{
      *win = 1-p;
      bomb( dp->x,dp->y,p );
    }
    return 0;
  }else{
    light( dp->x,dp->y,p );
    return 1;
  }
}


/* マップ初期化 */

static void init_map( void )
{
  int x,y;

  for( y = 0 ; y < MAP_H ; y++ )
    for( x = 0 ; x < MAP_W ; x++ )
      Map[x][y] = 0;
}


/* スタート */

static void init_game( void )
{
  init_map();

  Dot[0].x = 0;
  Dot[0].y = 0;
  Dot[0].dir = 1;
  Dot[0].key = 1;

  Dot[1].x = MAP_W-1;
  Dot[1].y = MAP_H-1;
  Dot[1].dir = 3;
  Dot[1].key = 3;

  XClearWindow( Disp[0],Scr_w[0] );
  XClearWindow( Disp[1],Scr_w[1] );

  Map[0][0] = 1;
  Map[MAP_W-1][MAP_H-1] = 2;

  light( 0,0,0 );
  light( MAP_W-1,MAP_H-1,1 );
}


/* Xlib関係の初期化  */

static void init_graph( void )
{
  int i,j;
  XGCValues gcv;

  for( i = 0 ; i < 2 ; i++ )
    for( j = 0 ; j < 2 ; j++ ){
      XtVaGetValues( Score[i][j],XtNforeground,&gcv.foreground,NULL );
      if( i == 0 )
	Gc_color[i][j] = XCreateGC( Disp[i],Scr_w[i],GCForeground,&gcv );
      else
	Gc_color[i][1-j] = XCreateGC( Disp[i],Scr_w[i],GCForeground,&gcv );
    }
}


/* 結果表示 */

static void show_result( int win )
{
  switch( win ){
  case 0:
    XtVaSetValues( Mesg[0],XtNlabel,"You won!",NULL );
    XtVaSetValues( Mesg[1],XtNlabel,"You lost!",NULL );
    break;

  case 1:
    XtVaSetValues( Mesg[0],XtNlabel,"You lost!",NULL );
    XtVaSetValues( Mesg[1],XtNlabel,"You win!",NULL );
    break;

  case 2:
    XtVaSetValues( Mesg[0],XtNlabel,"Draw!",NULL );
    XtVaSetValues( Mesg[1],XtNlabel,"Draw!",NULL );
    break;
  }
  XtMapWidget( Mesg[0] );
  XtMapWidget( Mesg[1] );
}


/* タイトル表示 */

static void title( XtPointer c,XtIntervalId id )
{
  XtVaSetValues( Mesg[0],XtNlabel,"XversusDOT",NULL );
  XtMapWidget( Mesg[0] );
  XtVaSetValues( Mesg[1],XtNlabel,"XversusDOT",NULL );
  XtMapWidget( Mesg[1] );
}


/* タイマー処理ルーチン */

static void timi( XtPointer c,XtIntervalId id )
{
  static int d = 0;
  int p;
  int win;

  set_timer();

  if( !game(&win) ){
    show_result(win);
    Mode = MD_OVER;
    Win[win]++;
    show_win();
    set_timer();
  }
}


/* 実際にスタート */

static void ready_go( XtPointer p, XtIntervalId id  )
{
  Mode = MD_GAME;
  XtUnmapWidget( Mesg[0] );
  XtUnmapWidget( Mesg[1] );
  set_timer();
}


/* スタート(アクション) */

void ac_start( Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( Mode == MD_TITLE || Mode == MD_OVER ){
    Mode = MD_READY;
    init_game();
    XtVaSetValues( Mesg[0],XtNlabel,"READY",NULL );
    XtMapWidget( Mesg[0] );
    XtVaSetValues( Mesg[1],XtNlabel,"READY",NULL );
    XtMapWidget( Mesg[1] );
    set_timer();
  }
}


/* 終了(アクション) */

void ac_quit( Widget w,XEvent *e,String *s,Cardinal *num )
{
  XtDestroyApplicationContext( App );
  exit(0);
}


/* キー入力(アクション) */

void ac_key( Widget w,XEvent *e,String *s,Cardinal *num )
{
  int d,p;

  if( *num == 0 )
    return;

  if( w == Base[0] )
    p = 0;
  else
    p = 1;

  switch( **s ){
  case 'U':
    d = 0;
    break;

  case 'R':
    d = 1;
    break;

  case 'D':
    d = 2;
    break;

  case 'L':
    d = 3;
    break;

  defaul:
    return;
  }
  if( p )
    d = (d+2)%4;

  Dot[p].key = d;
}


/* エクスポーズ(イベントハンドラ) */

static void ev_expose( Widget w,int p,XEvent *e )
{
  int x1,y1,x2,y2,x,y;
  int xx,yy;

  x1 = e->xexpose.x/8;
  x2 = (e->xexpose.x+e->xexpose.width-1)/8;
  y1 = e->xexpose.y/8;
  y2 = (e->xexpose.y+e->xexpose.height-1)/8;

  if( p == 0 ){
    for( y = y1 ; y <= y2 ; y++ )
      for( x = x1 ; x <= x2 ; x++ )
	if( Map[x][y] )
	  XFillRectangle( Disp[0],Scr_w[0],
			 Gc_color[0][Map[x][y]-1],x*8,y*8,8,8 );
  }else{
    for( y = y1 ; y <= y2 ; y++ ){
      yy = MAP_H - 1 - y;
      for( x = x1 ; x <= x2 ; x++ ){
	xx = MAP_W - 1 - x;
	if( Map[xx][yy] )
	  XFillRectangle( Disp[1],Scr_w[1],
			 Gc_color[1][Map[xx][yy]-1],x*8,y*8,8,8 );
      }
    }
  }
}



/* タイマー中断 */

static void reset_timer( void )
{
  if( IntervalState ){
    XtRemoveTimeOut( IntervalId );
    IntervalState = 0;
  }
}



/* タイマースタート */

static void set_timer( void )
{
  if( IntervalState )
    reset_timer();

  switch( Mode ){
  case MD_READY:
    IntervalId = XtAppAddTimeOut( App,800,(XtTimerCallbackProc)ready_go,NULL );
    break;

  case MD_GAME:
    IntervalId = XtAppAddTimeOut( App,SPEED,(XtTimerCallbackProc)timi,NULL );
    break;

  case MD_OVER:
    IntervalId = XtAppAddTimeOut( App,4000,(XtTimerCallbackProc)title,NULL );
  break;


  default:
    return;
  }
  IntervalState = 1;
}



int main(int argc,char **argv)
{
  static XtActionsRec a_table[] = {
    {"start",  (XtActionProc)ac_start},
    {"quit",   (XtActionProc)ac_quit},
    {"dir",    (XtActionProc)ac_key},
  };

  int o_argc;
  char **o_argv;

  Widget w1;
  int p;

  if( argv[0] == NULL )
    Myname="";
  else
    Myname = argv[0];

  o_argv = (char **)XtCalloc(argc,sizeof(char*));

  for( o_argc = 0 ; o_argc < argc ; o_argc++ )
    o_argv[o_argc] = argv[o_argc];

  Top[0] = XtVaAppInitialize(&App,"XVsDot",NULL,0,
			     &argc,argv,Res,NULL);
  XtAppAddActions( App,a_table,XtNumber(a_table) );

  Disp[0] = XtDisplay( Top[0] );

  if( argc < 2 ){
    fprintf( stderr,"usage : %s [-toolkitoptions] his_display\n",argv[0] );
    exit(1);
  }else
    Disp[1] = XtOpenDisplay( App,argv[1],NULL,"XVsTank",NULL,0,
			    &o_argc,o_argv );

  Top[1] = XtVaAppCreateShell( NULL,"XVsDot",applicationShellWidgetClass,
			      Disp[1],NULL );

  for( p = 0 ; p < 2 ; p++ ){
    Base[p] = XtVaCreateManagedWidget( "base",formWidgetClass,Top[p],NULL );

    Mesg[p] = XtVaCreateManagedWidget( "mesg",labelWidgetClass,Base[p],
				      XtNwidth,MAP_W*8,
				      XtNlabel,"",
				      XtNmappedWhenManaged,False,
				      NULL );

    Scr[p] = XtVaCreateManagedWidget( "scr",widgetClass,Base[p],
				     XtNwidth,MAP_W*8,
				     XtNheight,MAP_H*8,
				     NULL );

    Score[p][0] = XtVaCreateManagedWidget( "you",labelWidgetClass,Base[p],
					  XtNlabel,"Yours : 888",
					  NULL );

    Score[p][1] = XtVaCreateManagedWidget( "he",labelWidgetClass,Base[p],
					  XtNlabel,"His : 888",
					  NULL );

    Score[p][2] = XtVaCreateManagedWidget( "draw",labelWidgetClass,Base[p],
					  XtNlabel,"Draw : 888",
					  NULL );

    XtRealizeWidget(Top[p]);
    Scr_w[p] = XtWindow( Scr[p] );

    XtAddEventHandler( Scr[p],ExposureMask,False,ev_expose,p );
  }

  init_graph();
  init_map();

  Win[0] = 0;
  Win[1] = 0;
  Win[2] = 0;

  show_win();

  Mode = MD_TITLE;
  title(NULL,0);

  XtAppMainLoop(App);

  return 0;
}
