/********************************
  netpuyo(client)
  main.c --- X Toolkit operation
  (C) Oct 6, 1997 ROYALPANDA
*******************************/

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

#include"netpuyo.h"
#include"netpuyo_x.h"
#include"protocol.h"

#include"graph/icon.xbm"
#include"graph/icon_msk.xbm"

/* 現在の状態 */

#define MD_NCNCT 0   /* 対戦相手がいない */
#define MD_TITLE 1   /* タイトル */
#define MD_WAIT  2   /* 対戦開始待ち */
#define MD_GAME  3   /* 対戦中 */

/* キー */

#define KEY_RIGHT 1
#define KEY_LEFT  2
#define KEY_PLUS  3
#define KEY_MINUS 4
#define KEY_TURN_P 5
#define KEY_TURN_M 6

/* スピード */

#define TIME_FALL     800    /* 落ちる時間 */
#define TIME_HASTE    40     /* 高速に落す時間 */
#define TIME_GAME     40     /* 画面描画の間隔 */
#define TIME_KEY      200    /* キーリピートが始まるまでの時間 */
#define TIME_REPEAT   50     /* キーリピートの間隔 */


/* 変数(スコープ:モジュール内) */

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


static Widget Top,Scr[2],Next[2],Stock[2],Win[2],Lose[2],Msg[2],Wait;


static char *GraphDir = NULL;  /* グラフィックのあるディレクトリ  */
static char *WallFile[2] = {NULL,NULL};  /* 壁紙ファイル名 */
static char *Host = "localhost";   /* サーバのホスト名 */ 
static int Port = 9000;            /* サーバーのポート */
static int Alarm = 0;              /* アラームのオン/オフ */

static int IntervalState[3] = {0,0,0}; /* インターバルタイマが動作中か? */
static XtIntervalId IntervalId[3];  /* インターバルタイマID */
int Interval[3] = {TIME_GAME,TIME_FALL,TIME_KEY}; /*インターバルタイマの間隔 */


static int Mode; /* 現在の状態 */

static XKeyboardState Keyboard;  /* キーボードステータス */
static int Repeat_mode = 1;      /* キーリピートの状態(1:default 0:off) */

static int Key = 0; /* 押されているキー（下除く) */

/* 変数(スコープ:プログラム) */

int My_id; /* 自分のID */

int Sock = -1;    /* 通信用のソケットディスクリプタ */

Colormap Cmap;    /* カラーマップ */

char *Myname;            /* プロセス名  */

Display *Disp;           /* ディスプレイ構造体 */

Pixmap Wall_p[2];        /* 壁紙  */
Pixmap Char_p[6];        /* キャラクタ       */
GC Gc_mask[6];           /* マスク用GC       */
GC Gc_nomask;            /* マスクなし用ＧＣ */

Window Scr_w[2];   /* メイン画面     */
Window Next_w[2];  /* NEXT表示画面   */
Window Stock_w[2]; /* 予告ぷよ表示画面 */

player_t P[2];   /* プレーヤーのデータ */


/* プロトタイプ */

static void timi( int i,XtIntervalId id );


/* 組ぷよ移動開始 */

void start_move( void )
{
  Interval[1] = TIME_FALL;
  set_timer( 1 );
}


/* キーリピートを止める */

static void repeat_off( void )
{
  if( Repeat_mode ){
    XGetKeyboardControl( Disp,&Keyboard );
    XAutoRepeatOff( Disp );
    XFlush( Disp );
    Repeat_mode = 0;
  }
}


/* キーリピートを元に戻す */

static void repeat_on( void )
{
  if( !Repeat_mode ){
    if( Keyboard.global_auto_repeat )
      XAutoRepeatOn( Disp );
    else
      XAutoRepeatOff( Disp );
    XFlush( Disp );
    Repeat_mode = 1;
  }
}


/* メッセージを表示 */

void message( int p, char *msg )
{
  XtVaSetValues(Msg[p],XtNlabel,msg,NULL );
}


/* タイマー中断 */

void reset_timer( int i )
{
  if( IntervalState[i] ){
    XtRemoveTimeOut( IntervalId[i] );
    IntervalState[i] = 0;
  }
}

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

void set_timer( int i )
{
  if( IntervalState[i] )
    reset_timer(i);
  IntervalId[i] = XtAppAddTimeOut( App, Interval[i],
				  (XtTimerCallbackProc)timi,i );
  IntervalState[i] = 1;
}



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

static void timi( int i,XtIntervalId id )
{
  IntervalState[i] = 0;

  switch( i ){
  case 0:
    set_timer( 0 );
    animation();
    break;

  case 1:
    set_timer( 1 );
    if( !fall() )
      reset_timer( 1 );
    break;
    
  case 2:
    switch( Key ){
    case KEY_RIGHT:
      move( 1 );
      break;

    case KEY_LEFT:
      move( -1 );
      break;

    case KEY_PLUS:
      rotate( 1 );
      break;

    case KEY_MINUS:
      rotate( -1 );
      break;

    case KEY_TURN_P:
    case KEY_TURN_M:
      Key = 0;
    }
    Interval[2] = TIME_REPEAT;
    set_timer(2);
  }
}


/* サーバーからの通信を受信する */

static void from_server( XtPointer p,int *sock, XtInputId *id )
{
  int msg;

  fill_buf();

  if( Mode == MD_GAME )
    recieve();
  else{
    while( (msg = recv_msg()) ){
      switch( msg ){
      case MSG_CNCT:
	XtVaSetValues(Top,XtNiconName,"netpuyo",NULL);
	XtUnmapWidget( Wait );
	if( Alarm )
	  XBell( Disp,100 );
	Mode = MD_TITLE;
	break;

      case  MSG_GAME:
	if( Mode == MD_TITLE || Mode == MD_WAIT ){
	  message(0,"");
	  message(1,"");
	  XtUnmapWidget( Win[0] );
	  XtUnmapWidget( Win[1] );
	  XtUnmapWidget( Lose[0] );
	  XtUnmapWidget( Lose[1] );
	  clear_scr();

	  My_id = recv_snum();
	  P[0].next[0] = P[1].next[0] = recv_snum();
	  P[0].next[1] = P[1].next[1] = recv_snum();
	  P[0].next[2] = P[1].next[2] = recv_snum();
	  P[0].next[3] = P[1].next[3] = recv_snum();
	  draw_next( 0 );
	  draw_next( 1 );
	  Interval[1] = TIME_FALL;
	  Key = 0;
	  Mode = MD_GAME;
	  init_game();
	}
	break;

      case MSG_QUIT:
	quit();
	break;
      }
    }
  }
}


/* 対戦終了 */

void winner( int p )
{
  char buf[16];

  Mode = MD_TITLE;
  reset_timer( 0 );
  reset_timer( 1 );
  reset_timer( 2 );

  if( My_id )
    p = 1-p;

  P[p].win++;

  sprintf( buf,"WON:%d",P[0].win );
  message(0,buf );
  sprintf( buf,"WON:%d",P[1].win );
  message(1,buf );


  XtMapWidget( Win[p] );
  XtMapWidget( Lose[1-p] );
}


/* エクスポーズ処理 */

static void expose( Widget w,int i,XEvent *e )
{
  int x1,y1,x2,y2,x,y;

  switch( i ){
  case 0:
  case 1:
    x1 = e->xexpose.x / 32;
    y1 = e->xexpose.y / 32;
    x2 = (e->xexpose.x+e->xexpose.width-1) / 32;
    y2 = (e->xexpose.y+e->xexpose.height-1) / 32;

    for( y = y1 ; y <= y2 ; y++ )
      for( x = x1 ; x <= x2 ; x++ )
	draw( i,x,y );
    break;

  case 2:
  case 3:
    if( e->xexpose.count )
      return;
    draw_next( i-2 );
    break;
    
  case 4:
  case 5:
    x1 = (e->xexpose.x - 16) / 32;
    y1 = e->xexpose.y / 32;
    x2 = (e->xexpose.x+e->xexpose.width-17) / 32;
    y2 = (e->xexpose.y+e->xexpose.height-1) / 32;

    for( y = y1 ; y <= y2 ; y++ )
      for( x = x1 ; x <= x2 ; x++ )
	draw_stock( i-4,x+(1-y)*7 );
    break;
  }
}


/* フォーカス変更(キーリピートの切替え)(イベントハンドラ) */

static void focus( Widget w,XtPointer p,XEvent *e )
{
  if( e->type == FocusIn )
    repeat_off();
  else
    repeat_on();
}


/* 終了 */

void quit( void )
{
  reset_timer( 0 );
  reset_timer( 1 );
  close(Sock);
  repeat_on();
  XtDestroyApplicationContext( App );
  exit(0);
}  


/* プログラム終了 (アクションハンドラ) */

static void ac_quit( Widget w,XEvent *e,String *s,Cardinal *num )
{
  send_init(MSG_QUIT);
  send_msg();
  quit();
}


/* 対戦開始(アクションハンドラ) */
static void ac_start( Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( Mode != MD_TITLE )
    return;

  send_init(MSG_STRT);
  send_msg();
  Mode = MD_WAIT;
}


/* キーが押された */

static void ac_keyon( Widget w,XEvent *e,String *s,Cardinal *num )
{
  int k;

  if( Mode != MD_GAME )
    return;

  if( *num < 1 )
    return;

  switch( **s ){
  case 'F':
    reset_timer(2);
    Key = 0;
    Interval[1] = TIME_HASTE;
    if( IntervalState[1] ){
      set_timer( 1 );
      if( !fall() )
	 reset_timer( 1 );
    }
    return;
    break;

  case 'R':
    move( 1 );
    k = KEY_RIGHT;
    break;

  case 'L':
    move( -1 );
    k = KEY_LEFT;
    break;

  case '+':
    if( Key == KEY_TURN_P && turn() ){
      reset_timer( 2 );
      Key = 0;
      return;
    }
    rotate( 1 );
    k = KEY_PLUS;
    break;

  case '-':
    if( Key == KEY_TURN_M && turn() ){
      reset_timer( 2 );
      Key = 0;
      return;
    }
    rotate( -1 );
    k = KEY_MINUS;
    break;

  default:
    return;
    break;
  }

  if( k != Key ){
    Key = k;
    Interval[2] = TIME_KEY;
    set_timer(2);
  }
}


/* キーが離された */

static void ac_keyoff( Widget w,XEvent *e,String *s,Cardinal *num )
{
  int k;

  if( Mode != MD_GAME )
    return;

  if( *num < 1 )
    return;

  switch( **s ){
  case 'F':
    Interval[1] = TIME_FALL;
    if( IntervalState[1] )
      set_timer( 1 );
    return;
    break;

  case 'R':
    k = KEY_RIGHT;
    break;

  case 'L':
    k = KEY_LEFT;
    break;

  case '+':
    Key = KEY_TURN_P;
    Interval[2] = TIME_KEY;
    set_timer( 2 );
    return;
    break;

  case '-':
    Key = KEY_TURN_M;
    Interval[2] = TIME_KEY;
    set_timer( 2 );
    return;

  default:
    return;
    break;
  }

  if( k == Key ){
    Key = 0;
    reset_timer( 2 );
  }
}


/* アイコンの設定 */

static void set_icon( void )
{
  Pixmap icon,mask;

  icon = XCreateBitmapFromData( Disp,DefaultRootWindow(Disp),icon_bits,
			       icon_width,icon_height );

  mask = XCreateBitmapFromData( Disp,DefaultRootWindow(Disp),icon_msk_bits,
			       icon_msk_width,icon_msk_height );

  XtVaSetValues( Top,
		XtNiconPixmap,icon,
		XtNiconMask,mask,
		XtNiconName,"netpuyo(waiting)",
		NULL);
}



/* ヘルプ */

static void help()
{
  fprintf(stderr,"Usage: %s [options]\n",Myname);
  fprintf(stderr,"\t-toolkitoption ...\n");

  fprintf(stderr,
   "\t-host \"hostname\"\tconnect to \"hostname\"(default:localhost).\n");

  fprintf(stderr,
	  "\t-port \"port No.\"\tconnect to \"port No.\"(default:9000).\n");

  fprintf(stderr,
	  "\t-alarm\t\tring a bell when another user appears.\n");

  fprintf(stderr,
	  "\t-graphic \"directry\"\tuse your graphic in \"directry\".\n");

  fprintf(stderr,
	  "\t-wall  \"XPM file\"\tuse \"XPM file\" for background.\n");

  fprintf(stderr,
  "\t-wall1 \"XPM file\"\tuse \"XPM file\" for your own background.\n");

  fprintf(stderr,
     "\t-wall2 \"XPM file\"\tuse \"XPM file\" for rival's background.\n");

  fprintf(stderr,"\t-private\t\tuse private colormap.\n",Myname);

  fprintf(stderr,"\t-version\t\tshow version\n" );

  fprintf(stderr,"\t-help\t\t\tshow this messages.\n");
}


static void version( void )
{
  static char str[] ="\n" 
    "-----------------------------------------------------\n\n"
      "N e t P u y o ( client )   version 0.7( Prototype )\n\n"
	"(c) 1997 ROYALPANDA\n\n"
	  "Please send bug report to: i11053@ced.cas.uec.ac.jp\n\n"
	    "-----------------------------------------------------\n";

  fprintf( stderr,str );
}

/* コマンドラインオプション解析 */

static void option( int argc, char **argv )
{
  int i;

  for( i = 1 ; i < argc ; i++ ){
    if( strcmp( argv[i],"-graphic" ) == 0 ){
      GraphDir = argv[++i];
      continue;
    }

    if( strcmp( argv[i],"-wall" ) == 0 ){
      WallFile[0] = argv[++i];
      WallFile[1] = argv[i];
      continue;
    }

    if( strcmp( argv[i],"-wall1" ) == 0 ){
      WallFile[0] = argv[++i];
      continue;
    }

    if( strcmp( argv[i],"-wall2" ) == 0 ){
      WallFile[1] = argv[++i];
      continue;
    }

    if( strcmp( argv[i],"-private" ) == 0 ){
      Cmap =
	XCreateColormap( Disp,DefaultRootWindow(Disp),
			DefaultVisual(Disp,DefaultScreen(Disp)),AllocNone );
      XtVaSetValues( Top,XtNcolormap,Cmap,NULL);
      continue;
    }

    if( strcmp( argv[i],"-host" ) == 0 ){
      Host = argv[++i];
      continue;
    }

    if( strcmp( argv[i],"-port" ) == 0 ){
      Port = atoi( argv[++i] );
      continue;
    }

    if( strcmp( argv[i],"-alarm" ) == 0 ){
      Alarm = 1;
      continue;
    }

    if( strcmp( argv[i],"-version" ) == 0 ){
      version();
      exit(0);
    }

    if( strcmp( argv[i],"-help" ) == 0 ){
      help();
      exit(0);
    }
    fprintf(stderr,"%s: Unknown command line option \"%s\".\n",Myname,argv[i]);
    help();
    exit(1);
  }
}  



int main(int argc,char **argv)
{

  Widget w;
  int m;

  static XtActionsRec a_table[] = {
    {"quit",  (XtActionProc)ac_quit},
    {"start", (XtActionProc)ac_start},
    {"key_on",(XtActionProc)ac_keyon},
    {"key_off",(XtActionProc)ac_keyoff},
  };

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



  Top = XtVaAppInitialize(&App,"Netpuyo",NULL,0,
			&argc,argv,DefaultResources,NULL);

  Disp = XtDisplay( Top );

  Cmap = DefaultColormap(Disp,DefaultScreen(Disp));

  option( argc,argv );

  init_graph( GraphDir,WallFile );

  w = XtVaCreateManagedWidget("puyo",formWidgetClass,Top,NULL );

  w = XtVaCreateManagedWidget("base",formWidgetClass,w,
			      XtNleft,XawChainLeft,
			      XtNright,XawChainLeft,
			      XtNtop,XawChainTop,
			      XtNbottom,XawChainTop,
			      NULL);

  Wait = XtVaCreateManagedWidget("wait",labelWidgetClass,w,NULL );

  Win[0] = XtVaCreateManagedWidget("win0",labelWidgetClass,w,
				   XtNwidth,32*8,
				   XtNmappedWhenManaged,False,
				   NULL);

  Lose[0] = XtVaCreateManagedWidget("lose0",labelWidgetClass,w,
				    XtNwidth,32*8,
				    XtNmappedWhenManaged,False,
				    NULL);

  Stock[0] = XtVaCreateManagedWidget("stock0",widgetClass,w,
				     XtNwidth,32*8,
				     XtNheight,32*2,
				     XtNborderWidth,0,
				     NULL );

  Scr[0] = XtVaCreateManagedWidget("scr0",widgetClass,w,
				   XtNwidth,32*8,
				   XtNheight,32*14,
				   XtNborderWidth,0,
				   XtNbackgroundPixmap,Wall_p[0],
				   NULL);

  Msg[0] = XtVaCreateManagedWidget("msg0",labelWidgetClass,w,
				   XtNwidth,32*8,
				   XtNborderWidth,0,
				   XtNlabel,"",
				   NULL);


  Next[0] = XtVaCreateManagedWidget("next0",widgetClass,w,
				    XtNwidth,32,
				    XtNheight,160,
				    NULL);

  Next[1] = XtVaCreateManagedWidget("next1",widgetClass,w,
				    XtNwidth,32,
				    XtNheight,160,
				    NULL);

  Win[1] = XtVaCreateManagedWidget("win1",labelWidgetClass,w,
				   XtNwidth,32*8,
				   XtNmappedWhenManaged,False,
				   NULL);

  Lose[1] = XtVaCreateManagedWidget("lose1",labelWidgetClass,w,
				    XtNwidth,32*8,
				    XtNmappedWhenManaged,False,
				    NULL);

  Stock[1] = XtVaCreateManagedWidget("stock1",widgetClass,w,
				     XtNwidth,32*8,
				     XtNheight,32*2,
				     XtNborderWidth,0,
				     NULL );

  Scr[1] = XtVaCreateManagedWidget("scr1",widgetClass,w,
				   XtNwidth,32*8,
				   XtNheight,32*14,
				   XtNborderWidth,0,
				   XtNbackgroundPixmap,Wall_p[1],
				   NULL);

  Msg[1] = XtVaCreateManagedWidget("msg1",labelWidgetClass,w,
				   XtNwidth,32*8,
				   XtNborderWidth,0,
				   XtNlabel,"",
				   NULL);

  XtVaCreateManagedWidget( "copyright",labelWidgetClass,w,NULL );

  XtAddEventHandler( Scr[0],ExposureMask,FALSE,(XtEventHandler)expose,0 );
  XtAddEventHandler( Scr[1],ExposureMask,FALSE,(XtEventHandler)expose,1 );
  XtAddEventHandler( Next[0],ExposureMask,FALSE,(XtEventHandler)expose,2 );
  XtAddEventHandler( Next[1],ExposureMask,FALSE,(XtEventHandler)expose,3 );
  XtAddEventHandler( Stock[0],ExposureMask,FALSE,(XtEventHandler)expose,4 );
  XtAddEventHandler( Stock[1],ExposureMask,FALSE,(XtEventHandler)expose,5 );
  XtAddEventHandler( Top,FocusChangeMask,FALSE,(XtEventHandler)focus,NULL );

  XtAppAddActions( App,a_table,XtNumber(a_table));

  set_icon();

  XtRealizeWidget(Top);

  Scr_w[0] = XtWindow( Scr[0] );
  Next_w[0] = XtWindow( Next[0] );
  Scr_w[1] = XtWindow( Scr[1] );
  Next_w[1] = XtWindow( Next[1] );
  Stock_w[0] = XtWindow( Stock[0] );
  Stock_w[1] = XtWindow( Stock[1] );

  if( init_network( Host,Port ) < 0 )
    quit();

  XtAppAddInput( App,Sock,XtInputReadMask,from_server,NULL );


  P[0].next[0] = -1;
  P[1].next[0] = -1;

  P[0].win = 0;
  P[1].win = 0;

  Mode = MD_NCNCT;

  XtAppMainLoop(App);

  return 0;
}

