/*
 * Stonage: a puzzle game
 * Copyright (C) 2001 Luc Saillard <luc@saillard.org>
 *
 * Routine to start a new game, do transition between each turns ...
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include "stonage.h"

#include <SDL.h>
#if defined(HAVE_SDL_SDL_IMAGE_H)
#include <SDL/SDL_image.h>
#endif

#include "game.h"
#include "game_enterlevel.h"
#include "game_screen.h"
#include "screen.h"
#include "level.h"
#include "level_io.h"
#include "level_code.h"
#include "level_screen.h"
#include "image.h"
#include "music.h"

static enum game_menu_option Game_Menu(void);
static int Game_PlayLevel(void);
static int Game_Run(struct t_level *lvl);
static int Game_Run_Manage_Animation(struct t_game *g, struct t_level *l);
static enum game_return_action Game_Run_Manage_Key(struct t_level *lvl, const SDL_KeyboardEvent *event);
static enum game_return_action Game_Run_Manage_Automatic_Square(struct t_level *lvl, const SDL_UserEvent *event);
static enum game_return_action Game_Run_Manage_Automatic_Dino(struct t_level *lvl, const SDL_UserEvent *event);

extern SDL_Surface *screen; /* Declared in main.c */

char *datadir_path[]=
{
  STONAGE_DATA_DIR,
  ".",
  "../data",
  NULL
};

struct t_game game;

/*
 * Function: Game_Menu_Loop
 *
 * Description:
 *   This function manage the result of the main menu. With this menu, the user
 *   can choose to:
 *     * start a new level
 *     * change musik
 *     * quit the game ...
 *     ...
 * Input:
 * Output:
 */
void
Game_Menu_Loop(void)
{
  while (1)
   {
     trace(" ");
     switch(Game_Menu())
      {
       case GAME_MENU_START_GAME:
	 Game_PlayLevel();
	 break;
       case GAME_MENU_ENTER_LEVEL:
	 if (Game_EnterLevel(screen))
	   Game_PlayLevel();
	 break;
       case GAME_MENU_QUIT:
	 trace("Want to quit");
	 return;
       case GAME_MENU_CHANGE_DIFFICULTY:
	 game.difficulty++;
	 if (game.difficulty>=DIFFICULT_LEVEL)
	   game.difficulty=0;
	 trace("difficulty=%d",game.difficulty);
	 break;
       default:
	 trace("game menu option not reconized");
      }
   }
}

/*
 * Function: Game_PlayLevel
 *
 * Description:
 *   This launch the game level specified by (game.turns). When the user
 *   wins a new level is launched. When the user loose, the function draw
 *   a loose splash screen
 *
 * Input:
 * Output:
 *   return a code to indicate if the user have loose,won, ...
 */
static int
Game_PlayLevel(void)
{
  struct t_level *lvl;
  char levelname[FILENAME_MAX];
  int action;
  int game_won;

  /* Load Music */
#ifdef ENABLE_SOUND
  Music_Load_Module_Random("music");
  Music_Start();
#endif

  /* Draw a new level interscreen */
  Game_ScreenBegin(screen);

  /* Initialise some variables */
  game.score=0;
  game.candles=5;
  game_won=0;

  while (1)
   {
     /* Load Level */
     snprintf(levelname,FILENAME_MAX,"level%2.2d.lvl",game.level);
     lvl = Level_Load(levelname);
     if (!lvl)
      {
	trace("Can't load the level");
#ifdef ENABLE_SOUND
	Music_Stop();
#endif
	return 0;
      }
     if (!Level_Sanity_Check(lvl))
      {
	trace("This level is corrupted");
#ifdef ENABLE_SOUND
	Music_Stop();
#endif
	return 0;
      }

     game.time_remaining = lvl->times[game.difficulty];

     /* Do a cross fading with the last screen */
      {
	SDL_Surface *lvl_screen;
	lvl_screen = SDL_CreateRGBSurface(SDL_HWSURFACE,screen->w,screen->h,
	    screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
	SDL_FillRect(lvl_screen,NULL,0);
	Screen_Fade(screen,lvl_screen);
	Level_Draw_All(lvl,lvl_screen);
	Screen_Fade(screen,lvl_screen);
	SDL_FreeSurface(lvl_screen);
      }
     /* Run the game on this level */
     SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
     action = Game_Run(lvl);
     SDL_EnableKeyRepeat(0,0);
     Level_Free(lvl);
     switch (action)
      {
       case GAME_USER_LOSE:
	 /* Player Lose (Timeout) */
#ifdef ENABLE_SOUND
	 Music_Stop();
#endif
	 game_won=0;
	 game.candles--;
	 if (game.candles>0)
	  {
	    Game_ScreenTimeout(screen);
	    break;
	  }
	 else
	  {
	    Game_ScreenLose(screen);
	    return 0;
	  }
       case GAME_USER_WANT_TO_QUIT:
	 /* Player want to quit */
	 Game_ScreenLose(screen);
#ifdef ENABLE_SOUND
	 Music_Stop();
#endif
	 return 0;
	 break;
       case GAME_USER_WIN:
	  {
	    /* Player win */
	    int score_level=(++game_won*(1+(RANDOM()%(game.level*10))));
	    game.score+=score_level;
	    game.level++;
	    Game_ScreenWin(screen);
	    break;
	  }
       default:
	 trace("unknown Game_Run action %d",action);
      }
   }
}

/**
 *
 * Draw to screen the intro
 *
 */
void
Game_SplashScreen()
{
  SDL_Surface *image;
  SDL_Event event;

  image = Image_Load("images/splash.jpg",1);
  if (image!=NULL)
   {
     SDL_FillRect(screen,NULL,0);
     SDL_Flip(screen);
     Screen_Fade(screen,image);

     /* wait for key or button */
     while (1)
      {
	SDL_WaitEvent(&event);
	if (event.type == SDL_QUIT)
	  Game_Quit();
	if (event.type == SDL_KEYUP || event.type == SDL_MOUSEBUTTONUP)
	  break;
      }
     SDL_FreeSurface(image);
   } /* if (image) */
}

/**
 * Function: Game_Menu
 *
 * Description:
 *   This function is responsible to draw the main menu, and wait an action
 *   choosen by the user. When an action occured, the function returns a code
 *   to indicate this choice.
 *
 * Input:
 * Output:
 *   return a code to indicate if the user have loose,won, ...
 */
static enum game_menu_option
Game_Menu(void)
{
  SDL_Surface *image=NULL;
  SDL_Event event;

  image = Image_Load("images/menu.jpg",1);
  if (image==NULL)
    Game_Quit();
  Screen_Fade(screen,image);
  SDL_FreeSurface(image);

  while (SDL_PollEvent(&event)>0)
    ;

  while (1)
   {
     SDL_WaitEvent(&event);
     if (event.type == SDL_QUIT)
       Game_Quit();
     if (event.type == SDL_KEYUP)
      {
	switch(event.key.keysym.sym)
	  {
	   case SDLK_F1:
	     return GAME_MENU_START_GAME;
	   case SDLK_F2:
	     return GAME_MENU_ENTER_LEVEL;
	   case SDLK_F6:
	     return GAME_MENU_CHANGE_DIFFICULTY;
	   case SDLK_q:
	     return GAME_MENU_QUIT;
	   default:
	     break;
	  }
      }
   }
}

/**
 *
 */
void
Game_Quit(void)
{
  if (screen)
    SDL_FreeSurface(screen);
  SDL_Quit();
  exit(0);
}

struct
{
  int keysym;
  int direction;
} static move_actions[] =
{
   {SDLK_DOWN,MOVE_DOWN},
   {SDLK_UP,MOVE_UP},
   {SDLK_RIGHT,MOVE_RIGHT},
   {SDLK_LEFT,MOVE_LEFT},
   {0,0}
};

Uint32 callback_animation(Uint32 interval, void *data)
{
  SDL_Event e;
  struct t_level *lvl=(struct t_level *)data;

  if (lvl->control == CONTROL_KINESIST ||
      lvl->control == CONTROL_SQUARE)
   {
     e.type = AUTOMATIC_UPDATE_KINESIST;
     e.user.code = 0;
     SDL_PushEvent(&e);

     /* Update the sprite */
     lvl->kinesist_sprite=lvl->kinesist_sprite->next;
   }
  return interval;
}

Uint32 callback_update_timer(Uint32 interval, void *data)
{
  SDL_Event e;

  e.type = AUTOMATIC_UPDATE_TIMER;
  e.user.code = 0;
  SDL_PushEvent(&e);

  return interval;
}

#define USE_TIMER 1

/**
 *
 * This is the main loop function which play the game
 *
 */
static int
Game_Run(struct t_level *lvl)
{
  SDL_Event event;
#if USE_TIMER
  SDL_TimerID animation_id, timer_id;
#else
  int start, elapsed, seconds, next_time4animation;
#endif

  SDL_EventState(SDL_MOUSEMOTION,SDL_DISABLE); /* Clear the stack :( */
#if USE_TIMER
  animation_id = SDL_AddTimer(150, callback_animation, lvl);
  timer_id = SDL_AddTimer(1000, callback_update_timer, lvl);
#else
  start = SDL_GetTicks();
  seconds = next_time4animation = 0;

  lvl->time_start = SDL_GetTicks();
  game.time_animation = SDL_GetTicks();
#endif

  while (1)
   {
     /* This is our own SDL_WaitEvent(&event) */
#if USE_TIMER
     SDL_WaitEvent(&event);
#else
     SDL_PumpEvents();
     switch(SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_ALLEVENTS))
      {
       case 0:
	 Game_Run_Manage_Animation(&game,lvl);

	 if (game.time_elapsed > game.time_remaining)
	   return GAME_USER_LOSE;
	 
	 continue;
       case -1:
	 continue;
       case 1:
#endif
	 switch (event.type)
	  {
	   case SDL_QUIT:
	     Level_Free(lvl);
	     Game_Quit();
	     break;
	   case SDL_KEYDOWN:
	     switch(Game_Run_Manage_Key(lvl,&event.key))
	      {
	       case GAME_USER_HAS_MOVED:
		 Level_DoPostMoveAction(lvl);
		 Level_Update_Animation(lvl,screen);
		 SDL_Flip(screen);
		 if (lvl->control==CONTROL_PLAYER && Level_IsFinish(lvl))
		  {
#if USE_TIMER
		    SDL_RemoveTimer(animation_id);
		    SDL_RemoveTimer(timer_id);
#endif
		    return GAME_USER_WIN;
		  }
		 break;
	       case GAME_USER_WANT_TO_QUIT:
		  {
#if USE_TIMER
		    SDL_RemoveTimer(animation_id);
		    SDL_RemoveTimer(timer_id);
#endif
		    return GAME_USER_WANT_TO_QUIT;
		  }
	       case GAME_SQUARE_HAS_MOVED:
	       case GAME_KINESIST_HAS_MOVED:
		 Level_Update_Animation(lvl,screen);
		 SDL_Flip(screen);
		 break;
	       case GAME_NEED_REFRESH_SCREEN:
		 Level_Draw_All(lvl,screen);
		 SDL_Flip(screen);
		 break;
	       default:
		 break;
	      }
	     break;
	   case AUTOMATIC_MOVE_SQUARE:
	     if (Game_Run_Manage_Automatic_Square(lvl,&event.user)!=GAME_NO_ACTION_DONE)
	      {
		Level_Update_Animation(lvl,screen);
		SDL_Flip(screen);
	      }
	     break;
	   case AUTOMATIC_MOVE_DINOSAUR:
	     if (Game_Run_Manage_Automatic_Dino(lvl,&event.user)!=GAME_NO_ACTION_DONE)
	      {
		Level_Update_Animation(lvl,screen);
		SDL_Flip(screen);
	      }
	     break;
	   case AUTOMATIC_UPDATE_KINESIST:
	     Anim_Push(ANIMATION_KINESIST,0,0);
	     Level_Update_Animation(lvl,screen);
	     SDL_Flip(screen);
	     break;
	   case AUTOMATIC_UPDATE_TIMER:
	     game.time_remaining--;
	     if (game.time_remaining<1)
	      {
#if USE_TIMER
		SDL_RemoveTimer(animation_id);
		SDL_RemoveTimer(timer_id);
#endif
		return GAME_USER_LOSE;
	      }
	     Level_Update_Board(lvl,screen);
	     SDL_Flip(screen);
	     break;
	  } /* End of switch(event.type) */
#if 0
      } /* switch SDL_PeepEvents() */
#endif
   } /* End of while(1) */
}

/**
 *
 * This function do actions when the user press a key on the keyboard while the game
 * is running.
 *
 */
static enum game_return_action
Game_Run_Manage_Key(struct t_level *lvl, const SDL_KeyboardEvent *event)
{
  int i,direction;
  int dino_hasmoved;
  SDL_Event e;

  switch(event->keysym.sym)
   {
    case SDLK_q:
      return GAME_USER_WANT_TO_QUIT;
      break;
      /* When the user press the space bar:
       *   if the user control the player => go to in mode kinesist
       *   if the user control the kinesist and the square is moveable
       *     => try to move the square, or select it
       *   if the user control the square => go to in mode player
       */
    case SDLK_SPACE: /* Popup a carre to move a square */
      switch (lvl->control)
       {
	case CONTROL_PLAYER:
	  /* Now we control the select square */
	  lvl->control=CONTROL_KINESIST;
	  lvl->update_kinesist_sprite=1;
	  lvl->xkinesist=lvl->xdino;
	  lvl->ykinesist=lvl->ydino;
	  Anim_Push(ANIMATION_KINESIST,0,0);
	  return GAME_KINESIST_HAS_MOVED;
	case CONTROL_SQUARE:
	  lvl->control=CONTROL_KINESIST;
	  lvl->update_kinesist_sprite=1;
	  Anim_Push(ANIMATION_KINESIST,0,0);
	  return GAME_KINESIST_HAS_MOVED;
	case CONTROL_KINESIST:
	  /* Find if a moveable square is under the kinesist sprite */
	  if ((direction=Level_CanMoveSquare(lvl))!=0)
	   {
	     trace("Ok this square can move ");
	     /* The square is moveable => give control to the square */
	     lvl->control=CONTROL_SQUARE;
	     lvl->update_kinesist_sprite=1;
	     /* If the square can move in only one direction, move it NOW */
	     e.type = AUTOMATIC_MOVE_SQUARE;
	     switch(direction)
	      {
	       case CAN_MOVE_TO_WEST:
		 Level_MoveSquareTo(lvl,MOVE_LEFT);
		 e.user.code = MOVE_LEFT;
		 SDL_PushEvent(&e);
		 return GAME_SQUARE_HAS_MOVED;
	       case CAN_MOVE_TO_EAST:
		 Level_MoveSquareTo(lvl,MOVE_RIGHT);
		 e.user.code = MOVE_RIGHT;
		 SDL_PushEvent(&e);
		 return GAME_SQUARE_HAS_MOVED;
	       case CAN_MOVE_TO_NORTH:
		 Level_MoveSquareTo(lvl,MOVE_UP);
		 e.user.code = MOVE_UP;
		 SDL_PushEvent(&e);
		 return GAME_SQUARE_HAS_MOVED;
	       case CAN_MOVE_TO_SOUTH:
		 Level_MoveSquareTo(lvl,MOVE_DOWN);
		 e.user.code = MOVE_DOWN;
		 SDL_PushEvent(&e);
		 return GAME_SQUARE_HAS_MOVED;
	      }
	     //Anim_Push(ANIMATION_KINESIST,0,0);
	     return GAME_KINESIST_HAS_MOVED;
	   }
	  else /* It's not a moveable square, so give control to the player */
	   {
	     lvl->control=CONTROL_PLAYER;
	     lvl->update_kinesist_sprite=1;
	     Anim_Push(ANIMATION_DINO,lvl->xkinesist,lvl->ykinesist);
	     return GAME_KINESIST_HAS_MOVED;
	   }
	  trace("CORRECT ME, i can't go to this line");
	  break;
       }
      return GAME_NEED_REFRESH_SCREEN;
    /* It's generic key (like cursor key ) */
    default:
      for (i=0;move_actions[i].keysym != 0; i++)
       {
	 if (event->keysym.sym == move_actions[i].keysym)
	  {
	    switch (lvl->control)
	     {
	      case CONTROL_PLAYER:
		dino_hasmoved=Level_MoveDinoTo(lvl,move_actions[i].direction,0);
		switch(dino_hasmoved)
		 {
		  case 2:
		   e.type = AUTOMATIC_MOVE_DINOSAUR;
		   e.user.code = move_actions[i].direction;
		   SDL_PushEvent(&e);
		  case 1:
		   return GAME_USER_HAS_MOVED;
		 }
		break;
	      case CONTROL_KINESIST:
		if (Level_MoveKinesistTo(lvl,move_actions[i].direction))
		  return GAME_KINESIST_HAS_MOVED;
		else
		  return GAME_NO_ACTION_DONE;
	      case CONTROL_SQUARE:
		if (Level_MoveSquareTo(lvl,move_actions[i].direction))
		 {
		   e.type = AUTOMATIC_MOVE_SQUARE;
		   e.user.code = move_actions[i].direction;
		   SDL_PushEvent(&e);
		   return GAME_SQUARE_HAS_MOVED;
		 }
		return GAME_NO_ACTION_DONE;
	     }
	    /* We have not move anything , quit the for() */
	    break;
	  }
       }
      return GAME_NO_ACTION_DONE;
   } /* End of switch(event.key.keysym.sym) */
}

/**
 *
 * This function do some automatic action like move of square.
 *
 */
static enum game_return_action
Game_Run_Manage_Automatic_Square(struct t_level *lvl, const SDL_UserEvent *event)
{
  /* First try to move the square */
  if (Level_MoveSquareTo(lvl,event->code))
   {
     /* Ok, the square move, so repeat the same action */
     SDL_Event e;
     SDL_EventState(SDL_KEYDOWN,SDL_DISABLE); /* Clear the stack :( */
     e.type = AUTOMATIC_MOVE_SQUARE;
     e.user.code = event->code;
     SDL_PushEvent(&e);
     return GAME_SQUARE_HAS_MOVED;
   }
  /* Second, the square can move again ? */
  else if (Level_CanMoveSquare(lvl))
   {
     /* Yes, so give control to the square */
     lvl->control = CONTROL_SQUARE;
     SDL_EventState(SDL_KEYDOWN,SDL_ENABLE); /* Clear the stack :( */
     return GAME_NEED_REFRESH_SCREEN;
   }
  else
   {
     int last_lvl_control = lvl->control;
     /* The square can't move again ?, give control to the player to move kinesist */
     lvl->control = CONTROL_KINESIST;
     lvl->update_kinesist_sprite=1;
     SDL_EventState(SDL_KEYDOWN,SDL_ENABLE); /* Clear the stack :( */
     if (last_lvl_control==lvl->control)
       return GAME_NO_ACTION_DONE;
     Anim_Push(ANIMATION_KINESIST,0,0);
     return GAME_NEED_REFRESH_SCREEN;
   }
}

/**
 *
 * This function do some automatic action like move dinosaur
 *
 */
static enum game_return_action
Game_Run_Manage_Automatic_Dino(struct t_level *lvl, const SDL_UserEvent *event)
{
  int dino_havemoved;
  SDL_Event e;

  /* First try to move the dinosaur */
  dino_havemoved=Level_MoveDinoTo(lvl,event->code,1);
  switch(dino_havemoved)
   {
    case 2:
     /* Ok, the square move, so repeat the same action */
     SDL_EventState(SDL_KEYDOWN,SDL_DISABLE); /* Clear the stack :( */
     e.type = AUTOMATIC_MOVE_DINOSAUR;
     e.user.code = event->code;
     SDL_PushEvent(&e);
    case 1:
     return GAME_USER_HAS_MOVED;
    default:
     SDL_EventState(SDL_KEYDOWN,SDL_ENABLE); /* Clear the stack :( */
     return GAME_NO_ACTION_DONE;
   }
}

static int
Game_Run_Manage_Animation(struct t_game *g, struct t_level *l)
{
  int elapsed_time, current_time;

  
  current_time = SDL_GetTicks();
  elapsed_time = current_time - g->time_animation;
  g->time_animation = current_time;

  /* Each second, we need to decrement the counter time_remaining */
  if (elapsed_time > 1000 && !g->game_paused)
    g->time_elapsed+=elapsed_time;

  SDL_Delay(10);


  //FIXME: this code not compile
#if 0
  if (elapsed_time - g->time_start > seconds*1000)
   {
     seconds = 1 + (int)(elapsed/1000);
     game->time_remaining = lvl->times[game->difficulty] - seconds;
     if (game->time_remaining<1)
       return GAME_USER_LOSE;
     Level_Update_Board(lvl,screen);
     SDL_Flip(screen);
   }
#endif

  return 0;
}


