/*****************************************************************************
 *
 *  Copyright (C) 2003 Cédric Brégardis <cedric.bregardis@free.fr>
 *
 *  This file is part of BRIQUOLO
 *
 *  BRIQUOLO 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.
 *
 *  BRIQUOLO 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 BRIQUOLO; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *****************************************************************************/
#ifndef _ELEMENTCOLLISION
#define _ELEMENTCOLLISION

#include <map>
#include <string>
#include "ElementGraphique.h"
#include "Generateur.h"

using namespace std;

class Balle;
class Struct_Collision;

class Point
{
  public:
    float x, y;
    Point(float p_X, float p_Y):x(p_X), y(p_Y)
    {
    }
};

class ElementCollision: public ElementGraphique
{
  protected:
    // **** Types locaux à la classe ****
    typedef map<string, string> Map_Params;
    typedef Map_Params::iterator It_Map_Params;
    // **********************************

  protected:
    static ElementCollision * _DernierElementCollision;
    static Balle * _DerniereBalle;
    static float _DernierTempsCollision;


    unsigned int _Niveau;
    string _LigneOption;
    Map_Params _Params;

  protected:
    float _Determinant(float d1x, float d1y, float d2x, float d2y);
    float _Modulo(float a, float b);

    Struct_Collision _CircleCollisionWithoutRot(float c1ix, float c1iy, float r1, float v1x, float v1y,
                                                float brick_center_x, float brick_center_y,
                                                float c2ix, float c2iy, float r2, float v2x, float v2y,
                                                float tmax);
    Struct_Collision _CircleCollisionWithRotSearch(float c1ix, float c1iy, float r1, float v1x, float v1y,
                                                   float brick_center_x, float brick_center_y, float brick_rot_i,
                                                   float teta0, float ra, float r2, float v2x, float v2y, float v2r,
                                                   float tmin, float dtmin,
                                                   float tmax, float dtmax);

    Struct_Collision _CircleCollisionWithRot(float c1ix, float c1iy, float r1, float v1x, float v1y,
                                             float brick_center_x, float brick_center_y, float brick_rot_i,
                                             float c2ix, float c2iy, float r2, float v2x, float v2y, float v2r,
                                             float tmax);
    Struct_Collision _CircleCollision(float c1ix, float c1iy, float r1, float v1x, float v1y,
                                      float brick_center_x, float brick_center_y, float brick_rot_i,
                                      float c2ix, float c2iy, float r2, float v2x, float v2y, float v2r,
                                      float tmax);

    Struct_Collision _SegmentCollision(float c1ix, float c1iy, float r1, float v1x, float v1y,
                                       float brick_center_x, float brick_center_y, float brick_rot_i,
                                       float teta0, float ra, float gama0, float rb,
                                       float vtx, float vty, float vr,
                                       float tmin, float dtmin, float detmin,
                                       float tmax, float dtmax, float detmax);

    Struct_Collision _SegmentCollision(float c1ix, float c1iy, float r1, float v1x, float v1y,
                                       float brick_center_x, float brick_center_y, float brick_rot_i,
                                       float ax, float ay, float bx, float by, float vtx, float vty, float vr,
                                       float tmin, float tmax);

    void _Rebond(float nx, float ny, float vix, float viy, float & vfx, float & vfy);

    virtual void _AnalyserLigne();

  public:
    ElementCollision();
    ElementCollision(const ElementCollision & p_ElementCollision);
    virtual ~ElementCollision();


    /**
       Cette méthode statique permet de mémoriser le dernier élément responsable d'une collision avec une balle.
     */
    static void SetDerniereCollision(ElementCollision * p_Element, Balle * p_Balle);
    
    /**
       Permet de déterminer si la balle passée en paramètre entre en collision avec cet ElementCollision.
       Cette méthode doit être implémentée par la classe spécialisant cette classe abstraite.
       @return La structure de collision. Elle indique (entre autre) le moment (TempsCollision) de la collision si elle a lieu.
               En comparant les différents TempsCollision des différentes structures renvoyé par les différents élément, on
               peut savoir quel est l'élément sur lequel la collision a lieu en premier.
     */
    virtual Struct_Collision TesterCollision(Balle * p_Balle) = 0;

    /**
       Après avoir déterminé l'lément entrant en collision avec la balle (grace à TesterCollision) cette méthode est appelé sur
       l'élément en question. 
       @return <i>true</i> si l'élément disparait, <i>false</i> sinon.
     */
    virtual bool Toucher(Balle * p_Balle, double p_Temps);

    /**
       Permet de déplacer l'élement jusqu'au temps passé en paramètre. Cette méthode est appelée après après que le moment de la
       la collision ait été déterminé à l'aide de TesterCollision.
     */
    virtual void Deplacer(double p_Temps); 

    /**
       Permet de créer un générateur de particules correspondant à une collision. Elle est appelée lorsque l'élement 
       est responsable d'une collision. Si aucun générateur de particule ne doit être créé, alors la méthode renvoie NULL.
     */
    virtual Generateur * CreerGenerateurParticules();

    /**
       Permet de récupérer la ligne d'option associé à l'élément.
       @return La chaine de caractère représentant l'élément.
     */
    string GetLigneOption() const;

    /**
       Permet de définir la ligne d'option associé à l'élément.
       @param p_Ligne : la chaine de caractère représentant l'élément.
     */
    void SetLigneOption(const string & p_Ligne);

    virtual Point GetVisualCenter();


    /** 
     * Indique si la dernière collision peut être considérée comme achevé.
     * Lorsqu'une collision se produit, il est nécéssaire d'attendre
     * que la balle s'écarte légèrement avant de reconsidérer une nouvelle
     * collision sur cet élément.
     * 
     * @return <i>true</i> si on peut tester une nouvelle collision, <i>false</i> sinon.
     */
    bool IsDerniereCollisionOk(Balle * p_Balle);
};


struct Struct_Collision
{
    double VitesseX, VitesseY;
    double PositionX, PositionY;
    //double PositionCollision;
    double TempsCollision;
    double AddFacteurCollision;
    ElementCollision * ResponsableCollision;

    Struct_Collision()
    : VitesseX(0.0), VitesseY(0.0), 
      PositionX(0.0), PositionY(0.0),
      TempsCollision(0.0),
      AddFacteurCollision(1), 
      ResponsableCollision(NULL)
    {
    }
};


#endif
