/*  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 "sector.h"

#define DISTANCE_MAX 35
#define COS_DEMI_FOV 0.503630770442416

extern int TESTEUR;

extern game_data_t *world;
extern player_t *player;

sector_t *get_sector(double x, double z)
{
  for (int i=0; i < world->num_sectors; i++)
    {
      if ((x >= world->sectors[i].xmin) && (x <= world->sectors[i].xmax)
	  && (z >= world->sectors[i].zmin) && (z <= world->sectors[i].zmax))
	return &world->sectors[i];
    }
  return NULL;
}

void connect_sectors()
{
  int i=0;
  for (i=0; i < world->total_num_portals; i++)
    {
      switch (world->portals[i].TYPE)
	    {
	    case HORIZ:
	      world->portals[i].connexion1 =
		(sector_t *) get_sector(world->portals[i].x1 +1,
					world->portals[i].z1 -1);

	      world->portals[i].connexion2 =
		(sector_t *) get_sector(world->portals[i].x1 +1,
					world->portals[i].z1 +1);
	      break;

	    case VERTI:
	      world->portals[i].connexion1 =
		(sector_t *) get_sector(world->portals[i].x1 -1,
					world->portals[i].z1 +1);

	      world->portals[i].connexion2 =
		(sector_t *) get_sector(world->portals[i].x1 +1,
					world->portals[i].z1 +1);
	      break;

	    }
	  if ((!(world->portals[i].connexion1))
	      || (!(world->portals[i].connexion2)))
	    {
	      fprintf(stderr,"Error connecting sectors\n");
	      exit(1);
	    }
	  
	}

    

}

void test_visibility_sectors(sector_t *sector, VECTEUR2D vDir, double angle)
{
  portal_t *portal = 0;
  sector_t *next_sector = 0, *test_sector = 0;
  int i=0;
  
  VECTEUR2D MA, MB, new_dir, AB;
  double new_angle = 0;
  int test_result = 0;

  sector->visible = 1;

  // on teste tous les portals du secteur sauf celui qui sert à tester
  for (i=0; i < sector->num_portals; i++)
  {

    portal = &(world->portals[sector->portal_indices[i]]);

    if (portal->number != TESTEUR)
    {
      //printf("MAYBE testing %d\n",portal->number);
      // si le portal est déjà déclaré visible, on ne reteste pas
      //if (portal->number == 29) 
			//printf("VALEUR DE VISIBLE : %d, URL: %p\n",portal->visible,
					//&portal->number);
      if (portal->visible == 0)
      {
        //printf("REALLY testing %d\n",portal->number);
	// le secteur mitoyen n'est pas celui en cours
        test_sector = (sector_t *) portal->connexion1;
        if (test_sector == sector)
          next_sector = (sector_t *) portal->connexion2;
        else
          next_sector = (sector_t *) portal->connexion1;

	if (!(next_sector)) 
	  {
	    fprintf(stderr,"Error while testing sectors\n");
	  }

        // quelques calculs vigoureux necessaires pour les tests
        // de visibilité du portal considéré
        MA.x = portal->x1 - player->pos_x;
        MA.z = portal->z1 - player->pos_z;

        MB.x = portal->x2 - player->pos_x;
        MB.z = portal->z2 - player->pos_z;

        AB.x = portal->x2 - portal->x1;
        AB.z = portal->z2 - portal->z1;


        // on teste si le portal est visible
        test_result = test_visibility_portal(MA, MB, vDir, angle);
        if (test_result)
        {

          // si c'est le cas, on teste le secteur mitoyen
          // en ajustant champ de vision et direction avec ce portal
          portal->visible = 1;

          switch (test_result)
          {
          case 3:              // point A dedans
            // MB=reduitChamp(vDir, angle,MA,AB);
            new_dir = calc_Vdir(MA, MB);
            new_angle = calc_angle(MA, MB);
            break;
          case 4:              // point B dedans
            // MA=reduitChamp(vDir, angle,MB,AB);
            new_dir = calc_Vdir(MA, MB);
            new_angle = calc_angle(MA, MB);
            break;
          default:
            new_dir = calc_Vdir(MA, MB);
            new_angle = calc_angle(MA, MB);
            break;
          }

          TESTEUR = portal->number;
			 //printf("%d is visible\n", TESTEUR);
			 //if (portal->number == 29) printf("VALEUR DE VISIBLE : %d, URL:%p\n" ,portal->visible, &portal->number);

          test_visibility_sectors(next_sector, new_dir, new_angle);
        }

      }
    }
  }

}

double calc_angle(VECTEUR2D MA, VECTEUR2D MB)
{
  double d = 0;

  d = MA.scalaire(MB) / (MA.getNorme() * MB.getNorme());

  return acos(d);
}

VECTEUR2D calc_Vdir(VECTEUR2D MA, VECTEUR2D MB)
{
  VECTEUR2D newV, ua, ub;

  ua.x = MA.x / MA.getNorme();
  ua.z = MA.z / MA.getNorme();

  ub.x = MB.x / MB.getNorme();
  ub.z = MB.z / MB.getNorme();

  newV = ua.somme(ub);
  newV.divisePar(newV.getNorme());

  return newV;
}

char test_visibility_portal(VECTEUR2D MA, VECTEUR2D MB, VECTEUR2D vDir,
                              double angle)
{
  double cosDemiAngle = cos(angle / 2);
  double cosUMA = 0;
  double cosUMB = 0;
  double uScalMA = 0, uScalMB = 0, MAscalMB = 0, test = 0;

  uScalMA = vDir.scalaire(MA);
  uScalMB = vDir.scalaire(MB);
  MAscalMB = MA.scalaire(MB);

  cosUMA = (uScalMA / MA.getNorme());
  cosUMB = (uScalMB / MB.getNorme());

  // double égalité, ou presque : les points sur les droites limites du fov
  if ((cosUMA - cosDemiAngle < 0.00000000001)
      && (cosUMB - cosDemiAngle < 0.00000000001)
      && (cosUMA - cosDemiAngle >= 0) && (cosUMB - cosDemiAngle >= 0))
    return 1;

  if ((cosUMA == cosDemiAngle) && (cosUMB == cosDemiAngle))
    return 1;

  // les 2 points a l'interieur du fov
  if ((cosUMA > cosDemiAngle + 0.000000001)
      && (cosUMB > cosDemiAngle + 0.000000001) && ((uScalMA < DISTANCE_MAX)
                                                   || (uScalMB <
                                                       DISTANCE_MAX)))
    return 2;

  // que le point A
  if ((cosUMA > cosDemiAngle + 0.000000001)
      && (cosUMB < cosDemiAngle + 0.000000001))
    return 3;

  // que le point B
  if ((cosUMA < cosDemiAngle + 0.000000001)
      && (cosUMB > cosDemiAngle + 0.000000001))
    return 4;

  // les deux points a l'exterieur et qui coupent le fov
  if (vDir.determinant(MA) * vDir.determinant(MB) < 0)
  {

    if ((cosUMA < cosDemiAngle) && (cosUMA > 0))
    {
      test =
        uScalMB - ((uScalMA * MAscalMB) / (MA.getNorme() * MA.getNorme()));
      if (test > 0)
        return 5;
    }

    else if ((cosUMB < cosDemiAngle) && (cosUMB > 0))
    {
      test =
        uScalMA - ((uScalMB * MAscalMB) / (MB.getNorme() * MB.getNorme()));
      if (test > 0)
        return 6;
    }
  }

  return 0;
}

void test_visibility_bonus(sector_t * sector)
{
  VECTEUR2D MA;
  bonus_t *bonus;

  for (int i=0; i < sector->num_bonus; i++)
    {
      bonus = &sector->bonus[i];
      MA.x = bonus->x - player->pos_x;
      MA.z = bonus->z - player->pos_z;

      bonus->visible = test_visibility_point(MA);
    }
}

char test_visibility_point(VECTEUR2D MA)
{
  double uScalMA = 0;
  double cosUMA = 0;

  uScalMA = player->u.scalaire(MA);     // distance joueur/bonus (projeté)
  cosUMA = (uScalMA / MA.getNorme());

  if ((cosUMA - COS_DEMI_FOV < 0.00000000001) && (cosUMA - COS_DEMI_FOV >= 0)
      && (uScalMA < DISTANCE_MAX))
    return 1;

  if ((cosUMA > COS_DEMI_FOV + 0.000000001) && (uScalMA < DISTANCE_MAX))
    return 1;

  return 0;
}

void reset_sector(sector_t *sector)
{
  sector->visible=0;
  
  /* this is not needed; bue just to be sure... */
  for (int i=0; i < sector->num_bonus; i++)
    {
      sector->bonus[i].visible=0;
    }
}

void reset_portals()
{
  for (int i=0; i < world->total_num_portals; i++)
    {
      world->portals[i].visible = 0;
    }
}

char desactive_bonus(sector_t *sector, int square)
{
  bonus_t *bonus;
  for (int i=0; i < sector->num_bonus; i++)
    {
      bonus = &sector->bonus[i];
      if (bonus->square == square)
	{
	  bonus->still_here = 0;
	  return 1;
	}
    }
  return 0;
}
