#include<stdio.h>
#include"koken.h"


#define S_TITLE   0
#define S_PREPAIR 1
#define S_MOVE    2
#define S_CHECK   3
#define S_ERASE   4
#define S_FALL    5
#define S_DEAD    6
#define S_END     7

static int State;
static int Erase_count;
static int Fall_time;
static int Fast_fall;
static int Color_target;
static int Erase_x,Erase_y;
static int Erase_map[8][13];
static int Dead_line;
static int Move_x[3],Move_y[3];
static int Move_color[3];
static int Move_dir;
static int Score;
static int Bonus;
static int Line;
static int Chain;
static int Multiplyer;
static int Level;
static int Drop;
static int Num_block;
static int Block_table[6];

static int Magic_time;
static int Magic;

extern int Map[8][13];
extern int Next_color[3];
extern int Next_dir;


static int check_dead( void )
{
  int i;

  for( i = 0 ; i < 3 ; i++ )
    if( Map[Move_x[i]][Move_y[i]] != B_NONE )
      return 1;
  return 0;
}

static int add_score( int block )
{
  Line++;
  Multiplyer += Line;
  Multiplyer += block-3;
  Bonus += block * 100;
}


static int check_diag( int x,int y, int dir )
{
  int sx,sy,c;
  int result;

  result = 0;

  c = B_NONE;
  sy = y;
  do{
    if( (Map[x][y] & 15) != c ){
      if( c != B_NONE && y-sy >= 3 ){
	add_score( y-sy );
	result++;
	while( sy < y ){
	  Map[sx][sy] |= B_CHECK_MSK;
	  sx += dir;
	  sy++;
	}
      }
      sx = x;
      sy = y;
      c = Map[x][y] & 15;
    }
    x += dir;
    y++;
  }while( y < 13 && x >= 0 && x <= 7 );

  return result;
}


static int check_erase( void )
{
  int x,y,c;
  int s;
  int result;


  result = 0;

  for( y = 0 ; y < 12 ; y++ ){
    c = B_NONE;
    s = 1;
    for( x = 1 ; x <= 7 ; x++ ){
      if( (Map[x][y] & 15) != c ){
	if( c != B_NONE && x-s >= 3 ){
	  add_score( x-s );
	  result++;
	  while( s < x )
	    Map[s++][y] |= B_CHECK_MSK;
	}
	s = x;
	c = Map[x][y] & 15;
      }
    }
  }

  for( x = 1 ; x <= 6 ; x++ ){
    c = B_NONE;
    s = 0;
    for( y = 0 ; y <= 12 ; y++ ){
      if( (Map[x][y] & 15) != c ){
	if( c != B_NONE && y-s >= 3 ){
	  add_score( y-s );
	  result++;
	  while( s < y )
	    Map[x][s++] |= B_CHECK_MSK;
	}
	s = y;
	c = Map[x][y] & 15;
      }
    }
  }

  for( x = 4 ; x > 0 ; x-- )
    result += check_diag( x,0,1 );

  for( x = 3 ; x <= 6 ; x++ )
    result += check_diag( x,0,-1 );

  for( y = 1 ; y < 10 ; y++ ){
    result += check_diag( 1,y,1 );
    result += check_diag( 6,y,-1 );
  }

  return result;
}


static int magic_fire( void )
{
  int x,y;
  int c;

  c = -1;

  for( y = 0 ; y < 12 ; y++ )
    for( x = 1 ; x <= 6 ; x++ )
      if( Map[x][y] == B_MAGIC ){
	c = Map[x][y+1];
	Map[x][y] |= B_CHECK_MSK;
	goto found;
      }
 found:

  if( c == -1 )
    return 0;

  if( c == B_WALL )
    return 1;
  
  for( y = 0 ; y < 12 ; y++ )
    for( x = 1 ; x <= 6 ; x++ )
      if( Map[x][y] == c )
	Map[x][y] |= B_CHECK_MSK;

  return 1;
}

  

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

  if( Magic )
    Erase_count = 9;
  else{
    Erase_count = 5;
    if( Multiplyer > 999 )
      Multiplyer = 999;
    
    show_score( Multiplyer,Bonus );
    Score += Bonus*Multiplyer;
    
    if( Score > 99999999 )
      Score = 99999999;
    
    Chain++;
    if( Chain < 3 )
      Multiplyer *= 4;
    else
      Multiplyer += 16;
  }
  for( y = 0 ; y < 12 ; y++ )
    for( x = 1 ; x <= 6 ; x++ )
      if( Map[x][y] & B_CHECK_MSK ){
	Erase_map[x][y] = Map[x][y];
      }else
	Erase_map[x][y] = B_NONE;
}


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

  Erase_count--;

  if( Erase_count & 1 ){
    for( y = 0 ; y < 12 ; y++ )
      for( x = 1 ; x <= 6 ; x++ )
	if( Erase_map[x][y] != B_NONE ){
	  Map[x][y] = Erase_map[x][y];
	  draw_map(x,y);
	}
  }else{
    for( y = 0 ; y < 12 ; y++ )
      for( x = 1 ; x <= 6 ; x++ )
	if( Map[x][y] & B_CHECK_MSK ){
	  Map[x][y] = Magic ? B_FIRE : B_NONE;
	  draw_map(x,y);
	}
  }
  if( Erase_count == 0 )
    show_score(0,Score);
  return  !Erase_count;
}


static int fall_all( void )
{
  int x,y;
  int r;

  for( y = 0 ; y < 12 ; y++ )
    for( x = 1 ; x <= 6 ; x++ )
      if( Map[x][y] == B_FIRE ){
	Map[x][y] = B_NONE;
	draw_map(x,y);
      }

  r = 1;
  for( y = 11 ; y > 0 ; y-- )
    for( x = 1 ; x <= 6 ; x++ )
      if( Map[x][y] == B_NONE && Map[x][y-1] != B_NONE ){
	Map[x][y] = Map[x][y-1];
	draw_map(x,y);
	Map[x][y-1] = B_NONE;
	draw_map(x,y-1);
	r = 0;
      }

  return r;
}

static void start_dead( void )
{
  Dead_line = 13;
}

static int dead( void )
{
  int x,y;
  
  for( y = 12 ; y > 0 ; y-- )
    for( x = 1 ; x <= 6 ; x++ )
      if( Map[x][y] != Map[x][y-1] ){
	Map[x][y] = Map[x][y-1];
	draw_map(x,y);
      }
  for( x = 1 ; x <= 6 ; x++ )
    if( Map[x][0] != B_NONE ){
     Map[x][0] = B_NONE;
     draw_map(x,0);
   }


  Dead_line--;
  return !Dead_line;
}


static int encount_fire( void )
{
  int x;

  if( Magic_time ){
    Magic_time--;
    return 0;
  }

  for( x = 1 ; x <= 6 ; x++ )
    if( Map[x][1] != B_NONE )
	return 1;

  return 0;
}

    
static void set_next( void )
{
  int i;

  if( encount_fire() ){
    Next_color[0] = B_MAGIC;
    Next_color[1] = B_FIRE;
    Next_color[2] = B_FIRE;
    Magic_time = 50;
  }else
    for( i = 0 ; i < 3 ; i++ )
    Next_color[i] = Block_table[ random() % Num_block ];

  Next_dir = -Next_dir;

  draw_next();
}

static void put_move( void )
{
  Map[Move_x[0]][Move_y[0]] = Move_color[0];
  Map[Move_x[1]][Move_y[1]] = Move_color[1];
  Map[Move_x[2]][Move_y[2]] = Move_color[2];
  draw_map(Move_x[0],Move_y[0]);
  draw_map(Move_x[1],Move_y[1]);
  draw_map(Move_x[2],Move_y[2]);
}

static void remove_move( void )
{
  Map[Move_x[0]][Move_y[0]] = B_NONE;
  Map[Move_x[1]][Move_y[1]] = B_NONE;
  Map[Move_x[2]][Move_y[2]] = B_NONE;
  draw_map(Move_x[0],Move_y[0]);
  draw_map(Move_x[1],Move_y[1]);
  draw_map(Move_x[2],Move_y[2]);
}

int start_game( void )
{
  int x,y;
  int i;

  show_title(0);

  Fast_fall = 0;
  Drop = 0;
  Level = 0;
  Num_block = 4;
#if 1
  Magic_time = 60;
#else
  Magic_time = 10;
#endif

  for( i =  0 ; i < 6 ; i++ )
    Block_table[i] = -1;

  for( i = 0 ; i < 6 ; i++ ){
    do{
      x = random()%6;
    }while( Block_table[x] != -1 );
    Block_table[x] = i;
  }

  for( y = 0 ; y < 12 ; y++ ){
    Map[0][y] = B_WALL;
    draw_map(0,y);
    Map[7][y] = B_WALL;
    draw_map(7,y);
    for( x = 1 ; x <= 6 ; x++ ){
      Map[x][y] = B_NONE;
      draw_map( x,y );
    }
  }
  for( x = 0 ; x < 8 ; x++ ){
    Map[x][12] = B_WALL;
    draw_map( x,y );
  }

  State = S_PREPAIR;

  Score = 0;
  show_score(0,0);
  show_level(1);
  if( random() % 100 < 50 )
    Next_dir = -1;
  else
    Next_dir = 1;

  set_next();
  return 1000;
}

static int prepair_move( void )
{
  int r;
  static int mul_time[] = {256,192,144,108,81};

  Move_dir = Next_dir;

  if( Move_dir == -1 ){
    Move_x[0] = 4;
    Move_x[1] = 4;
    Move_x[2] = 3;
  }else{
    Move_x[0] = 3;
    Move_x[1] = 3;
    Move_x[2] = 4;
  }
  Move_y[0] = 1;
  Move_y[1] = 0;
  Move_y[2] = 0;

  Move_color[0] = Next_color[0];
  Move_color[1] = Next_color[1];
  Move_color[2] = Next_color[2];

  Fast_fall = 0;
  Multiplyer = 0;
  Chain = 0;
  r = check_dead();
  set_next();
  put_move();

  if( ++Drop == 10 ){
    Level++;
    Drop = 0;
    show_level( Level+1 );
  }
  Fall_time = 1200 - (Level / 5) * 300;
  Fall_time = Fall_time * mul_time[Level%5]/256;

  if( Fall_time < 300 )
    Fall_time = 300;
  
  if( Level < 10 )
    Num_block = Level/5 + 4;
  else
    Num_block = 6;

  return !r;
}


static int avail_move( int dx )
{
  int x;

  if( Move_dir == dx )
    x = Move_x[2] + dx;
  else
    x = Move_x[1] + dx;
  
  if( Map[x][Move_y[2]] != B_NONE )
    return 0;
  if( Map[Move_x[0]+dx][Move_y[0]] != B_NONE )
    return 0;

  return 1;
}

static int avail_fall( void )
{
  if( Map[Move_x[0]][Move_y[0]+1] != B_NONE )
    return 0;

  if( Map[Move_x[2]][Move_y[2]+1] != B_NONE )
    return 0;

  return 1;
}

static int fall( void )
{

  if( !avail_fall() )
    return 0;

  remove_move();
  Move_y[0]++;
  Move_y[1]++;
  Move_y[2]++;
  put_move();

  if( Fast_fall )
    return 50;
  else
    return Fall_time;
}


void change_color( int dir )
{
  int c;

  if( State != S_MOVE )
    return;

  if( dir ){
    c = Move_color[0];
    Move_color[0] = Move_color[1];
    Move_color[1] = Move_color[2];
    Move_color[2] = c;
  }else{
    c = Move_color[2];
    Move_color[2] = Move_color[1];
    Move_color[1] = Move_color[0];
    Move_color[0] = c;
  }
  put_move();
}


void move_right( void )
{
  if( State != S_MOVE )
    return;
  if( avail_move(1) ){
    remove_move();
    Move_x[0]++;
    Move_x[1]++;
    Move_x[2]++;
  }
  put_move();
}

void move_left( void )
{
  if( State != S_MOVE )
    return;
  if( avail_move(-1) ){
    remove_move();
    Move_x[0]--;
    Move_x[1]--;
    Move_x[2]--;
  }
  put_move();
}

void rotate( void )
{
  int c;

  if( State != S_MOVE )
    return;

  remove_move();

  if( Map[Move_x[0]+Move_dir][Move_y[0]] == B_NONE ){
    Move_x[0] = Move_x[2];
    Move_x[2] = Move_x[1];
    Move_x[1] = Move_x[0];
    Move_dir = -Move_dir;
  }else if( Map[Move_x[1]-Move_dir][Move_y[1]] == B_NONE ){
    Move_x[2] = Move_x[1] - Move_dir;
    Move_dir = -Move_dir;
  }
#if 0
  c = Move_color[1];
  Move_color[1] = Move_color[2];
  Move_color[2] = c;
#endif
  
  put_move();

}

int fall_on( void )
{
  if( State != S_MOVE )
    return 0;

  Fast_fall = 1;
  return 1;
}


int push_start( void )
{
  if( State == S_TITLE )
    return 1;
  else if( State == S_END )
    return 2;
  else
    return 0;
}


void fall_off( void )
{
  Fast_fall = 0;
}


int game( void )
{
  int t;

  t = 0;

  do{
    switch( State ){
    case S_TITLE:
      show_title(-1);
      t = 500;
      break;
      
    case S_PREPAIR:
      if( prepair_move() ){
	State = S_MOVE;
	t = 500;
      }else{
	start_dead();
	t = 500;
	State = S_DEAD;
      }
      break;

    case S_MOVE:
      t = fall();
      if( t == 0 ){
	State = S_FALL;
	t = 60;
      }
      break;

    case S_CHECK:
      Bonus = 0;
      Line = 0;
      if( magic_fire() ){
	Magic = 1;
	start_erase();
	State = S_ERASE;
	t = 20;
      }else if( check_erase() ){
	Magic = 0;
	start_erase();
	State = S_ERASE;
	t = 20;
      }else{
	State = S_PREPAIR;
	t = 500;
      }
      break;

    case S_ERASE:
      if( erase() ){
	State = S_FALL;
      }else
	t = 200;
      break;

    case S_FALL:
      if( fall_all() ){
	State = S_CHECK;
	t = 500;
      }else
	t = 60;
      break;

    case S_DEAD:
      if( dead() ){
	show_gameover(1);
	State = S_END;
	t = 10000;
      }else
	t = 60;
      break;

    case S_END:
      save_highscore( Score );
      show_gameover(0);
      show_title(1);
      State = S_TITLE;
      t = 500;
      break;
    }
  }while( t == 0 );

  return t;

}

void init_game( void )
{
  int x,y;

  for( y = 0 ; y < 13 ; y++ )
    for( x = 0 ; x < 8 ; x++ )
      Map[x][y] = B_NONE;

  Next_dir = 0;
  State = S_TITLE;
}
