
#include "TprtJet.h"

// raffraichissement 1/50 (ou 1/60 ?)
static uint64_t     p_fps = 50;
static float        p_ratio = 1.0f / 50.0f;

// -----------------------
// --- le constructeur ---
// -----------------------

TprtJet::TprtJet()
{
    m_vertex = 0;
    m_uv = 0;
    m_texParam.id = 0;
    m_particle_buffer = 0;
    m_particle_color_buffer = 0;
    //
    m_visible = false;
}

// ----------------------
// --- le destructeur ---
// ----------------------

TprtJet::~TprtJet()
{
    glDeleteBuffers(1, &m_vertex);
    glDeleteBuffers(1, &m_uv);
    glDeleteTextures(1, &m_texParam.id);
	glDeleteBuffers(1, &m_particle_buffer);
	glDeleteBuffers(1, &m_particle_color_buffer);
}

// --------------------
// --- les méthodes ---
// --------------------

void TprtJet::create_by(std::string code)
{
    /*
    // ecoulement de tuyau
    "jet0 images/ particles.png 0 0 32 32 / 1000 10 9999 / -5 5 270 280 / 0.1 0.5 / 0.02 0 0 0 / 0.01 0.03 3 3.5 / 1 1 1 1 1 1 1 1 / 0.9 -0.1 0 /"
    // splash
    "jet0 images/ particles.png 0 0 32 32 / 10 6 1 / 0 360 30 60 / 1.0 1.5 / 0.1 0 0 0 / 0.5 1 1 2 / 1 1 1 1 1 1 1 1 / 0.985 -0.1 0 /"
    */
    if (code == "splash")
    {
    m_code = "splash";
    m_path = "modJeu/donnees/";
    m_file = "chapeau.png";
    m_src.set(816,0,48,48);
    //
    m_maxParticles = 10;
    m_param.generation_nbr = 6;     // nombre de particules nouvelles
    m_param.generation_loop = 1;    // combien de fois est exécutée la generation
    //
    m_param.angleH.set(0.0f,360.0f);
    m_param.angleV.set(30.0f, 60.0f);
    m_param.vit_ejection.set(1.0f, 1.5f);
    m_param.zone.w = 0;     // sphere cube plan *** à faire
    m_param.zone.x = 0.1f;  // rayon
    //
    m_param.size_var.set(0.5f, 1.0f);
    m_param.life_var.set(1.0f, 2.0f);   // seconde
    m_param.color_min.set(1,1,1,1);
    m_param.color_max.set(1,1,1,1);
    //
    m_var_air = 0.985f;      // pourcentage (multiplie la vitesse, positive négative)
    m_var_gravite = -0.1f;   // ajout
    m_var_color = 0.0f;
    //
    }

    _composition();
}

// code path file src.xywh / max generation_nbr gen_loop / anhleH.min.max angleV.min.max /
// vitesse.min.max / zone.type zone.yzw / size.min.max life.min.max /
// color.rgb.min color.rgb.max / var_air var_gravite var_color /
void TprtJet::create(std::ifstream& flux)
{
    std::string sep;

    flux >> m_code >> m_path >> m_file >> m_src.x >> m_src.y >> m_src.w >> m_src.h >> sep;
    flux >> m_maxParticles >> m_param.generation_nbr >> m_param.generation_loop >> sep;
    flux >> m_param.angleH.x >> m_param.angleH.y >> m_param.angleV.x >> m_param.angleV.y >> sep;
    flux >> m_param.vit_ejection.x >> m_param.vit_ejection.y >> sep;
    flux >> m_param.zone.x >> m_param.zone.y >> m_param.zone.z >> m_param.zone.w >> sep;
    flux >> m_param.size_var.x >> m_param.size_var.y >> m_param.life_var.x >> m_param.life_var.y >> sep;
    flux >> m_param.color_min.r >> m_param.color_min.g >> m_param.color_min.b >> m_param.color_min.a;
    flux >> m_param.color_max.r >> m_param.color_max.g >> m_param.color_max.b >> m_param.color_max.a >> sep;
    flux >> m_var_air >> m_var_gravite >> m_var_color >> sep;

    _composition();
}

void TprtJet::create(std::string flux)
{
    std::vector<std::string> strList;
    std::string sep;

    ECO_strCut(strList, flux, ' ');

    m_code = strList[0];
    m_path = strList[1];
    m_file = strList[2];
    m_src.x = ECO_strToInt( strList[3] );
    m_src.y = ECO_strToInt( strList[4] );
    m_src.w = ECO_strToInt( strList[5] );
    m_src.h = ECO_strToInt( strList[6] );
    // sep 7
    m_maxParticles = ECO_strToInt( strList[8] );
    m_param.generation_nbr = ECO_strToInt( strList[9] );    // nombre de particules nouvelles
    m_param.generation_loop = ECO_strToInt( strList[10] );  // combien de fois est exécutée la generation
    // 11
    m_param.angleH.x = ECO_strToFloat( strList[12] );
    m_param.angleH.y = ECO_strToFloat( strList[13] );

    m_param.angleV.x = ECO_strToFloat( strList[14] );
    m_param.angleV.y = ECO_strToFloat( strList[15] );
    // 16
    m_param.vit_ejection.x = ECO_strToFloat( strList[17] );
    m_param.vit_ejection.y = ECO_strToFloat( strList[18] );
    // 19
    m_param.zone.x = ECO_strToFloat( strList[20] );
    m_param.zone.y = ECO_strToFloat( strList[21] );
    m_param.zone.z = ECO_strToFloat( strList[22] );
    m_param.zone.w = ECO_strToFloat( strList[23] ); // type 0=sphere (nom implémenté 1=parallepipede
    // 24
    m_param.size_var.x = ECO_strToFloat( strList[25] );
    m_param.size_var.y = ECO_strToFloat( strList[26] );

    m_param.life_var.x = ECO_strToFloat( strList[27] );
    m_param.life_var.y = ECO_strToFloat( strList[28] );
    // 29
    m_param.color_min.r = ECO_strToFloat( strList[30] );
    m_param.color_min.g = ECO_strToFloat( strList[31] );
    m_param.color_min.b = ECO_strToFloat( strList[32] );
    m_param.color_min.a = ECO_strToFloat( strList[33] );

    m_param.color_max.r = ECO_strToFloat( strList[34] );
    m_param.color_max.g = ECO_strToFloat( strList[35] );
    m_param.color_max.b = ECO_strToFloat( strList[36] );
     m_param.color_max.a = ECO_strToFloat( strList[37] );
    // 38
    m_var_air = ECO_strToFloat( strList[39] );
    m_var_gravite = ECO_strToFloat( strList[40] );
    m_var_color = ECO_strToFloat( strList[41] );
    // 42

    _composition();
}

void TprtJet::_composition()
{
    m_visible = false;
    m_pos.set(0,0,0);
    m_nbParticles = 0;
    if (m_path == "null") m_path = "";
    _load_texture(m_path + m_file);
    _make();
}




void TprtJet::draw()
{
    if (!m_visible) return;

    //glEnable(GL_DEPTH_TEST);
	//glEnable(GL_TEXTURE_2D);
	//glEnable(GL_BLEND);
    //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //glDepthMask(GL_FALSE);

	glBindBuffer(GL_ARRAY_BUFFER, m_particle_buffer);
	glBufferData(GL_ARRAY_BUFFER, m_nbParticles * 4 * sizeof(float), NULL, GL_STREAM_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER, 0, m_nbParticles * 4 * sizeof(float), &m_particle_data[0]);

    glBindBuffer(GL_ARRAY_BUFFER, m_particle_color_buffer);
    glBufferData(GL_ARRAY_BUFFER, m_nbParticles * 4 * sizeof(float), NULL, GL_STREAM_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, m_nbParticles * 4 * sizeof(float), &m_particle_color[0]);

    //vp = projection * view;
	//shdParticle3d.enable();
        //matrice horizontale
        //shdParticle3d.setCameraRight(view.getValue(0), view.getValue(1), view.getValue(2));
		//shdParticle3d.setCameraUp(view.getValue(4), view.getValue(5), view.getValue(6));
		//shdParticle3d.setVP(vp);

        // passage des attributs
        glBindBuffer(GL_ARRAY_BUFFER, m_vertex);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

        glBindBuffer(GL_ARRAY_BUFFER, m_uv);
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

        glBindBuffer(GL_ARRAY_BUFFER, m_particle_buffer);
        glEnableVertexAttribArray(2);
        glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);

        // GL_TRUE => normalise
        glBindBuffer(GL_ARRAY_BUFFER, m_particle_color_buffer);
        glEnableVertexAttribArray(3);
        glVertexAttribPointer(3, 4, GL_FLOAT, GL_TRUE, 0, (void*)0);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, m_texParam.id);

        // spécifique à glDrawArraysInstanced.
        // le premier paramètre fait référence au buffer (m_vertex et m_particles positions)
        // le second paramètre est le pas d'avancement
        // http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml
        glVertexAttribDivisor(0, 0); // toujours les 4 mêmes vertices
        glVertexAttribDivisor(1, 0); // uv
        glVertexAttribDivisor(2, 1); // position suivante à chaque appel
        glVertexAttribDivisor(3, 1); // 1 par quad
        glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, m_nbParticles);
        //impératif sinon erreur d'affichage sur l'interface 2d
        glVertexAttribDivisor(3, 0);
        glVertexAttribDivisor(2, 0);
        //glVertexAttribDivisor(0, 0);

        //glDisableVertexAttribArray(4);
        glDisableVertexAttribArray(3);
        glDisableVertexAttribArray(2);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(0);
    //shdParticle.disable();

    //désactive !
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    //glDepthMask(GL_TRUE);
}




std::string TprtJet::getCode()
{
    return m_code;
}

void TprtJet::generation()
{
    static int idx = 0;

    float ah,av,vit;
    float radius;

    if (!m_visible) return;
    if (m_loop == 0) m_visible = false; // ! avant la decrementation sinon rien n'est visible
    m_loop--;

    // ---

    for (int n = 0; n < m_param.generation_nbr; n++)
    {
        ah = ECO_rnd_f(m_param.angleH.x, m_param.angleH.y) * 0.01745329f; //ECO_DEGTORAD
        av = ECO_rnd_f(m_param.angleV.x, m_param.angleV.y) * 0.01745329f;
        vit = ECO_rnd_f(m_param.vit_ejection.x, m_param.vit_ejection.y);
        radius = ECO_rnd_f(m_param.zone.x);

        m_particle[idx].pos.x = m_pos.x + (cos(ah) * radius);
        m_particle[idx].pos.y = m_pos.y + (sin(ah) * radius);
        m_particle[idx].pos.z = m_pos.z + (sin(av) * radius);

        m_particle[idx].speed.x = cos(ah) * vit;
        m_particle[idx].speed.y = sin(ah) * vit;
        m_particle[idx].speed.z = sin(av) * vit;
        //
        if (m_param.color_min < m_param.color_max)
        {
            m_particle[idx].r = ECO_rnd_f(m_param.color_min.r, m_param.color_max.r);
            m_particle[idx].g = ECO_rnd_f(m_param.color_min.g, m_param.color_max.g);
            m_particle[idx].b = ECO_rnd_f(m_param.color_min.b, m_param.color_max.b);
            m_particle[idx].a = ECO_rnd_f(m_param.color_min.a, m_param.color_max.a);
        }
        else
        {
            m_particle[idx].r = m_param.color_min.r;
            m_particle[idx].g = m_param.color_min.g;
            m_particle[idx].b = m_param.color_min.b;
            m_particle[idx].a = m_param.color_min.a;
        }

        m_particle_color[(idx*4) +0] = m_particle[idx].r;
        m_particle_color[(idx*4) +1] = m_particle[idx].g;
        m_particle_color[(idx*4) +2] = m_particle[idx].b;
        m_particle_color[(idx*4) +3] = m_particle[idx].a;
        //
        m_particle[idx].size = ECO_rnd_f(m_param.size_var.x, m_param.size_var.y);
        //
        m_particle[idx].life = ECO_rnd_f(m_param.life_var.x, m_param.life_var.y) * p_fps;
        //

        idx++;
        if (idx == m_maxParticles)
        {
            idx = 0;
        }
    }

    //cherche la dernière particule
    m_nbParticles = 0;
    for (int i = m_maxParticles-1; i >= 0 ; i--)
    {
        if (m_particle[i].life > 0)
        {
            m_nbParticles = i+1;
            break;
        }
    }
    if (m_nbParticles == 0) m_visible = false;
}





void TprtJet::hide()
{
    m_visible = false;
}




void TprtJet::_load_texture(std::string file)
{
	OSurface surface;

	surface.load(file);
	if (!surface.exist())
	{
		ECO_error_set("echec chargement %s >TprtJet.load_texture", file.c_str());
		return;
	}

    //activation des textures
    glEnable(GL_TEXTURE_2D);
    if (m_texParam.id) glDeleteTextures(1, &m_texParam.id);
    //generation de la texture
    glGenTextures(1, &m_texParam.id);
    //selection de la texture
    glBindTexture(GL_TEXTURE_2D, m_texParam.id);
	// génération
	m_texParam.w = surface.getWidth();
	m_texParam.h = surface.getHeight();
	glTexImage2D(GL_TEXTURE_2D, 0, surface.getBpp(), m_texParam.w, m_texParam.h, 0, surface.getFormat(), GL_UNSIGNED_BYTE, surface.getPixels() );

	// on applique les filtres usuels
	if (((m_texParam.w & (m_texParam.w-1)) != 0) || ((m_texParam.h & (m_texParam.h-1)) != 0))
	{
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST );
        glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
        //OTEXTURELIST_INTERFACE:
        // texture proche = non lissée
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        // texture lointaine ou scale
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	}

	//déverrouillage libération
    glBindTexture(GL_TEXTURE_2D, 0);
}





void TprtJet::_make()
{
    m_particle.resize(m_maxParticles, s_particle());
	m_particle_data.resize(m_maxParticles * 4, 0.0f);
	m_particle_color.resize(m_maxParticles * 4, 1.0f);

    // ordre impératif (parce que STRIP)
    float vertex[] = {
        0.0f, 0.0f, 0.0f,
		1.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f,
		1.0f, 1.0f, 0.0f
	};

    float ratioW = 1.0f / (float)m_texParam.w;
    float ratioH = 1.0f / (float)m_texParam.h;
    float x0,y0,x1,y1;
    float uv[4*2];

    x0 = m_src.x * ratioW;
    y0 = (m_texParam.h - m_src.y - m_src.h) * ratioH;
    x1 = x0 + (m_src.w * ratioW);
    y1 = y0 + (m_src.h * ratioH);

	uv[0] = x0; uv[1] = y0;
	uv[2] = x1; uv[3] = y0;
	uv[4] = x0; uv[5] = y1;
	uv[6] = x1; uv[7] = y1;

    if (m_vertex) glDeleteBuffers(1, &m_vertex);
    if (m_uv) glDeleteBuffers(1, &m_uv);
	if (m_particle_buffer) glDeleteBuffers(1, &m_particle_buffer);
    if (m_particle_color_buffer) glDeleteBuffers(1, &m_particle_color_buffer);

	glGenBuffers(1, &m_vertex);
	glBindBuffer(GL_ARRAY_BUFFER, m_vertex);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);

    glGenBuffers(1, &m_uv);
	glBindBuffer(GL_ARRAY_BUFFER, m_uv);
	glBufferData(GL_ARRAY_BUFFER, sizeof(uv), uv, GL_STATIC_DRAW);

	glGenBuffers(1, &m_particle_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, m_particle_buffer);
	glBufferData(GL_ARRAY_BUFFER, m_maxParticles * 4 * sizeof(float), NULL, GL_STREAM_DRAW);

    glGenBuffers(1, &m_particle_color_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, m_particle_color_buffer);
    glBufferData(GL_ARRAY_BUFFER, m_maxParticles*4 * sizeof(float), NULL, GL_STREAM_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
}





void TprtJet::save(std::ofstream& flux)
{
    flux << m_code << " " <<  m_path << " " <<  m_file << " ";
    flux << m_src.x << " " << m_src.y << " " << m_src.w << " " << m_src.h << " / ";
    flux << m_maxParticles << " " <<  m_param.generation_nbr << " " <<  m_param.generation_loop << " / ";
    flux << m_param.angleH.x << " " <<  m_param.angleH.y << " " <<  m_param.angleV.x << " " <<  m_param.angleV.y << " / ";
    flux << m_param.vit_ejection.x << " " <<  m_param.vit_ejection.y << " / ";
    flux << m_param.zone.x << " " <<  m_param.zone.y << " " <<  m_param.zone.z << " " <<  m_param.zone.w << " / ";
    flux << m_param.size_var.x << " " <<  m_param.size_var.y << " " <<  m_param.life_var.x << " " <<  m_param.life_var.y << " / ";
    flux << m_param.color_min.r << " " <<  m_param.color_min.g << " " <<  m_param.color_min.b << " " << m_param.color_min.a;
    flux << m_param.color_max.r << " " <<  m_param.color_max.g << " " <<  m_param.color_max.b << " " << m_param.color_max.a << " / ";
    flux << m_var_air << " " <<  m_var_gravite << " " << m_var_color << " /";
}

void TprtJet::setPos(OVec3_f& pos)
{
    m_pos = pos;
}

void TprtJet::start(OVec3_f& pos)
{
    m_pos = pos;
    m_visible = true;
    m_loop = m_param.generation_loop;
    generation();
}

void TprtJet::stop()
{
    m_visible = false;
}




void TprtJet::update(float ambient)
{
    bool exist = false;
    int idx;

    if (!m_visible) return;

    for (int i = 0; i < m_nbParticles; i++)
    {
        s_particle& p = m_particle[i]; // shortcut

        if (p.life > 0)
        {
            exist = true;

            p.life--;
            if (p.life < 50)
            {
                p.a -= 0.02f;
            }

            p.speed.x *= m_var_air;
            p.speed.y *= m_var_air;
            p.speed.z += m_var_gravite;

            p.pos += p.speed * p_ratio;

            p.r += m_var_color;
            p.g += m_var_color;
            p.b += m_var_color;

            // mise à jour
            idx = 4*i;
            m_particle_data[idx+0] = p.pos.x;
            m_particle_data[idx+1] = p.pos.y;
            m_particle_data[idx+2] = p.pos.z;

            m_particle_data[idx+3] = p.size;

            m_particle_color[idx+0] = p.r * ambient;
            m_particle_color[idx+1] = p.g * ambient;
            m_particle_color[idx+2] = p.b * ambient;
            m_particle_color[idx+3] = p.a; // * ambient;
        }
    }

    if (!exist) m_visible = false;
}

// -----------------
// --- propriété ---
// -----------------

void TprtJet::p_setFps(uint64_t value)
{
    p_fps = value;
    p_ratio = 1.0f / value;
}




