
#include "Twood.h"

// shader en fin

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

Twood::Twood()
{
    //
}

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

Twood::~Twood()
{
    for (size_t i = 0; i < m_part.size(); i++)
    {
        if (m_part[i].vao_buffer[0] != 0) glDeleteBuffers(m_vao_nbr_buffer, m_part[i].vao_buffer);
        if (m_part[i].vao != 0) glDeleteVertexArrays(1, &m_part[i].vao);
        m_part[i].vao = 0;
    }
}

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

// 000 deco/ PineTree_3.obj 1 / donnees/image/wood_0.txt / "
void Twood::create(std::string flux)
{
    std::vector<std::string> strList;

    ECO_strCut(strList, flux, ' ');

    m_code = strList[0];
    m_path = strList[1];
    m_file = strList[2];
    m_cull_face = ECO_strToInt(strList[3]);
    // 4
    m_file_map = strList[5]; // chemin complet depuis programme

    _composition();

}

void Twood::create(std::ifstream& flux)
{
    std::string sep;

    flux >> m_code >> m_path >> m_file >> m_cull_face >> sep >> m_file_map >> sep;

    _composition();

}

void Twood::_composition()
{
    std::string file;

    dshWood.create();

    _load_map(m_file_map);

    // ---

    if (m_path != "null")
    {
        file = m_path + m_file;
    }
    else
    {
        file = m_file;
    }

    m_type_obj = false;

    if (m_file.find(".obj") != std::string::npos)
    {
        m_type_obj = true;
        OMesh_obj::P_setPath(PATH_MESH);
        if (!mesh_obj.create(file))
        {
            ECO_error_set("echec chargement %s >Twood::_composition", m_file.c_str());
            return;
        }
        for (size_t i = 0; i < mesh_obj.getPart_size(); i++)
        {
            if (mesh_obj.getPart(i).topologie_style != TOP_TRIANGLE)
            {
                ECO_error_set("obj triangulé uniquement > %s >Twood::_composition", m_file.c_str());
                return;
            }
        }
        _make_vao_obj();
    }
    else if (m_file.find(".glb") != std::string::npos || m_file.find(".gltf") != std::string::npos)
    {
        OMesh_gltf::P_setPath(PATH_MESH);
        if (!mesh_glb.create(file))
        {
            ECO_error_set("echec chargement %s >Twood::_composition", m_file.c_str());
            return;
        }
        _make_vao_glb();
    }
    else
    {
        ECO_error_set("erreur fichier %s >Twood", m_file.c_str());
        return;
    }

};



void Twood::draw(OMat4& vp)
{
    static float time = 0.0f;
    time += 0.05f; // 1/fps;

    dshWood.enable();
        dshWood.setVP(vp);
        dshWood.setTime(time);

    if (m_cull_face)
        glEnable(GL_CULL_FACE);
    else
        glDisable(GL_CULL_FACE);

    if (m_type_obj)
    {
        for (size_t i_par = 0; i_par < m_part.size(); i_par++)
        {
            dshWood.setTextures( mesh_obj.getId_diffuseMap(i_par), mesh_obj.getId_normalMap(i_par), mesh_obj.getId_specularMap(i_par));
            //
            glBindVertexArray(m_part[i_par].vao);
            glDrawElementsInstanced(GL_TRIANGLES, mesh_obj.getPart(i_par).indice.size(), GL_UNSIGNED_INT, 0, m_wood.size());
        }
    }
    else
    {
        // passage de ma verticale z en verticale gltf y
        dshWood.setVerticaleZ(true);
        for (size_t i_par = 0; i_par < m_part.size(); i_par++)
        {
            dshWood.setTextures( mesh_glb.getId_diffuseMap(i_par), mesh_glb.getId_normalMap(i_par), mesh_glb.getId_specularMap(i_par));
            glBindVertexArray(m_part[i_par].vao);
            glDrawElementsInstanced(GL_TRIANGLES, mesh_glb.getPart(i_par).indice.size(), GL_UNSIGNED_INT, 0, m_wood.size());

        }
        dshWood.setVerticaleZ(false);
    }

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE0);

}




void Twood::_load_map(std::string file)
{
    std::ifstream flux;
    std::string sep;
    float px, py, pz, rx, ry, rz, sxyz;
    size_t idx = 0;

    flux.open( file.c_str(), std::ios::in);
    if (!flux)
    {
        ECO_error_set("echec load %s", file.c_str());
        return;
    }
    //codage utf-8 / mode par defaut = inutile
	ECO_fileSeekBOM(flux);

	m_wood.clear();
	m_wood.reserve( _load_nb_line(m_file_map)+1 );

    while(!flux.eof())
    {
        // -115.0 -140.0 1.8 / 0 0 0 / 1.0 /
        flux >> px >> py >> pz >> sep >> rx >> ry >> rz >> sep >> sxyz >> sep;

        m_wood.push_back(s_wood());
        m_wood[idx].position.set(px,py,pz);
        m_wood[idx].rotation.set(rx,ry,rz);
        m_wood[idx].scale = sxyz;
        //m_wood[idx].windPhase = ECO_rnd_f(6.28318f); // 2 pi
        idx++;
    //}
    }

}


int Twood::_load_nb_line(std::string file)
{
    std::ifstream flux;
    std::string ligne;
    int result = 0;

    flux.open( file.c_str(), std::ios::in);
    if (!flux)
    {
        //ECO_error_set("echec load_nb_lignes");
        return 0;
    }
    //codage utf-8 / mode par defaut = inutile
	ECO_fileSeekBOM(flux);

	while(std::getline(flux, ligne))
    {
        //if (ligne.substr(0,1) == "#") continue;
        result++;
    }
    flux.close();

    return result;
}





void Twood::_make_vao_obj()
{
    m_part.resize(mesh_obj.getPart_size());

    for (size_t i_par = 0; i_par < mesh_obj.getPart_size(); i_par++)
    {
        const s_obj_part &meshPart = mesh_obj.getPart(i_par);

        //génération de l'identifiant du VAO
        glGenVertexArrays(1, &m_part[i_par].vao);
        //activation du VAO
        glBindVertexArray(m_part[i_par].vao);

        // créations des buffers
        glGenBuffers(m_vao_nbr_buffer, m_part[i_par].vao_buffer);

        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(OVec3_f) * meshPart.vertex.size(), &meshPart.vertex[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(LOCATION_POSITION);
        glVertexAttribPointer(LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[1]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(OVec3_f) * meshPart.normal.size(), &meshPart.normal[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(LOCATION_NORMAL);
        glVertexAttribPointer(LOCATION_NORMAL, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[2]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(OVec2_f) * meshPart.uv.size(), &meshPart.uv[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(LOCATION_TEXCOORD);
        glVertexAttribPointer(LOCATION_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, 0);

        // >>> connexion au vao instance
        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[3]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(s_wood) * m_wood.size(), m_wood.data(), GL_STATIC_DRAW);
        // Instance attributes
        glEnableVertexAttribArray(3);
        glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, position));
        glVertexAttribDivisor(3, 1);

        glEnableVertexAttribArray(4);
        glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, rotation));
        glVertexAttribDivisor(4, 1);

        glEnableVertexAttribArray(5);
        glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, scale));
        glVertexAttribDivisor(5, 1);

        //glEnableVertexAttribArray(6);
        //glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, windPhase));
        //glVertexAttribDivisor(6, 1);

        // ---

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_part[i_par].vao_buffer[4]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * meshPart.indice.size(), &meshPart.indice[0], GL_STATIC_DRAW);
    }

    // fin
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}


void Twood::_make_vao_glb()
{
    m_part.resize(mesh_glb.getPart_size());

    for (size_t i_par = 0; i_par < mesh_glb.getPart_size(); i_par++)
    {
        const s_gltf_part &meshPart = mesh_glb.getPart(i_par); // mesh_glb.meshPart[i_par];

        //génération de l'identifiant du VAO
        glGenVertexArrays(1, &m_part[i_par].vao);
        //activation du VAO
        glBindVertexArray(m_part[i_par].vao);

        // créations des buffers
        glGenBuffers(m_vao_nbr_buffer, m_part[i_par].vao_buffer);

        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(OVec3_f) * meshPart.vertex.size(), &meshPart.vertex[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(LOCATION_POSITION);
        glVertexAttribPointer(LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[1]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(OVec3_f) * meshPart.normal.size(), &meshPart.normal[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(LOCATION_NORMAL);
        glVertexAttribPointer(LOCATION_NORMAL, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[2]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(OVec2_f) * meshPart.uv.size(), &meshPart.uv[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(LOCATION_TEXCOORD);
        glVertexAttribPointer(LOCATION_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, 0);

        // >>> connexion au vao instance
        glBindBuffer(GL_ARRAY_BUFFER, m_part[i_par].vao_buffer[3]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(s_wood) * m_wood.size(), m_wood.data(), GL_STATIC_DRAW);
        // Instance attributes
        glEnableVertexAttribArray(3);
        glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, position));
        glVertexAttribDivisor(3, 1);

        glEnableVertexAttribArray(4);
        glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, rotation));
        glVertexAttribDivisor(4, 1);

        glEnableVertexAttribArray(5);
        glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, scale));
        glVertexAttribDivisor(5, 1);

        //glEnableVertexAttribArray(6);
        //glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, sizeof(s_wood), (void*)offsetof(s_wood, windPhase));
        //glVertexAttribDivisor(6, 1);

        // ---

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_part[i_par].vao_buffer[4]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * meshPart.indice.size(), &meshPart.indice[0], GL_STATIC_DRAW);
    }

    // fin
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}




void Twood::save(std::ofstream& flux)
{
    flux << FLUX_WOOD1 << " ";

    flux << m_code << " " << m_path << " " << m_file << " " << m_cull_face << " / ";

    flux << std::endl;
}




// --- shader ---

static const char* pVS = " \n\
#version 330 \n\
 \n\
layout (location = 0) in vec3 in_vertex; \n\
layout (location = 1) in vec3 in_normal; \n\
layout (location = 2) in vec2 in_uv; \n\
layout (location = 3) in vec3 instancePos; \n\
layout (location = 4) in vec3 instanceRot; \n\
layout (location = 5) in float instanceScale; \n\
//layout (location = 6) in float instanceWind; \n\
 \n\
uniform mat4 uVP; \n\
uniform float uTime; \n\
uniform float uWindSpeed = 20.0; \n\
uniform float uFlexibility = 0.005; \n\
uniform bool uVerticaleZ = false; \n\
 \n\
out vec2 var_uv; \n\
out vec3 var_normal; \n\
out vec3 var_model; \n\
out float var_colorVariation; \n\
//flat out int var_instanceID; \n\
 \n\
float random (vec2 st) \n\
{\n\
    return fract(sin(dot(st.xy,vec2(12.9898,78.233)))*43758.5453123);\n\
    //return mod((st.x*st.x)+(0.13-st.x*st.y), 0.01)/0.01; \n\
}\n\
 \n\
mat4 rotationMatrix(vec3 rot) { \n\
    float cx,sx; \n\
    // pour gltf (verticale y) dans ma verticale z \n\
    if (uVerticaleZ) {cx = cos(rot.x-90); sx = sin(rot.x-90); } else {cx = cos(rot.x); sx = sin(rot.x); } \n\
    //float cx = -cos(rot.x), sx = -sin(rot.x); \n\
    float cy = cos(rot.y), sy = sin(rot.y); \n\
    float cz = cos(rot.z), sz = sin(rot.z); \n\
 \n\
    mat4 rotX = mat4(1,0,0,0, 0,cx,-sx,0, 0,sx,cx,0, 0,0,0,1); \n\
    mat4 rotY = mat4(cy,0,sy,0, 0,1,0,0, -sy,0,cy,0, 0,0,0,1); \n\
    mat4 rotZ = mat4(cz,-sz,0,0, sz,cz,0,0, 0,0,1,0, 0,0,0,1); \n\
 \n\
    return rotZ * rotY * rotX; \n\
} \n\
 \n\
void main() \n\
{ \n\
    // Calcul de la matrice modèle \n\
    mat4 scale = mat4( \n\
        instanceScale, 0.0, 0.0, 0.0, \n\
        0.0, instanceScale, 0.0, 0.0, \n\
        0.0, 0.0, instanceScale, 0.0, \n\
        0.0, 0.0, 0.0, 1.0 \n\
    ); \n\
 \n\
    mat4 rotation = rotationMatrix(instanceRot); \n\
    mat4 translation = mat4( \n\
        1,0,0,0, \n\
        0,1,0,0, \n\
        0,0,1,0, \n\
        instancePos.x, instancePos.y, instancePos.z, 1 \n\
    ); \n\
    mat4 model = translation * rotation * scale; \n\
 \n\
    vec3 pos = in_vertex; \n\
    var_uv = in_uv; \n\
    var_model = (model * vec4(pos, 1.0)).xyz; \n\
    var_normal = (model * vec4(in_normal, 0.0)).xyz; \n\
    var_normal = normalize(var_normal); \n\
    float rnd = random(var_model.xy); \n\
    var_colorVariation = 1.0 - rnd*0.25; \n\
\n\
    // tous les arbres ne bougent pas de la même façon \n\
    float windflux = rnd * uWindSpeed*0.1; \n\
    // Calcul de la hauteur du sommet > seul le sommet bouge\n\
    float b = uWindSpeed * uFlexibility * pos.z * 0.2; \n\
    pos.x += cos((uTime+var_model.x) *0.5) * b; \n\
    pos.y += sin((uTime+var_model.y) *0.25) * b; \n\
 \n\
    gl_Position = uVP * model * vec4(pos, 1.0); \n\
}";


static const char* pFS = " \n\
#version 330 core \n\
 \n\
in vec2 var_uv; \n\
in vec3 var_normal; \n\
in vec3 var_model; \n\
in float var_colorVariation; \n\
//flat in int var_instanceID; \n\
 \n\
layout (location = 0) out vec3 positionMap; \n\
layout (location = 1) out vec4 colorMap; \n\
layout (location = 2) out vec3 normalMap; \n\
 \n\
uniform sampler2D uColorMap; \n\
uniform sampler2D uNormalMap; \n\
uniform sampler2D uSpecularMap; \n\
 \n\
vec3 calcBumpNormal() \n\
{ \n\
    vec3 Tangent0;  \n\
    vec3 c1 = cross(var_normal, vec3(0.0, 0.0, 1.0));  \n\
    vec3 c2 = cross(var_normal, vec3(0.0, 1.0, 0.0));  \n\
    if (length(c1) > length(c2))  \n\
        Tangent0 = c1; \n\
    else  \n\
        Tangent0 = c2; \n\
    \n\
    // normale bump \n\
    vec3 Normal = normalize(var_normal); \n\
    vec3 Tangent = normalize(Tangent0); \n\
    Tangent = normalize(Tangent - dot(Tangent, Normal) * Normal); \n\
    vec3 Bitangent = cross(Tangent, Normal); \n\
    vec3 BumpMapNormal = texture(uNormalMap, var_uv).xyz; \n\
    BumpMapNormal = (2.0 * BumpMapNormal) - vec3(1.0,1.0,1.0); \n\
    vec3 NewNormal; \n\
    mat3 TBN = mat3(Tangent, Bitangent, Normal); \n\
    NewNormal = TBN * BumpMapNormal; \n\
    return normalize(NewNormal); \n\
} \n\
 \n\
void main(){ \n\
    vec4 color = texture(uColorMap, var_uv); \n\
	float alpha = smoothstep(0.6, 0.95, color.a); \n\
    if (alpha < 0.05) discard; \n\
    color *= var_colorVariation; \n\
    color.a = texture(uSpecularMap, var_uv).a; \n\
    \n\
    positionMap = var_model; \n\
    colorMap = color; \n\
    normalMap = calcBumpNormal(); \n\
}";




Twood_shader::Twood_shader()
{
	//
}

bool Twood_shader::create()
{
    if (!ODshader::init())
    {
        ECO_error_set("echec init Twood_shader");
        return false;
    }
    if (!addShader(GL_VERTEX_SHADER, pVS))
    {
        ECO_error_set("echec add vertex Twood_shader");
        return false;
    }
    if (!addShader(GL_FRAGMENT_SHADER, pFS))
    {
        ECO_error_set("echec add fragment Twood_shader");
        return false;
    }
    if (!finalize())
    {
        ECO_error_set("echec finalize Twood_shader");
        return false;
    }

    m_vp = getUniformLocation("uVP");
	m_colorMap = getUniformLocation("uColorMap");
    m_normalMap = getUniformLocation("uNormalMap");
    m_specularMap = getUniformLocation("uSpecularMap");
    m_time = getUniformLocation("uTime");
    m_verticaleZ = getUniformLocation("uVerticaleZ");
/*
	if (m_vp == INVALID_UNIFORM_LOCATION
    || m_colorMap == INVALID_UNIFORM_LOCATION
    || m_normalMap == INVALID_UNIFORM_LOCATION
    || m_specularMap == INVALID_UNIFORM_LOCATION
    || m_time == INVALID_UNIFORM_LOCATION
    || m_verticaleZ == INVALID_UNIFORM_LOCATION
    )
    {
        ECO_error_set("Echec Twood_shader");
        return false;
    }
*/
    return true;
}

void Twood_shader::setVP(const OMat4& vp)
{
    glUniformMatrix4fv(m_vp, 1, GL_TRUE, vp.getFv());
}




void Twood_shader::setTextures(uint32_t texture, uint32_t normal, uint32_t specular)
{
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, specular);
    glUniform1i(m_specularMap, 2);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, normal);
    glUniform1i(m_normalMap, 1);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(m_colorMap, 0);

    //glActiveTexture(GL_TEXTURE0);
}

// +1/fps
void Twood_shader::setTime(float value)
{
    glUniform1f(m_time, value);
}

void Twood_shader::setVerticaleZ(bool mode)
{
    glUniform1ui(m_verticaleZ, mode);
}




// ---


//depuis fichier texte > grille construite depuis blender >
bool Twood_make(std::string file)
{
    std::ifstream flux_in(file, std::ios::in);
    std::ofstream flux_out("donnees/image/test.txt", std::ios::out);

    float a,d;
    float px,py,pz, rx,ry,rz, sxyz;
    std::string sep1, sep2, sep3;

    if (!flux_in)
    {
        ECO_error_set("echec ouverture %s", file.c_str());
        return false;
    }
    ECO_fileSeekBOM(flux_in);

    if (!flux_out)
    {
        ECO_error_set("echec ecriture");
        return false;
    }

    while(!flux_in.eof())
    {
        // decompose > -115.0 -140.0 1.8 / 0 0 0 / 1.0 /
        flux_in >> px >> py >> pz >> sep1 >> rx >> ry >> rz >> sep2 >> sxyz >> sep3;

        a = ECO_rnd_f(360.0f);
        d = ECO_rnd_f(0.75f) + 0.25f;
        px += cos(a*ECO_DEGTORAD) * d;
        py += sin(a*ECO_DEGTORAD) * d;
        rz = ECO_rnd_f(360.0f);
        sxyz = 0.5f + ECO_rnd_f(1.2f);

        flux_out << px << " " << py << " " << pz << " / " << rx << " " << ry << " " << rz << " / " << sxyz << " /" << std::endl;
    }

    return true;
}


