var timer = null;
var objet_courant = 0;

/* Paramètres de l'univers */
var dt = 0.01;
var g = [0, -9.81];
var dimensions_univers = [500, 500];
var id_univers = "univers";
var univers = new Array();
var compteur = 0;
var coefficient_frottement = 0;
var chocs_particulaires = true;
var chocs_parois = true;
var constante_gravitationnelle = 0;

/* Pour le drag & drop */
var vitesse_souris = [0, 0];
var position_souris = [0, 0];
var elementSuivi = "";
var elementSuivi_n = 0;
var decalageX = 0;
var decalageY = 0;

function vecteur_of_chaine (s)
{
    //s.search(/^\([0-9]+,[0-9]+\)$/);
    var pos_virgule = s.indexOf(',');
    if(pos_virgule == -1)
    {
        return ([0, 0]);
    }
    else
    {
        var s1 = s.substring(1, pos_virgule);
        var s2 = s.substring(pos_virgule + 1, s.length - 1);
        return ([parseFloat(s1), parseFloat(s2)]);
    }
}

function modulo(m, n)
{
    while(m > n)
    {
        m = m - n;
    }
    while(m < 1)
    {
        m = m + n;
    }

    return m;
}

/* Fonctions de manipulation des vecteurs */
function somme(a, b)
{
    return [a[0]+b[0], a[1]+b[1]];
}
function produit(k, a)
{
    return [k*a[0], k*a[1]];
}
function moins(a)
{
    return [-a[0], -a[1]];
}
function scalaire(a, b)
{
    return (a[0]*b[0] + a[1]*b[1]);
}
function norme(a)
{
    return Math.sqrt(a[0]*a[0] + a[1]*a[1]);
}


/* Fonctions traduisant des lois physiques */
function gravitation(p1, p2, m1, m2)
{
    /* Loi la gravitation */
    var v = somme(p2, moins(p1));
    return (produit(constante_gravitationnelle*m1*m2/Math.pow(norme(v), 3), v));
}
function frottement (rayon, vitesse)
{
    /* Le frottement est en -arv où a est le coefficient de frottement */
    return (produit(- coefficient_frottement * rayon, vitesse));
}
function choc_elastique(p1, p2, v1, v2, m1, m2)
{
    /* On fait l'hypothèse que le choc élastique entre les deux solides se comporte
    comme s'il y avait choc tangentiellement et rien du tout normalement */
    var n = somme(moins(p1), p2);
    n = produit(1/norme(n), n);
    var t = [-n[1], n[0]];

    var v1n = scalaire(v1, n);
    var v2n = scalaire(v2, n);
    var vgn = (m1*v1n + m2*v2n)/(m1 + m2);

    return ([somme(produit(scalaire(v1, t), t), produit(2*vgn - v1n, n)), somme(produit(scalaire(v2, t), t), produit(2*vgn - v2n, n))]);
}
function calculer_choc_paroi(position, rayon, vitesse)
{
    if(vitesse[0] > 0)
    {
        var dx = dimensions_univers[0] - position[0];
    }
    else
    {
        var dx = position[0];
    }

    if(vitesse[1] > 0)
    {
        var dy = dimensions_univers[1] - position[1];
    }
    else
    {
        var dy = position[1];
    }

    return [Math.abs((dx - rayon)/vitesse[0]), Math.abs((dy - rayon)/vitesse[1])];
}
function calculer_choc_particule(p1, v1, r1, t1, p2, v2, r2, t2)
{
    var distance = norme(somme(p2, moins(p1)));
    var dp1 = norme(produit(t1, v1));
    var dp2 = norme(produit(t2, v2));

    if(distance > dp1 + r1 + dp2 + r2)
    {
        return null;
    }
    else
    {
        var t = Math.min(t1, t2);

        var r = (r1 + r2)*(r1 + r2);
        var Px = p2[0] - p1[0];
        var Py = p2[1] - p1[1];
        var Vx = v2[0] - v1[0];
        var Vy = v2[1] - v1[1];

        var racine = 2*Px*Vx*Py*Vy-Vy*Vy*Px*Px+Vy*Vy*r+Vx*Vx*r-Vx*Vx*Py*Py;
        if(racine < 0)
        {
            return null;
        }

        var tc1 = (-2*Px*Vx-2*Py*Vy + 2*Math.sqrt(racine))/(2*(Vy*Vy+Vx*Vx));
        var tc2 = (-2*Px*Vx-2*Py*Vy - 2*Math.sqrt(racine))/(2*(Vy*Vy+Vx*Vx));

        if(((tc2 < 0 || tc2 > t) && (tc1 < 0 || tc1 > t)))
        {
            return null;
        }
        else
        {
            var tc = (tc2 < 0)? tc1 : tc2;
            var pc1 = somme(p1, produit(tc, v1));
            var pc2 = somme(p2, produit(tc, v2));
            return [pc1, pc2, tc];
        }
    }
}


/* Parametrer l'univers */
function actualiser_parametres ()
{
    g = vecteur_of_chaine(document.getElementById('parametre_univers_g').value);
    constante_gravitationnelle = parseFloat(document.getElementById('parametre_univers_G').value);
    dt = parseFloat(document.getElementById('parametre_univers_dt').value);
    coefficient_frottement = parseFloat(document.getElementById('parametre_univers_f').value);
    chocs_particulaires = (document.getElementById('parametre_univers_chocs').value == "1") ? true : false;
    chocs_parois = (document.getElementById('parametre_univers_parois').value == "1") ? true : false;
    changer_dimensions_univers(vecteur_of_chaine(document.getElementById('parametre_univers_dim').value));
}
function changer_dimensions_univers (dim)
{
    dimensions_univers = dim;
    document.getElementById(id_univers).style.height = dim[1]+"px";
    document.getElementById(id_univers).style.width = dim[0]+"px";
}



/* un objet pour gérer les objets */
function objet ()
{
    var n;
    var m;
    var rayon;
    var t;

    var p = [0, 0];
    var v = [0, 0];
    var a = [0, 0]
    var trajectoire_v = [0, 0];
    var trajectoire_p = [0, 0];
    var forces = [0, 0];

    this.creer = function (n, p, v, a)
    {
        this.n = n;
        this.p = p;
        this.v = v;
        this.a = a;

        this.m = 1;
        this.t = 0;
        this.rayon = 3;
        this.trajectoire_v = [0, 0];
        this.trajectoire_p = [0, 0];
        this.forces = [0, 0];

        var nouvel_objet = document.createElement("div");
        nouvel_objet.setAttribute("id", "objet"+n);
        nouvel_objet.setAttribute("class", "objet");
        nouvel_objet.setAttribute("onclick", "activer_infos("+n+");");
        nouvel_objet.setAttribute("onmousedown", "suivre('objet"+n+"', "+n+");");
        nouvel_objet.appendChild(document.createTextNode(' '));
        document.getElementById(id_univers).appendChild(nouvel_objet);

        this.deplacer();
    }

    this.deplacer = function ()
    {
        var absx = parseInt(this.p[0]) - this.rayon;
        var ordy = parseInt(dimensions_univers[1] - this.p[1]) - this.rayon;
        document.getElementById("objet"+this.n).style.top = ordy+"px";
        document.getElementById("objet"+this.n).style.left = absx+"px";
    }

    this.ajouter_force = function(force)
    {
        this.forces = somme(this.forces, force);
    }

    this.calculer_vitesse = function()
    {
        var somme_forces = somme(this.forces, somme(frottement(this.rayon, this.v), produit(this.m, g)));

        var acceleration = produit(1/this.m, somme_forces);
        var vitesse = somme(this.v, produit(dt, acceleration));

        this.a = acceleration;
        this.trajectoire_v = vitesse;

        this.trajectoire_p = this.p;
        this.t = dt;
        this.forces = [0, 0];
    }

    this.calculer_trajectoire = function ()
    {
        var choc;
        var vitesses;

        if(chocs_particulaires)
        {
        for(var i = (this.n)+1; i <= compteur; i++)
        {
            if(univers[i] != null)
            {
                choc = calculer_choc_particule(this.trajectoire_p, this.trajectoire_v, this.rayon, this.t, univers[i].trajectoire_p, univers[i].trajectoire_v, univers[i].rayon, univers[i].t);
                if(choc != null)
                {
                    vitesses = choc_elastique(choc[0], choc[1], this.trajectoire_v, univers[i].trajectoire_v, this.m, univers[i].m);
                    this.trajectoire_p = choc[0];
                    this.trajectoire_v = vitesses[0];
                    univers[i].trajectoire_p = choc[1];
                    univers[i].trajectoire_v = vitesses[1];

                    this.t = this.t - choc[2];
                    univers[i].t = univers[i].t - choc[2];
                }
            }
        }
        }

        if(chocs_parois)
        {
            var temps_choc = calculer_choc_paroi(this.trajectoire_p, this.rayon, this.trajectoire_v);
            var temps_min = Math.min(temps_choc[0], temps_choc[1]);

            if(temps_min > this.t)
            {
                this.trajectoire_p = somme(this.trajectoire_p, produit(this.t, this.trajectoire_v));
                this.t = 0;
            }
            else
            {
                var inversion_vitesse = [-1, -1];
                if(temps_choc[0] > this.t)
                {
                    inversion_vitesse[0] = 1;
                }
                if(temps_choc[1] > this.t)
                {
                    inversion_vitesse[1] = 1;
                }

                this.t = this.t - temps_min;
                this.trajectoire_p = somme(this.trajectoire_p, produit(temps_min, this.trajectoire_v));
                this.trajectoire_v = [this.trajectoire_v[0]*inversion_vitesse[0], this.trajectoire_v[1]*inversion_vitesse[1]];
            }
        }
        else
        {
            var p = somme(this.trajectoire_p, produit(this.t, this.trajectoire_v));
            this.trajectoire_p = [modulo(p[0], dimensions_univers[0]), modulo(p[1], dimensions_univers[1])];
            this.t = 0;
        }

    }

    this.avancer_temps = function ()
    {

        while(this.t > 0)
        {
            this.calculer_trajectoire();
        }

        this.p = this.trajectoire_p;
        this.v = this.trajectoire_v;

        this.deplacer();
    }

    this.infos = function()
    {
        return ("Description de l'objet : 'objet"+this.n+"'\n"+
              " - p = (" + (Math.round(100*this.p[0])/100) + ", " + (Math.round(this.p[1]*100)/100) + ")\n" +
              " - v = (" + (Math.round(100*this.v[0])/100) + ", " + (Math.round(this.v[1]*100)/100) + ")\n" +
              " - a = (" + (Math.round(100*this.a[0])/100) + ", " + (Math.round(this.a[1]*100)/100) + ")");
    }

}

function creer_objet (p1, p2, v1, v2, a1, a2)
{
    var ok = true;
    var i = 1;
    while(ok && i <= compteur)
    {
        if(univers[i] != null)
        {
            ok = ok && (norme(somme(univers[i].p, [-p1, -p2])) > (univers[i].rayon + 4))
        }
        i++;
    }

    if(ok)
    {
        compteur++;
        univers[compteur] = new objet();
        univers[compteur].creer(compteur, [p1, p2], [v1, v2], [a1, a2]);
    }
}

/* gestion du temps */
function avancer_temps()
{
    var forcegravitation;
    for(var i = 1; i <= compteur; i++)
    {
        if(i != elementSuivi_n && univers[i] != null)
        {
            if(constante_gravitationnelle != 0)
            {
            for(var j = i+1; j <= compteur; j++)
            {
                if(univers[j] != null)
                {
                    forcegravitation = gravitation(univers[i].p, univers[j].p, univers[i].m, univers[j].m);
                    univers[i].ajouter_force(forcegravitation);
                    univers[j].ajouter_force(moins(forcegravitation));
                }
            }
            }

            univers[i].calculer_vitesse();
        }
    }
    for(var i = 1; i <= compteur; i++)
    {
        if(i != elementSuivi_n && univers[i] != null)
        {
            univers[i].avancer_temps();
        }
    }

    if(objet_courant != 0)
    {
        document.getElementById("informations_contenu").innerHTML = univers[objet_courant].infos();
    }

    if(elementSuivi_n != 0)
    {
        vitesse_souris = produit(1/dt, somme([decalageX, decalageY], moins(position_souris)));
        position_souris = [decalageX, decalageY];
    }

    timer = setTimeout(avancer_temps, dt*1000);
}

function gerer_temps()
{
    if(timer != null)
    {
        clearTimeout(timer);
        timer = null;
        document.getElementById('lien_temps').innerHTML = "Lancer le temps";
    }
    else
    {
        avancer_temps();
        document.getElementById('lien_temps').innerHTML = "Arrêter le temps";
    }
}

/* gestion du drag & drop */
document.onmousemove = function(e) {
    if(elementSuivi != "") {
        if(decalageX == 0 || decalageY == 0) {
            decalageX = e.clientX;
            decalageY = e.clientY;
        }
        var doc = document.getElementById(elementSuivi);
        doc.style.top = parseInt(parseInt(doc.style.top)+e.clientY-decalageY) + "px";
        doc.style.left = parseInt(parseInt(doc.style.left)+e.clientX-decalageX) + "px";
        decalageX = e.clientX;
        decalageY = e.clientY;

        if(elementSuivi_n != 0)
        {
            var rayon = univers[elementSuivi_n].rayon;
            univers[elementSuivi_n].p = [parseInt(doc.style.left) + rayon, dimensions_univers[1] - parseInt(doc.style.top) - rayon];
            univers[elementSuivi_n].v = [vitesse_souris[0], - vitesse_souris[1]];
        }
    }
}

document.onmouseup = function() {
    /*
    if(elementSuivi_n != 0)
    {
        var doc = document.getElementById(elementSuivi);
        var rayon = univers[elementSuivi_n].rayon;
        univers[elementSuivi_n].p = [parseInt(doc.style.left) + rayon, dimensions_univers[1] - parseInt(doc.style.top) - rayon];
        univers[elementSuivi_n].v = [vitesse_souris[0], - vitesse_souris[1]];
    }
    */
    elementSuivi_n = 0;
    elementSuivi = "";
}

function suivre(id, n) {
    elementSuivi = id;
    elementSuivi_n = n;
    decalageX = 0;
    decalageY = 0;
}

function colorer(id) {
    document.getElementById(id).style.opacity = 0.8;
}

function acolorer(id) {
    document.getElementById(id).style.opacity = 0.5;
}

/* gestion des fenêtres */
function activer_infos(n)
{
    objet_courant = n;
    document.getElementById("informations").style.display = "block";
    changer_objet_courant(0);
}

function desactiver_infos()
{
    document.getElementById("informations_contenu").innerHTML = "Aucun élément sélectionné";
    document.getElementById("parametre_objet_m").value = "";
    document.getElementById("parametre_objet_r").value = "";
}

function ouvrir_fenetre(id)
{
    document.getElementById(id).style.display = "block";
}

function fermer_fenetre(id)
{
    if(id == 'informations')
    {
        objet_courant = 0;
        desactiver_infos();
    }
    document.getElementById(id).style.display = "none";
}

/* gestion de l'objet courant */
function changer_objet_courant(n)
{
    objet_courant = modulo(objet_courant + n, compteur);
    if(univers[objet_courant] == null)
    {
        changer_objet_courant(n);
    }
    else
    {
        document.getElementById("parametre_objet_m").value = univers[objet_courant].m;
        document.getElementById("parametre_objet_r").value = univers[objet_courant].rayon;
    }
}

function actualiser_parametres_objet_courant ()
{
    var rayon = parseInt(document.getElementById("parametre_objet_r").value);
    univers[objet_courant].m = parseInt(document.getElementById("parametre_objet_m").value);
    univers[objet_courant].rayon = rayon;
    document.getElementById("objet"+objet_courant).style.background = 'url("point_materiel_'+rayon+'.png") no-repeat';
    document.getElementById("objet"+objet_courant).style.height = (1 + 2*rayon)+"px";
    document.getElementById("objet"+objet_courant).style.width = (1 + 2*rayon)+"px";
}

function arreter_objet_courant()
{
    univers[objet_courant].v = [0, 0];
}

function detruire_objet_courant()
{
    document.getElementById("objet"+objet_courant).style.display = "none";
    univers[objet_courant] = null;
    objet_courant = 0;
    desactiver_infos();
}