/*  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 <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>

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

#include <math.h>

#include "timers.h"
#include "world_geometry.h"
#include "hut.h"
#include "bonus.h"
#include "mango.h"
#include "world_building.h"
#include "texture.h"
#include "world_geometry.h"
#include "sector.h"
#include "map.h"
#include "game_loop.h"
#include "hud.h"
#include "sounds.h"

#define DISTMIN_SHMOL_SHMIXMAN 4

extern game_data_t *world;
extern player_t *player;

GLUquadricObj *quadratic;

CHRONOMETRE *prox_warning_timer=0;
char is_first_time=1;

void shmol_procreation(shmollux_t *shmollux)
{
  shmollux->square_last_test = shmollux->my_address;
  return_home(shmollux);
  choose_direction(analyze_situation(shmollux), shmollux);
}

void return_home(shmollux_t *shmollux)
{
  shmollux->need_choice=1;
  shmollux->square = shmollux->my_address;

  shmollux->map_x = square_to_map_x(shmollux->my_address);
  shmollux->map_z = square_to_map_z(shmollux->my_address);

  shmollux->pos_x = shmollux->map_x * SCALE_FACTOR +1.5;
  shmollux->pos_z = shmollux->map_z * SCALE_FACTOR +1.5;

  shmollux->status = ETAT_NAISSANCE;
}

void shmol_hunt(shmollux_t *shmollux)
{
  shmol_move(shmollux);
}

void shmol_escape(shmollux_t *shmollux)
{
  shmol_move(shmollux);
}

void shmol_move(shmollux_t *shmollux)
{
  int where_can_i;
  where_can_i=analyze_situation(shmollux);
  if (shmollux->need_choice) 
    {
      choose_direction(where_can_i, shmollux); 
    }
  shmol_move_forward(shmollux);
}

int analyze_situation(shmollux_t *shmollux)
{
  int num_possibilities=0;
  int i=0;
  if (!(test_left(shmollux->square))) 
    {
      shmollux->possible_dir[SHMOL_GAUCHE]=1;
      num_possibilities++;
    }
  else shmollux->possible_dir[SHMOL_GAUCHE]=0;

  if (!(test_right(shmollux->square))) 
    {
      shmollux->possible_dir[SHMOL_DROITE]=1;
      num_possibilities++;
    }
  else shmollux->possible_dir[SHMOL_DROITE]=0;

  if (!(test_up(shmollux->square)))
    {
      shmollux->possible_dir[SHMOL_HAUT]=1;
      num_possibilities++;
    }
  else shmollux->possible_dir[SHMOL_HAUT]=0;

  if (!(test_down(shmollux->square))) 
    {
      shmollux->possible_dir[SHMOL_BAS]=1;
      num_possibilities++;
    }
  else shmollux->possible_dir[SHMOL_BAS]=0;

  if ((shmollux->square != shmollux->square_last_test)
      && (fabs(shmollux->pos_x-shmollux->map_x*SCALE_FACTOR -1.5) < 0.15)
      && (fabs(shmollux->pos_z-shmollux->map_z*SCALE_FACTOR -1.5) < 0.15))
    {
      if ( (shmollux->possible_dir[0]!=shmollux->possible_dir_before[0])
	   || (shmollux->possible_dir[1]!=shmollux->possible_dir_before[1])
	   || (shmollux->possible_dir[2]!=shmollux->possible_dir_before[2])
	   || (shmollux->possible_dir[3]!=shmollux->possible_dir_before[3]))
	{
	  shmollux->need_choice=1;
	  shmollux->square_last_test=shmollux->square;
	  for (i=0; i<4; i++) {
	    shmollux->possible_dir_before[i]=shmollux->possible_dir[i];
	  }
	}
    }
  else shmollux->need_choice=0;

  return num_possibilities;
}

void choose_direction(int how_many_choices, shmollux_t *shmollux)
{
  int i=0; int j=0; 
  int choice = 0; char from_where=0;
  switch (shmollux->direction)
    {
    case SHMOL_GAUCHE: from_where=SHMOL_DROITE; break;
    case SHMOL_DROITE: from_where=SHMOL_GAUCHE; break;
    case SHMOL_HAUT: from_where=SHMOL_BAS; break;
    case SHMOL_BAS: from_where=SHMOL_HAUT; break;
    }
  
  // don't go back on our steps
  if (how_many_choices > 1) 
    {
#ifdef WIN32
      choice = rand() % (how_many_choices-1) +1;
#else
      choice = random() % (how_many_choices-1) +1;
#endif
      shmollux->possible_dir[from_where]=0;
      shmollux->possible_dir_before[from_where]=0;
    }
  else 
#ifdef WIN32
    choice = rand() % how_many_choices +1;
#else
  choice = random() % how_many_choices +1;
#endif  
  for (i=0; i<4; i++)
    {
      if (shmollux->possible_dir[i]) j++;
      if (j==choice) {shmollux->direction = i; i=4;}
    }
}

void shmol_move_forward(shmollux_t *shmollux)
{
  char fautChanger=0;
  double currentSpeed = shmollux->speed[shmollux->status]*(double)world->game_sync->dt;
  int square = map_to_square(shmollux->map_x, shmollux->map_z);
  
  switch (shmollux->direction)
    {
    case SHMOL_GAUCHE: 
      if (test_left(square)) {
	if ((shmollux->pos_x-currentSpeed) - shmollux->map_x * SCALE_FACTOR > DIST_MAXI)
	  {
	    shmollux->pos_x -= currentSpeed;
	    shmollux->map_x = (int) floor(shmollux->pos_x / SCALE_FACTOR);
	  }

	else 
	  {
	    fautChanger=1;
	    //printf("je n'avance pas :(\n");
	  }
      }

      else 
	{
	  shmollux->pos_x -= currentSpeed;
	  shmollux->map_x = (int) floor(shmollux->pos_x / SCALE_FACTOR);
	}

      shmollux->pos_z = shmollux->map_z * SCALE_FACTOR + 1.5;
      break;

    case SHMOL_DROITE: 
      if (test_right(square)) {
	if ( (shmollux->map_x+1)* SCALE_FACTOR -(shmollux->pos_x+currentSpeed)> DIST_MAXI)
	  {
	    shmollux->pos_x += currentSpeed;
	    shmollux->map_x = (int) floor(shmollux->pos_x / SCALE_FACTOR);
	  }
	else 
	  {
	    fautChanger=1;
	    //printf("je n'avance pas :(\n");
	  }
      }

      else 
	{
	  shmollux->pos_x += currentSpeed;
	  shmollux->map_x = (int) floor(shmollux->pos_x / SCALE_FACTOR);
	}

      shmollux->pos_z = shmollux->map_z * SCALE_FACTOR + 1.5; 
      break;

    case SHMOL_HAUT:
      if (test_up(square)) {
	if ((shmollux->pos_z-currentSpeed) - shmollux->map_z * SCALE_FACTOR > DIST_MAXI)
	  {
	    shmollux->pos_z -= currentSpeed;
	    shmollux->map_z = (int) floor(shmollux->pos_z / SCALE_FACTOR);
	  }
	else 
	  {
	    //printf("je n'avance pas :(\n");
	    fautChanger=1;
	  }
      }

      else 
	{
	  shmollux->pos_z -= currentSpeed;
	  shmollux->map_z = (int) floor(shmollux->pos_z / SCALE_FACTOR);
	}

      shmollux->pos_x = shmollux->map_x * SCALE_FACTOR + 1.5;
      break;

    case SHMOL_BAS:
      if (test_down(square)) {
	if ((shmollux->map_z+1)  * SCALE_FACTOR -(shmollux->pos_z+currentSpeed)> DIST_MAXI)
	  {
	    shmollux->pos_z += currentSpeed;
	    shmollux->map_z = (int) floor(shmollux->pos_z / SCALE_FACTOR);
	  }
	else 
	  {
	    //printf("je n'avance pas :(\n");
	    fautChanger=1;
	  }
      }

      else 
	{
	  shmollux->pos_z += currentSpeed;
	  shmollux->map_z = (int) floor(shmollux->pos_z / SCALE_FACTOR);
	}

      shmollux->pos_x = shmollux->map_x * SCALE_FACTOR + 1.5;
      break;

    default: 
      printf("Erreur: Shmollux: mauvaise direction\n");
      break;
    }

  shmollux->square = map_to_square(shmollux->map_x, shmollux->map_z);
  if (fautChanger) 
    {
      choose_direction(analyze_situation(shmollux), shmollux);
    }
}

void draw_shmollux(shmollux_t *shmollux)
{
  if ((shmollux->status==ETAT_POURSUIVANT)
      ||(shmollux->status==ETAT_POURSUIVI)
      ||(shmollux->status==ETAT_STOP))
    {
      glPushMatrix();
      glEnable(GL_TEXTURE_2D);

      glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
      glTranslatef(shmollux->pos_x, -shmollux->pos_z, 0.8);

      switch (shmollux->direction)
	{
	case SHMOL_DROITE: glRotatef(90.0f, 0.0f, 0.0f, 1.0f); break;
	case SHMOL_GAUCHE: glRotatef(-90.0f, 0.0f, 0.0f, 1.0f); break;
	case SHMOL_HAUT: glRotatef(180.0f, 0.0f, 0.0f, 1.0f); break;
	default: break;

	}

      if ((player->status == ETAT_POURSUIVI)||(player->status==STATUS_WAITING)
	  ||(player->status == STATUS_DEAD) || (player->status == GAME_OVER))
	glBindTexture(GL_TEXTURE_2D, shmollux->my_textures[0]);

      else
	glBindTexture(GL_TEXTURE_2D, shmollux->my_textures[1]);

      //glCallList(*pMaListe);
      //gluCylinder( quadratic, 0.3f, 0.0f, 1.2f, 5, 5 );
      gluSphere( quadratic, 0.3f, 10, 10 );
      
      glDisable(GL_TEXTURE_2D);
      glPopMatrix();
    }
}

void interaction_player_shmol(shmollux_t *shmollux)
{
  double radius=0;

  radius = (player->pos_x-shmollux->pos_x)
	*(player->pos_x-shmollux->pos_x)
	+ (player->pos_z-shmollux->pos_z)
	*(player->pos_z-shmollux->pos_z);

  if ((world->warning_radius)&&(radius < world->warning_radius)
      &&(radius > DISTMIN_SHMOL_SHMIXMAN)
      && (player->status == ETAT_POURSUIVI) &&(!player->highlander))
    {
      if (is_first_time) {
	is_first_time=0;
	prox_warning_timer = new CHRONOMETRE;
	prox_warning_timer->initialise();
	prox_warning_timer->setAlarm(1);
	    
	play_misc_sound(S_PROXIMITY_WARNING);
      }
      else proximity_warning();
	
    }

   if (radius < DISTMIN_SHMOL_SHMIXMAN)
     {

      switch (player->status)
	{
	case ETAT_POURSUIVANT:
	  play_misc_sound(S_SHMOLLUX_EATEN);
	  hud_new_message("You have eaten a nasty ShmolluX!");
	  shmollux->status = ETAT_MORT;
	  player->score += 200;
	  break;


	case ETAT_POURSUIVI:
	  if (!(player->highlander))
	    {
	      if (player->num_lives > 1) 
		{
		  player->num_lives--;
		  play_misc_sound(S_PLAYER_DIES);
		  hud_new_message("You have been eaten");
		  printf("You are dead.\n");
		  player->status = STATUS_DEAD;
		}

	      else 
		{
		  player->num_lives--;
		  play_misc_sound(S_GAME_OVER);
		  player->status = GAME_OVER;
		  printf("You lose. Game is over.\n");
		}
	    }
	  break;
	  
 	}
     }
}

void proximity_warning()
{
  if (prox_warning_timer->update_temps()) {
    play_misc_sound(S_PROXIMITY_WARNING);
    prox_warning_timer->resetTemps();
    prox_warning_timer->setAlarm(1);
  }

}
