#include "ElecField.h"

#define WHILE_LIMIT 10000

struct Charge
{
    bool sign ; // true si positive
    Sint16 value ;
    Uint16 nbOut ;

    double x ; // entre 0.0 et 1.0
    double y ;
};

ElecField::ElecField() : m_graph( 0x0 )
{
    //ctor
}

ElecField::~ElecField()
{
    if ( !m_graph ) SDL_FreeSurface( m_graph ) ;
}


void ElecField::renderGraph( int w, int h )
{
    // Détruit vieille surface si existante
    if ( !m_graph ) SDL_FreeSurface( m_graph ) ;

    // Créé nouvelle surface
    m_graph = SDL_CreateRGBSurface( SDL_HWSURFACE, w, h, 32, 0, 0, 0, 0 );

    // Variables
    double pas( 0.01 ) ;
    double x, y, angle ;

    // Affichage
    for ( std::vector<Charge>::iterator it( m_chList.begin() ); it != m_chList.end(); it ++ )
    {
        // Cercle qui localise la charge
        if ( it->sign )
            circleRGBA( m_graph, Sint16( it->x * w ), Sint16( it->y * h ), it->value, 255, 255, 0, 255 );
        else
            circleRGBA( m_graph, Sint16( it->x * w ), Sint16( it->y * h ), -it->value, 0, 128, 255, 255 );

        // Ligne de champ qui partent d'une charge +
        if ( it->sign )
        {
            for ( int s(0); s < it->nbOut; s ++ )
            {
                angle = s * 2 * M_PI / it->nbOut ;

                x = it->x + it->value * std::cos( s ) / w ;
                y = it->y + it->value * std::sin( s ) / h ;

                drawLineFrom( x, y );
            }
        }
    }
}

SDL_Surface* ElecField::getGraph() const
{
    return m_graph ;
}


void ElecField::addCharge(int value, double x, double y, Uint16 nbOut)
{
    m_chList.push_back( { value > 0, value, nbOut, x, y } );
}

void ElecField::drawLineFrom( double x, double y )
{
    double pas( 0.001 );
    double xp, yp ;

    int w( m_graph->w );
    int h( m_graph->h );

    Uint32 incr(0) ;

    while ( x > 0.0 && x < 1.0 && y > 0.0 && y < 1.0 && incr < WHILE_LIMIT )
    {
        // Incrémentation
        incr ++ ;

        // Mémoriser l'endroit actuel
        xp = x ;
        yp = y ;

        // Calculer la composante de la force
        double fx( 0.0 );
        double fy( 0.0 );
        double r;

        for ( std::vector<Charge>::iterator it( m_chList.begin() ); it != m_chList.end(); it ++ )
        {
            // Calcul de la distance
            r = std::sqrt( ( it->x - x )*( it->x - x ) + ( it->y - y )*( it->y - y ) );

            // Il faut sortir de la boucle si la ligne se termine sur une charge -
            if ( !it->sign && std::abs( r ) < -it->value / ( std::sqrt( h*h + w*w ) ) )
                break ;

            // Calcul des forces
            fx += ( x - it->x ) * it->value / r / r / r ;
            fy += ( y - it->y ) * it->value / r / r / r ;
        }


        // Normaliser
        r = std::sqrt( fx * fx + fy * fy );
        fx /= r ;
        fy /= r ;

        // Il faut sortir de la boucle si la force est trop faible
        if ( r < 0.5 )
            break ;

        // Discrétiser
        fx *= pas ;
        fy *= pas ;

        // Bouger
        x += fx ;
        y += fy ;

        // Dessiner
        //lineRGBA( m_graph, xp * m_graph->w, yp * m_graph->h, x * m_graph->w, y * m_graph->h, 255, 255, 255, 255 );
        pixelRGBA( m_graph, x * m_graph->w, y * m_graph->h, 255, 255, 128, 255 );
    }
}
