/*****************************************
  xjump version 2

  main.c   初期化 ／ X Intrinsics関係

  (C) Oct 28, 1997  ROYALPANDA
*****************************************/

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
#include<X11/Intrinsic.h>
#include<X11/StringDefs.h>
#include<X11/Xaw/Form.h>
#include<X11/Xaw/Viewport.h>
#include<X11/Xaw/Label.h>
#include<X11/Shell.h>
#include<X11/xpm.h>

#include"xjump.h"
#include"record.h"

#include"picture.xpm"
#include"icon.xbm"
#include"icon_msk.xbm"

#define TITLE 0
#define GAME  1
#define OVER  2

#define SPEED 20

extern char *DefaultResources[];

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

static Widget
       Top,              /* 最上位                        */
       Score,            /* スコア(ラベル)                */
       Scr,              /* メイン画面（コア)             */
       Score_v,          /* ハイスコア(ビューポート)      */
       ScoreList,        /* ハイスコア(ラベル)            */
       Gameover,         /* ゲームオーバー(ラベル)        */
       Pause;            /* ポーズ(ラベル)                */


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

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

static char *GraphFile = NULL;     /* ユーザーグラフィックファイル */

static int GameMode; /* モード (0タイトル  1ゲーム  2ゲームオーバー 3ポーズ) */

static int Sc_now;

static char Score_list[43*(RECORD_ENTRY+2)+1]="";    /* ハイスコアテキスト */

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

/* プロトタイプ宣言 */

static void timi( XtPointer c, XtIntervalId id );


/* グローバル変数 */

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

GC Gc_nomask;            /* GC (マスク無し) */
GC Gc_mask;              /* GC (マスクあり) */

Drawable Scr_d;          /* メイン画面 */
Pixmap Char_p;           /* キャラクタ */
Pixmap Char_m;           /* キャラクタ(マスク) */
Pixmap Floor_p[4];       /* 床 */
Pixmap Back_p;           /* 背景 */

int Floor_top;

record_t Record[ RECORD_ENTRY ];  /* ハイスコアリスト                    */
int Record_entry;                 /* ハイスコアリストの数  -1:記録しない */

char *Myname;   /* プログラム名 */


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

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


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

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

    Repeat_mode = 1;
  }
}


/* スコア表示 */

static void put_score( void )
{
  char buf[4];

  sprintf( buf,"%03d",Sc_now );
  XtVaSetValues( Score,XtNlabel,buf,NULL );
}


/* ハイスコア画面作成 */

static void make_score( void )
{
  int i;
  char *p;

  if( Record_entry == -1 )
    return;

  p = Score_list;

  p += sprintf( p,"RANK FLOOR   NAME\n\
---- ----- -------------------------------\n");

  for( i = 0 ; i < Record_entry ; i++ )
    p += sprintf( p,"%4d  %3d  %-31.31s\n",i+1,
	    Record[i].score,Record[i].name );

  p--;
  *p = '\0';

  XtVaSetValues( ScoreList,
		XtNlabel,Score_list,
		NULL );
}


/* タイマー中断 */

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


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

static void set_timer( void )
{
  if( IntervalState )
    reset_timer();
  IntervalId = XtAppAddTimeOut( App, SPEED ,(XtTimerCallbackProc)timi,NULL );
  IntervalState = 1;
}


/* ゲームオーバー */

static void gameover( void )
{
  XtMapWidget( Gameover );

  GameMode = OVER;

  save_record( Sc_now );
  make_score();

}


/* タイトル */

static void title( void )
{
  reset_timer();
  XtUnmapWidget( Gameover );
  XtUnmapWidget( Scr );
  GameMode = TITLE;
  if( Record_entry != -1 )
    XtMapWidget( Score_v );
}



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

static void timi( XtPointer c,XtIntervalId id )
{
  int floor;
  static int timer;

  IntervalState = 0;

  set_timer();

  switch( GameMode ){

  case GAME:
    floor = GAME_main();
    if( floor < 0 ){
      gameover();
      timer = 0;
    }
    else if( floor > Sc_now ){
      Sc_now = floor;
      if( Sc_now > 999 )
	Sc_now = 999;
      put_score();
    }
    break;
    
  case OVER:
    if( ++timer > 250 )
      title();
    break;
  }
}


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

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


#if 0
/* エクスポーズ処理 (イベントハンドラ) */

static void expose( Widget w,XtPointer p,XEvent *e )
{
  recover_scr( e->xexpose.x,e->xexpose.y,
	      e->xexpose.width,e->xexpose.height );
}
#endif

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

static void quit_game( Widget w,XEvent *e,String *s,Cardinal *num )
{
  repeat_on();
  XFlush( Disp );
  exit(0);
}


/* プログラム終了(シグナルハンドラ) */

static void sig_handler( int i )
{
  repeat_on();
  XFlush( Disp );
  exit(0);
}


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

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

  switch( GameMode ){
  case TITLE:
    level = atoi(*s);
    XtUnmapWidget( Score_v );
    XtUnmapWidget( Gameover );
    reset_timer();
    XtMapWidget( Scr );
    GAME_init(level);
    Sc_now = 0;
    GameMode = GAME;

    set_timer();
    break;

  case OVER:
    title();
    break;
  }
}


/* ポーズ (アクションハンドラ) */

static void pause_game( Widget w,XEvent *e,String *s,Cardinal *num )
{
  if( GameMode == 1 ){
    reset_timer();
    XtMapWidget( Pause );
    GameMode = 3;
  }else if( GameMode == 3 ){
    GameMode = 1;
    XtUnmapWidget( Pause );
    set_timer();
  }
}



/* キー押された(アクションハンドラ) */

static void key_on( Widget w,XEvent *e,String *s,Cardinal *num )
{
  switch( **s ){
  case 'U':
    GAME_key_press(KEY_UP);
    break;
  case 'R':
    GAME_key_press(KEY_RIGHT);
    break;
  case 'L':
    GAME_key_press(KEY_LEFT);
    break;
  }
}

/* キー離された (アクションハンドラ) */

static void key_off( Widget w,XEvent *e,String *s,Cardinal *num )
{
  switch( **s ){
  case 'U':
    GAME_key_release(KEY_UP);
    break;
  case 'R':
    GAME_key_release(KEY_RIGHT);
    break;
  case 'L':
    GAME_key_release(KEY_LEFT);
    break;
  }
}


/* アイコンの設定 */

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,NULL );
}


/* ヘルプ */

static void help()
{
  fprintf( stderr,"Usage: %s [options]\n",Myname );
  fprintf( stderr,"\t-toolkitoption ...\n" );
  fprintf( stderr,"\t-private\t\tuse private colormap.\n" );
  fprintf( stderr,"\t-graphic \"file\"\t\tuse your xpm graphic \"file\"\n" );
  fprintf( stderr,"\t-keymap\t\t\tread keymap directly.\n" );
  fprintf( stderr,"\t-help\t\t\tshow this messages.\n" );
}


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

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

  for( i = 1 ; i < argc ; i++ ){

    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],"-graphic" ) == 0 ){
      GraphFile = argv[++i];
      continue;
    }

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


/* グラフィックの作成／読み込み */

static void make_graphic( void )
{
  int i;
  XpmAttributes attr;

  attr.valuemask = XpmColormap;
  attr.colormap = Cmap;

  if( GraphFile != NULL )
    i = XpmReadFileToPixmap( Disp,DefaultRootWindow(Disp),GraphFile,
			    &Char_p,&Char_m,&attr );
    else
      i = XpmCreatePixmapFromData( Disp,DefaultRootWindow(Disp),
				  picture_xpm,&Char_p,&Char_m,&attr );
  if( i ){
    fprintf( stderr,"%s: %s\n",Myname,XpmGetErrorString(i) );
    exit(1);
  }

  Gc_mask = XCreateGC( Disp,DefaultRootWindow(Disp),0,NULL );
  XSetClipMask( Disp,Gc_mask,Char_m );
}



/* Xlib関係の前処理 */

static int init_graph( void )
{
  int x,i;

  make_graphic();

  Gc_nomask = XCreateGC( Disp,DefaultRootWindow(Disp),0,NULL );

  Back_p = XCreatePixmap( Disp,DefaultRootWindow(Disp),
			 WIDTH*16,16,
			 DefaultDepth(Disp,DefaultScreen(Disp)) );
  XCopyArea( Disp,Char_p,Back_p,Gc_nomask,80,128,16,16,0,0 );
  XCopyArea( Disp,Char_p,Back_p,Gc_nomask,96,128,16,16,WIDTH*16-16,0 );
  for( x = 16 ; x < WIDTH*16-16 ; x+=16 )
    XCopyArea( Disp,Char_p,Back_p,Gc_nomask,0,128,16,16,x,0 );
  for( i = 0 ; i < 3 ; i++ ){
    Floor_p[i] = XCreatePixmap(Disp,DefaultRootWindow(Disp),
    				WIDTH*16-32,16,
    				DefaultDepth(Disp,DefaultScreen(Disp)));
    for( x = 0 ; x < WIDTH*16-32 ; x+=16 )
      XCopyArea(Disp,Char_p,Floor_p[i],Gc_nomask,i*16,128,16,16,x,0);
  }
  Floor_p[3] = XCreatePixmap(Disp,DefaultRootWindow(Disp),
   			WIDTH*16-32,16,
    			DefaultDepth(Disp,DefaultScreen(Disp)));
  for( x = 0 ; x < WIDTH*16-32 ; x+=32 )
    XCopyArea(Disp,Char_p,Floor_p[3],Gc_nomask,48,128,32,16,x,0);
}


/* メイン */

int main( int argc,char **argv )
{
  static XtActionsRec a_table[] = {
    {"start",  (XtActionProc)start_game},
    {"pause",  (XtActionProc)pause_game},
    {"quit",   (XtActionProc)quit_game},
    {"key_on", (XtActionProc)key_on},
    {"key_off",(XtActionProc)key_off},
  };

  Widget game,w;

  uid_t uid;

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

  uid = geteuid();
  seteuid(getuid());

  XtSetLanguageProc( NULL,NULL,NULL );

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

  Disp = XtDisplay( Top );


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

  option( argc,argv );

  seteuid( uid );

  init_graph();

  game = XtVaCreateManagedWidget( "game",formWidgetClass,Top,NULL );

  XtVaCreateManagedWidget( "title",labelWidgetClass,game,NULL );
  XtVaCreateManagedWidget( "title2",labelWidgetClass,game,NULL );

  w = XtVaCreateManagedWidget( "scoreBoard",formWidgetClass,game,
			      NULL );

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

  Score = XtVaCreateManagedWidget( "score",labelWidgetClass,w,
				  XtNlabel,"000",
				  NULL );

  Gameover = XtVaCreateManagedWidget( "gameover",labelWidgetClass,game,
				     XtNmappedWhenManaged,FALSE,
				     NULL );

  Pause = XtVaCreateManagedWidget( "pause",labelWidgetClass,game,
				  XtNmappedWhenManaged,FALSE,
				  NULL );

  Scr = XtVaCreateManagedWidget( "scr",widgetClass,game,
				XtNwidth,WIDTH*16,
				XtNheight,HEIGHT*16,
				XtNmappedWhenManaged,FALSE,
				XtNbackgroundPixmap,Back_p,
				NULL );

  Score_v = XtVaCreateManagedWidget( "record_v",viewportWidgetClass,game,
				    XtNwidth,WIDTH*16,
				    XtNheight,HEIGHT*16,
				    NULL );

  ScoreList = XtVaCreateManagedWidget( "record",labelWidgetClass,Score_v,
				      XtNlabel,"",
				      NULL );

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

  XtAddEventHandler( Top,FocusChangeMask,FALSE,(XtEventHandler)focus,NULL );
#if 0
  XtAddEventHandler( Scr,ExposureMask,FALSE,(XtEventHandler)expose,NULL );
#endif

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

  if( signal( SIGINT,SIG_IGN ) != SIG_IGN )
    signal( SIGINT,sig_handler );

  if( signal( SIGTERM,SIG_IGN ) != SIG_IGN )
    signal( SIGTERM,sig_handler );

  XtRealizeWidget( Top );
  set_icon();
  Scr_d = XtWindow(Scr);

  init_record();
  if( Record_entry != -1 )
    make_score();

  srnd();

  GameMode = TITLE;

  XtAppMainLoop( App );

  return 0;
}
