/*  The Blue Mango Quest
 *  Copyright (c) Clément 'phneutre' Bourdarias (code)
 *                   email: phneutre@users.sourceforge.net
 *                Guillaume 'GuBuG' Burlet (graphics)
 *                   email: gubug@users.sourceforge.net
 *
 *  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 Library 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.
 */

#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>

#ifdef HAVE_SDL_MIXER
# include <SDL/SDL_mixer.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "world_building.h"
#include "map.h"
#include "timers.h"
#include "world_geometry.h"
#include "hut.h"
#include "bonus.h"
#include "mango.h"
#include "game_loop.h"
#include "system_gl.h"
#include "draw_scene_gl.h"
#include "sector.h"
#include "move.h"
#include "hud.h"
#include "sounds.h"
#include "menus.h"

#define MOUSE_REFRESH_TIME 10 //in milliseconds

extern player_t *player;
extern game_data_t *world;
extern options_t *options;
extern hud_t *hud;

int mouse_rel_x=0, mouse_rel_y=0;
int old_mouse_x=0, old_mouse_y=0;
double smoothed_mouse_x=0, smoothed_mouse_y=0;

int time_since_last_mouse_update=0;
int last_mouse_update_date=0;
char need_winning_post_message=1;

char one_minute_w_played=0, countdown_w_played=0;

void new_round()
{
  reset_player_pos();
  all_shmol_go_home();
  player->status = STATUS_WAITING;
  world->mat_amb_diff[0] = 0.937;
  world->mat_amb_diff[1]= 0.937;
  world->mat_amb_diff[2] = 0.937;
  world->mat_amb_diff[3] = 1.0;
}

void special_loop(char status)
{
  int loop_end=0;
  CHRONOMETRE *loop_timer=0;

  if (status == STATUS_DEAD)
    {
      loop_timer = new CHRONOMETRE;
      loop_timer->initialise();
      loop_timer->setAlarm(2);
    }

  player->mySector = get_sector(player->pos_x, player->pos_z);
  if (status != STATUS_PAUSE) 
    {
      if (world->current_bonus > -1) 
	world->tab_bonus[world->current_bonus]->cancelAction();
      world->current_bonus = -1 ;
    }

  while(!(loop_end))
    {
      world->game_sync->update_temps();
      world->game_timer->update_temps();
      if (status == STATUS_DEAD)
	{
	  if (loop_timer->update_temps()) loop_end=1;
	}

      if (status == STATUS_PAUSE) update_bonus();

      {
	SDL_Event event;

	while (SDL_PollEvent(&event))
	  {
	    if (event.type == SDL_KEYDOWN)
	      {
		switch (event.key.keysym.sym)
		  {
		  case SDLK_SPACE:
		    if ((status == GAME_OVER)||(status == STATUS_WIN)) 
		      world->gameEnd=1;
		    loop_end = 1;
		    break;
		    
		  case SDLK_ESCAPE:
		    if ((status != STATUS_DEAD)
			|| (status != STATUS_PAUSE)) world->gameEnd = 1;
		    
		    loop_end = 1;
		    break;

		  case SDLK_p:
		    if (status == STATUS_PAUSE)
		      loop_end=1;
		    
		  default:
		    break;
		  }
	      }

	    if (event.type == SDL_MOUSEBUTTONDOWN)
	      {
		if ((status == GAME_OVER)||(status == STATUS_WIN)) 
		  world->gameEnd=1;
		loop_end = 1;
	      }

	  }
      }
  
      draw_entire_scene_GL();
    }
  
  switch (status)
    {
    case STATUS_WAITING:
      player->status = ETAT_POURSUIVI;
      SDL_GetRelativeMouseState(&mouse_rel_x,&mouse_rel_y);
      break;

    case STATUS_DEAD:
      new_round();
      break;

    case STATUS_WIN:
    case STATUS_PAUSE:
      SDL_GetRelativeMouseState(&mouse_rel_x,&mouse_rel_y);
      break;
    }

  if (status == STATUS_DEAD) delete loop_timer;
}

int mainLoop()
{
  world->gameEnd = 0;
  world->need_to_restart_map=0;
  world->in_ingame_menu=0;
  one_minute_w_played=0;
  countdown_w_played=0;

  world->game_sync->initialise();

  world->game_timer->resetTemps();
  world->game_timer->setAlarm(world->time_limit);
  last_mouse_update_date=0;
  need_winning_post_message=1;

  printf ("\n- Falling into the main game loop -\n");
  player->mySector = get_sector(player->pos_x, player->pos_z);

  hud_new_message("Map loaded successfully");
  if (world->finish_square >=0) 
    hud_new_message("This map contains a winning post");

  play_music();
  while (!(world->gameEnd))
  {
    update_mouse_moves_only();
    if ((player->status == STATUS_WAITING) || (player->status == STATUS_WIN)
	|| (player->status == STATUS_DEAD) || (player->status == GAME_OVER))
      {
	world->game_timer->update_temps();
	world->game_timer->togglePaused();
	special_loop(player->status);
	world->game_timer->togglePaused();
    }

    if (world->game_paused)
      {
	pause_toggle_music();
	world->game_paused = 1;
	halt_sounds_on_all_channels();
	pause_game();
	special_loop(STATUS_PAUSE);
	unpause_game();
	world->game_paused = 0;
	pause_toggle_music();
      }

    if (world->in_ingame_menu)
      {
	world->game_paused = 1;
	halt_sounds_on_all_channels();
	pause_game();
	ingame_menu_loop();
	unpause_game();
	world->game_paused = 0;
	world->in_ingame_menu=0;
      }

    world->game_sync->update_temps();
    if ((world->game_timer->update_temps())&&(world->time_limit)) {
      printf("Time limlit hit! Game over.\n");
      game_over();
    }

    update_input();

    player->mySector = get_sector(player->pos_x, player->pos_z);
    if (!(player->mySector)) 
      {
	fprintf(stderr, "Error: Not In A Sector (this is a known bug).\n");
	exit(1);
      }

    update_special();
    update_ia();
    update_bonus();
    update_dituboxes();

    draw_entire_scene_GL();

  }
  stop_music();
  halt_sounds_on_all_channels();
  reset_fog_state();
  printf ("- Exiting from the main game loop -\n");
  return 1;
}

void update_input()
{
  extern skirmish_menu_t *sk_menu;
  extern main_menu_t *main_menu;
  char is_mouse_motion=0;
  Uint8 *keys;
  SDLMod modifs;
  //char buffer[30];

////////////////////////////////////////////            
  {
    SDL_Event event;

    while (SDL_PollEvent(&event))
    {
      if (event.type == SDL_KEYDOWN)
      {

        switch (event.key.keysym.sym)
        {
        case SDLK_ESCAPE:
	  world->in_ingame_menu=1;
          break;

	case SDLK_F12:
	  sk_menu->loop_end=1;
	  main_menu->loop_end=1;
          world->gameEnd = 1;
	  break;

	case SDLK_F1:
	  use_stocked_bonus(E_ARMAGGEDON);
          break;

	case SDLK_F10:
	  pause_game();
	  options_menu_loop(1);
	  unpause_game();
	  SDL_GetRelativeMouseState(&mouse_rel_x,&mouse_rel_y);
          break;

        case SDLK_F2:
	  use_stocked_bonus(E_ONDEDECHOC);
          break;
        case SDLK_F3:
	  use_stocked_bonus(E_RAPIDE);
          break;
        case SDLK_F4:
	  use_stocked_bonus(E_STOPTEMPS);
          break;

	case SDLK_TAB:
	  if (hud->draw_bigminimap) hud->draw_bigminimap=0;
	  else hud->draw_bigminimap=1;
	  break;

	case SDLK_p:
	  world->game_paused = 1;
	  break;

	case SDLK_m:
	  SDL_WM_GrabInput(SDL_GRAB_OFF);
	  //if (!(hud_new_message("This is a HUD message"))) {
	  //  printf("Error: no slot available\n");
	  //  exit(1);
	  //}
	  break;

	case SDLK_n:
	  SDL_WM_GrabInput(SDL_GRAB_ON);
	  break;

        default:
          break;
       }
      }

       if (event.type == SDL_MOUSEMOTION)
 	{
	  is_mouse_motion=1;
 	}

       if (event.type == SDL_MOUSEBUTTONDOWN)
 	{
	  switch (event.button.button)
	    {
	    case SDL_BUTTON_LEFT:
	      switch(player->selected_stocked_bonus)
		{
		case 0:
		  use_stocked_bonus(E_ARMAGGEDON);
		  break;

		case 1:
		  use_stocked_bonus(E_ONDEDECHOC);
		  break;
		case 2:
		  use_stocked_bonus(E_RAPIDE);
		  break;
		case 3:
		  use_stocked_bonus(E_STOPTEMPS);
		  break;
		default: break;
		}
	      player->selected_stocked_bonus=-1;
	      break;

	    case SDL_BUTTON_RIGHT:
	      if (has_player_stock()) {
		if (player->selected_stocked_bonus < 3)
		  player->selected_stocked_bonus++;
		else player->selected_stocked_bonus=0;
	      }
	      break;
	    }
 	}
    }
  }
////////////////////////////////////////////    

  keys = SDL_GetKeyState(NULL);
  modifs = SDL_GetModState();

  if (modifs & KMOD_ALT)
    {
      if (!player->slide)
	player->slide = 1;
    }
  else if (player->slide)
    player->slide = 0;

  update_mouse_moves_only();

  if (keys[player->theKeys[KEY_UP]] == SDL_PRESSED)
    {
      move_forward();
    }

  if (keys[player->theKeys[KEY_DOWN]] == SDL_PRESSED)
    {
      move_backward();
    }

  if (options->alternate_controls)
    {
if (((player->slide == SHX_LATERAL) || (player->slide == SHX_DROITE))
      && (keys[player->theKeys[KEY_RIGHT]] == SDL_PRESSED))
  {
    move_slide_right();
    if (keys[player->theKeys[KEY_LEFT]] == SDL_PRESSED)
      rotate(-25);
  }

  if ((keys[player->theKeys[KEY_RIGHT]] != SDL_PRESSED) && (player->slide))
    player->slide = SHX_LATERAL;

  if (((player->slide == SHX_LATERAL) || (player->slide == SHX_GAUCHE))
      && (keys[player->theKeys[KEY_LEFT]] == SDL_PRESSED))
  {
    move_slide_left();
    if (keys[player->theKeys[KEY_RIGHT]] == SDL_PRESSED)
      rotate(25);
  }

  if ((keys[player->theKeys[KEY_LEFT]] != SDL_PRESSED) && (player->slide))
    player->slide = SHX_LATERAL;

  if ((keys[player->theKeys[KEY_RIGHT]] == SDL_PRESSED) && (!(player->slide)))
  {
    rotate(25);
  }

  if ((keys[player->theKeys[KEY_LEFT]] == SDL_PRESSED) && (!(player->slide)))
  {
    rotate(-25);
  }
    }

  else {

    if (keys[SDLK_a] == SDL_PRESSED) { rotate(-20);}
    if (keys[SDLK_z] == SDL_PRESSED) { rotate(20);}

    if (keys[player->theKeys[KEY_RIGHT]] == SDL_PRESSED)
      {
	if (options->alternate_controls) 
	  rotate(20);
	else move_slide_right();
      }

    if (keys[player->theKeys[KEY_LEFT]] == SDL_PRESSED)
      {
	if (options->alternate_controls)
	  rotate(-20);
	else move_slide_left();
      }
  }

  if (keys[SDLK_PAGEUP] == SDL_PRESSED)
    {
      look_around(SHX_HAUT,15);
    }

  if (keys[SDLK_PAGEDOWN] == SDL_PRESSED)
    {
      look_around(SHX_BAS,15);
    }

  if (keys[SDLK_END] == SDL_PRESSED)
    {
      look_around(SHX_CENTRE,1);
    }

}

void update_mouse_moves_only()
{  
  SDL_GetRelativeMouseState(&mouse_rel_x,&mouse_rel_y);

  if (last_mouse_update_date == 0) {
    mouse_rel_x=0;
    last_mouse_update_date=1;
  }

  if (whichBonusActive() == E_INVERTTOUCHES)
    {
      mouse_rel_x = -mouse_rel_x;
      mouse_rel_y = -mouse_rel_y;
    }

  if (mouse_rel_x) {
    mouse_rel_x *= options->mouse_sens_pre;
    smoothed_mouse_x = (double)(mouse_rel_x + old_mouse_x) * 0.5f*0.3f;
    old_mouse_x = mouse_rel_x;
    smoothed_mouse_x *= (double)options->mouse_sens_post;

    last_mouse_update_date = SDL_GetTicks();

  }

  else {
    if (last_mouse_update_date) {
      time_since_last_mouse_update = SDL_GetTicks()-last_mouse_update_date;
      if (options->mouse_smooth) {
	if (time_since_last_mouse_update < options->mouse_smooth) {}
	else smoothed_mouse_x=0;
      }
    }
    else smoothed_mouse_x=0;
  }

  if ((mouse_rel_y)&&(options->is_mouse_y)) {
    mouse_rel_y *= options->mouse_sens_pre;
    smoothed_mouse_y = (double)(mouse_rel_y + old_mouse_y) * 0.5f;
    old_mouse_y = mouse_rel_y;
    smoothed_mouse_y *= (double)options->mouse_sens_post;
  }

  if (options->mouse_smooth)
    rotate(smoothed_mouse_x);
  else rotate(mouse_rel_x);

  if ((mouse_rel_y)&&(options->is_mouse_y))
    {
      if (options->mouse_smooth) {
	if (smoothed_mouse_y > 0)
	  look_around(SHX_BAS,smoothed_mouse_y);
	else look_around(SHX_HAUT,-smoothed_mouse_y);
      }
      else {
	if (mouse_rel_y > 0)
	  look_around(SHX_BAS,mouse_rel_y);
	else look_around(SHX_HAUT,-mouse_rel_y);
      }
    }
}

void update_special()
{
  int typeSpecial=0;
  if (player->elevation == AU_SOL)
    {
      typeSpecial = test_special(player->square);

      if ((typeSpecial >= TEL_D1)&&(typeSpecial <= TEL_A5))
	{
	  if (player->teleporting < 0) {

	    player->teleporting = world->map[player->square].paramSpecial ;
	    teleport_to(world->map[player->square].paramSpecial);
	  }
	}

      if (player->teleporting) {
	if (player->square != player->teleporting) 
	  player->teleporting = -1;
      }
    }

  /* time warnings */
  if (world->time_limit > 0) {
    if ( (world->game_timer->getRestSec() == 0)&&(world->game_timer->getRestMin() == 1)) {

      if (one_minute_w_played==0) {
	play_time_warning_sound(S_ONE_MINUTE);
	one_minute_w_played=1;
      }
    }
    
    if ( (world->game_timer->getRestSec() == 10)&&(world->game_timer->getRestMin() == 0))

      if (countdown_w_played==0) {
	play_time_warning_sound(S_COUNTDOWN);
	countdown_w_played=1;
      }
  }
  
}
void update_ia()
{
   hut_t *hut;
   for (int i=0; i < world->num_huts; i++)
     {
       hut = &world->huts[i];
       update_shmollux(hut);
     }
}

void update_bonus()
{
  int param=0, bonus_type = 0;

  /* bonus rotation */
  world->bonus_rotat += 0.1*world->game_sync->dt;
  if (world->bonus_rotat >= 360)
    world->bonus_rotat = 0;

  if (world->bonus_updown < -0.1)
    world->bonus_up=1;
  if (world->bonus_updown > 0.07)
    world->bonus_up=0;

  if (world->bonus_up)
    world->bonus_updown += 0.00015*world->game_sync->dt;

  else 
    world->bonus_updown -= 0.00015*world->game_sync->dt;

  /* arrow for winning post */
  world->winning_post_object.rotat_z += 0.1*world->game_sync->dt;
  if (world->winning_post_object.rotat_z >= 360)
    world->winning_post_object.rotat_z = 0;

  if (world->winning_post_object.updown < -0.22)
    world->winning_post_object.go_up=1;
  if (world->winning_post_object.updown > 0.22)
    world->winning_post_object.go_up=0;

  if (world->winning_post_object.go_up)
    world->winning_post_object.updown += 0.00033*world->game_sync->dt;

  else 
    world->winning_post_object.updown -= 0.00033*world->game_sync->dt;

  world->current_bonus = whichBonusActive();

  if (world->current_bonus >= 0) {
    
    /* bonus is no longer active if updateTime returns 0 */
    if (world->tab_bonus[world->current_bonus]->updateTimer()==0)
      world->current_bonus = -1;
  }

  if (world->tab_bonus[E_SHMIXGOMME]->actif) {
	world->b_shmixgomme->updateTimer();
  }

  if (player->elevation == AU_SOL)
    {

      bonus_type = test_bonus(player->square);

      switch (bonus_type)
	{
	case E_RIEN:
	  break;
	case E_DITUBOITE:
	  world->map[player->square].is_ditugomme = 0;
	  world->num_dituboites--;
	  if (!(desactive_bonus(player->mySector, player->square)))
	    fprintf(stderr,"Error: can't find bonus on square %d\n",
		    player->square);
	  world->dituboite->agir(param);
	  break;

	case E_ARMAGGEDON:
	  player->score+=50;
	  play_misc_sound(ARMAGGEDON_PICKED);
	  hud_new_message("You picked the Armaggedon! (use with F1)");
	  stock_bonus(0);
	  break;

	case E_ONDEDECHOC:
	  player->score+=25;
	  play_misc_sound(SHOCKWAVE_PICKED);
	  hud_new_message("You picked ShockWave (use with F2)");
	  stock_bonus(1);
	  break;

	case E_RAPIDE:
	  play_misc_sound(HIGHSPEED_PICKED);
	  hud_new_message("You picked a HighSpeed bonus (use with F3)");
	  stock_bonus(2);
	  break;

	case E_STOPTEMPS:
	  player->score+=25;
	  play_misc_sound(STOPTIME_PICKED);
	  hud_new_message("You picked a StopTime bonus (use with F4)");
	  stock_bonus(3);
	  break;

	case E_PERDBONUS:
	case E_SHMIXGOMME:
	case E_HASARD:
	case E_VIE:
	  execute_bonus(bonus_type);
	  break;

	default:
	  if (world->current_bonus <= E_COUPDEBOL) /* a good bonus is active */
	    {
	      if (world->current_bonus > -1)
		world->tab_bonus[world->current_bonus]->cancelAction();
	      execute_bonus(bonus_type);
	    }

	  else  /* a malus is active, we wait for it to finish */
	    {


	    }
   
	  break;
	}
      
    } 
}

void update_dituboxes()
{
  if (world->num_dituboites == 0) {
	
    /* a winning post is not specified */
    if (world->finish_square < 0) {
      player->status = STATUS_WIN;
      play_misc_sound(S_YOU_WIN);
    }
	
    else {
      /* a winning post is specified, we must be on it to win */
      if (need_winning_post_message) {
	hud_new_message("Run away to the winning post to finish !!");
	printf("Run away to the winning post to finish !!");
	need_winning_post_message=0;
	printf("la\n");
      }

      if (player->square == world->finish_square) {
	player->status = STATUS_WIN;
	play_misc_sound(S_YOU_WIN);
	printf("ici\n");
      }
    }
  }
}

void toggle(int what)
{
  int check = 0;

  if (!(what))
  {
    what = 1;
    check = 1;
  }

  if ((what) && (!(check)))
  {
    what = 0;
  }

  //what = !what;
}

void game_over()
{
  player->status = GAME_OVER;
  printf("You are a loser. Game is over.\n");
}

void pause_game()
{
  hud_new_message("Game paused");
  world->game_timer->togglePaused();
  if ( world->current_bonus > 0) {
    world->tab_bonus[world->current_bonus]->timer->togglePaused();
  }

  if (world->tab_bonus[E_SHMIXGOMME]->actif)
   world->tab_bonus[E_SHMIXGOMME]->timer->togglePaused(); 
}

void unpause_game()
{
  hud_new_message("Game un-paused");
  world->game_timer->togglePaused();
  if ( world->current_bonus > 0) {
    world->tab_bonus[world->current_bonus]->timer->togglePaused();
  }

  if (world->tab_bonus[E_SHMIXGOMME]->actif)
    world->tab_bonus[E_SHMIXGOMME]->timer->togglePaused(); 
}
