
#include "Tperso.h"

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

Tperso::Tperso()
{
	//
}

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

Tperso::~Tperso()
{
    for (size_t i = 0; i < ECO_sizeOf(attache); i++)
    {
        attache[i].pt_objet = nullptr;
    }
}

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

void Tperso::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_pos.x = ECO_strToFloat(strList[5]);
	m_pos.y = ECO_strToFloat(strList[6]);
	m_pos.z = ECO_strToFloat(strList[7]);
	m_rot.x = ECO_strToFloat(strList[8]);
	m_rot.y = ECO_strToFloat(strList[9]);
	m_rot.z = ECO_strToFloat(strList[10]);
    m_scale.x = ECO_strToFloat(strList[11]);
	m_scale.y = ECO_strToFloat(strList[12]);
	m_scale.z = ECO_strToFloat(strList[13]);
	// 14

    _composition();
    _setModel();
}

void Tperso::_composition()
{
    OMesh_gltf::P_setPath(PATH_MESH);

    // securite
    for (size_t i = 0; i < ECO_sizeOf(attache); i++)
    {
        attache[i].pt_objet = nullptr;
    }

    if (m_file.find(".glb") != std::string::npos || m_file.find(".gltf") != std::string::npos)
    {
        std::string file;
        if (m_path != "null")
        {
            file = m_path + m_file;
        }
        else
        {
            file = m_file;
        }
        if (!mesh.create(file))
        {
            ECO_error_set("echec chargement %s >Tperso::_composition", m_file.c_str());
            return;
        }
    }
    else
    {
        ECO_error_set("erreur fichier %s >Tperso", m_file.c_str());
        return;
    }

    // ---

    m_state_type = PER_STATE_NULL;
    m_state_delay = 0.0f;
    m_state_rot = 0.0f;
    OMesh_obj::P_setPath(PATH_MESH);
    mesh_ko.create("perso/ko.obj");
    billfire.create();
    m_select = false;

    for (size_t i_par = 0; i_par < mesh.getPart_size(); i_par++)
    {
        m_emissive.push_back(s_emissive());
        if (mesh.getEmissive(i_par).a > 0.0f)
        {
            m_emissive[i_par].max = mesh.getEmissive(i_par).a;    // energie max
            m_emissive[i_par].value = 1.0f;                         // energie actuelle (partie éclairée)
        }
    }
/*
    OVec3_f b;
    ECO_error_set("%s", m_file.c_str());
    b = mesh.getBound_min();
    ECO_error_set("min %f %f %f", b.x, b.y, b.z);
    b = mesh.getBound_max();
    ECO_error_set("max %f %f %f", b.x, b.y, b.z);
*/
}




// les etats (freeze, ko, ...) doivent bloquer les animations !
void Tperso::anim_start(int index, int loop)
{
    if (index < 0)
    {
        mesh.anim_stop();
    }
    else
    {
        mesh.anim_start(index, loop);
    }
}

void Tperso::anim_start(std::string name, int loop)
{
    mesh.anim_start(name, loop);
}

void Tperso::anim_stop()
{
    mesh.anim_stop();
}




void Tperso::draw(ODshGeo& dshGeo, ODshFreeze& dshFreeze)
{
    switch(m_state_type)
    {
    case PER_STATE_NULL:
        _draw(dshGeo);
        break;

    case PER_STATE_FREEZE_UP:
    case PER_STATE_FREEZE:
    case PER_STATE_FREEZE_DOWN:
        _draw_freeze(dshFreeze);
        break;

    case PER_STATE_KO:
        _draw(dshGeo);
        _draw_ko(dshGeo);
        break;

    //case PER_STATE_FIRE:
    // > billboard
    //    break;

    default:
        _draw(dshGeo);
        break;
    }
}

void Tperso::_draw(ODshGeo& dshGeo)
{
    OMat4 model1;

    dshGeo.enable();
        dshGeo.setModel(m_model);
        dshGeo.setMaskValue(m_select * PPOUTLINE_GREEN);

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

    dshGeo.setBones(false);

    if (mesh.anim_running())
    {
        //std::vector<OMat4> axis;
        mesh.anim_getAxis(m_anim_axis);

        dshGeo.setBones(true);
        dshGeo.setJointMat(m_anim_axis);
    }
    else if (mesh.getAnimation_size() > 0)
    {
        dshGeo.setBones(true);
        dshGeo.setJointMat(mesh.getAxisNeutral());
    }

    for (size_t i_par = 0; i_par < mesh.getPart_size(); i_par++)
    {
        if (!mesh.getPart_visibility(i_par)) continue;

        switch(mesh.getAnimation_mode(i_par))
        {
        case GLTF_ANIM_NULL:
            dshGeo.setBones(false);
            break;
        case GLTF_ANIM_RIG:
            dshGeo.setBones(true);
            break;
        case GLTF_ANIM_AXIS:
            dshGeo.setBones(false);
            model1 = m_model * mesh.getJointOfPart(i_par);
            dshGeo.setModel(model1);
            break;
        case GLTF_ANIM_MORPH:
            dshGeo.setBones(false);
            break;
        default:
            dshGeo.setBones(false);
            break;
        }

        dshGeo.setTextures(mesh.getId_diffuseMap(i_par), mesh.getId_normalMap(i_par), mesh.getId_specularMap(i_par) );
        mesh.draw(i_par);

        dshGeo.setModel(m_model);
    }
    mesh.end_draw_part();

    dshGeo.setBones(false);

    // --------------------------
    // >> les objets d'equipement
    // --------------------------

    for (size_t i_obj = 0; i_obj < ECO_sizeOf(attache); i_obj++)
    {
        if (attache[i_obj].pt_objet == nullptr) continue;

        //OMesh_gltf& tomesh = attache[i_obj].pt_objet->getMesh();
        OMesh_gltf& tomesh = attache[i_obj].pt_objet->mesh;

        for (size_t i_par = 0; i_par < tomesh.getPart_size(); i_par++)
        {
            dshGeo.setTextures( tomesh.getId_diffuseMap(i_par), tomesh.getId_normalMap(i_par), tomesh.getId_specularMap(i_par) );

            model1 = m_model;
            model1 *= mesh.getAxisAttach(attache[i_obj].ref_axe);

            model1.translate(attache[i_obj].decal_pos);
            model1.rotate(attache[i_obj].decal_rot.y, 0,1,0); // verticale gltf
            model1.rotate(attache[i_obj].decal_rot.z, 0,0,1);
            model1.rotate(attache[i_obj].decal_rot.x, 1,0,0);

            dshGeo.setModel(model1);

            tomesh.draw(i_par);
        }
        tomesh.end_draw_part();
    }

    dshGeo.setMaskValue(false);
}

void Tperso::draw(OShLight& shLight, OVec3_f& camDir)
{
    if (m_state_type != PER_STATE_FIRE) return;

    // vitesse de generation
    m_state_delay += 0.25f;
    if (m_state_delay > 1.0f)
    {
        m_state_delay = 0.0f;
        billfire.generate(m_pos);
    }

    // montée des flammes
    billfire.update();

    // rotation car glft a Y en verticale > notre verticale Z
    OVec3_f eyePos = camDir;
    eyePos.rotateX(90);

    billfire.draw(shLight, eyePos);
}


void Tperso::_draw_freeze(ODshFreeze& dshFreeze)
{
    // update
    if (m_state_type == PER_STATE_FREEZE_UP)
    {
        m_state_delay += 0.02f;
        if (m_state_delay >= 1.0f) m_state_type = PER_STATE_FREEZE;
    }

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

    dshFreeze.enable();
        dshFreeze.setModel(m_model);
        dshFreeze.setBones(false);
        dshFreeze.setFreezeAmount( m_state_delay ); // 0 > 1
/*
    if (mesh.anim_running())
    {
        //std::vector<OMat4> m_anim_axis;
        mesh.anim_getAxis(m_anim_axis);

        dshFreeze.setBones(true);
        dshFreeze.setJointMat(m_anim_axis);
    }
    else if (mesh.getAnimation_size() > 0)
    {*/
        dshFreeze.setBones(true);
        dshFreeze.setJointMat(mesh.getAxisNeutral());
    //}

    for (size_t i_par = 0; i_par < mesh.getPart_size(); i_par++)
    {
        dshFreeze.setTextures( mesh.getId_diffuseMap(i_par), mesh.getId_normalMap(i_par), mesh.getId_specularMap(i_par) );
        mesh.draw(i_par);
    }
    mesh.end_draw_part();

    dshFreeze.setBones(false);

    // --------------------------
    // >> les objets d'equipement
    // --------------------------

    OMat4 model;

    for (size_t i_obj = 0; i_obj < ECO_sizeOf(attache); i_obj++)
    {
        if (attache[i_obj].pt_objet == nullptr) continue;

        OMesh_gltf& tomesh = attache[i_obj].pt_objet->getMesh();

        for (size_t i_par = 0; i_par < tomesh.getPart_size(); i_par++)
        {
            dshFreeze.setTextures( tomesh.getId_diffuseMap(i_par), tomesh.getId_normalMap(i_par), tomesh.getId_specularMap(i_par) );

            model = m_model;
            model *= mesh.getAxisAttach(attache[i_obj].ref_axe);
            // decalage selon mes repères
            model.translate(attache[i_obj].decal_pos);
            model.rotate(attache[i_obj].decal_rot.y, 0,1,0);
            model.rotate(attache[i_obj].decal_rot.z, 0,0,1);
            model.rotate(attache[i_obj].decal_rot.x, 1,0,0);

            dshFreeze.setModel(model);

            tomesh.draw(i_par);
        }
        tomesh.end_draw_part();
    }
}


void Tperso::_draw_ko(ODshGeo& dshGeo)
{
    OMat4 model;

    // update
    m_state_rot += 2.0f;

    glEnable(GL_CULL_FACE);

    dshGeo.enable();
        model.loadIdentity();   // attention aux différentes transformations
        model.translate(m_pos); // pos perso
        model.translate(0.0f, 0.f, 2.2f);
        model.rotate(m_state_rot, 0,0,1);

        dshGeo.setModel(model);

    for (size_t part = 0; part < mesh_ko.getPart_size(); part++)
    {
        dshGeo.setTextures( mesh_ko.getId_diffuseMap(part), mesh_ko.getId_normalMap(part), mesh_ko.getId_specularMap(part));
        mesh_ko.draw(part);
    }
    mesh_ko.end_draw_part();
}

void Tperso::draw_emissive(OShGlow& shGlow)
{
    // parce que uniquement skeleton, sinon les variables statiques s'appliquent à tous les personnages
    static float val = 0.0f;
    val += 0.025f;

    // activer > glDepthFunc(GL_EQUAL); finir par glDepthFunc(GL_LESS);
    shGlow.enable();
        shGlow.setModel(m_model);

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

    shGlow.setBones(false);

    if (mesh.anim_running())
    {
        //std::vector<OMat4> axis;
        //mesh.anim_getAxis(axis); // fait dans draw initial > m_anim_axis

        shGlow.setBones(true);
        shGlow.setJointMat(m_anim_axis);
    }
    else if (mesh.getAnimation_size() > 0)
    {
        shGlow.setBones(true);
        shGlow.setJointMat(mesh.getAxisNeutral());
    }

    for (size_t i_par = 0; i_par < mesh.getPart_size(); i_par++)
    {
        // color rgb et a = energie
        if (m_emissive[i_par].value > 0.0f)
        {
            shGlow.setTexture( mesh.getId_emissiveMap(i_par) );
            shGlow.setEnergy( abs(sin(val)) );
            mesh.draw(i_par);
            shGlow.setEnergy( 1.0f );
        }
    }
    mesh.end_draw_part();

    shGlow.setBones(false);
}





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

// pour recuperer la liste des animations
OMesh_gltf& Tperso::getMesh()
{
    return mesh;
}

OMat4& Tperso::getModel()
{
    return m_model;
}

bool Tperso::getSelect()
{
    return m_select;
}





bool Tperso::setEquipement(Tobjet& refObjet)
{
    return setEquipement(&refObjet);
}

bool Tperso::setEquipement(Tobjet* ptObjet)
{
    int emplacement = -1;

    switch(ptObjet->getType())
    {
    case OBJ_CASQUE:
        emplacement = EMP_TETE;
        break;
    case OBJ_ARME:
    case OBJ_ARME_TIR:
        emplacement = EMP_MAIN_D;
        break;
    case OBJ_BOUCLIER:
        emplacement = EMP_BRAS_G;
        break;
    default:
        // erreur
        return false;
    }
    if (emplacement < EMP_TETE || emplacement >= EMP_CEINTURE) return false;

    // trouver la reference des decalages du perso
    int ref = 0; // base >null
    for (size_t i = 0; i < ECO_sizeOf(gl_equi_decalage); i++)
    {
        if (gl_equi_decalage[i].code == m_code)
        {
            ref = i;
            break;
        }
    }
    // ref_pos = tete / +1 bras_gauche / +2 main_droite
    //if (emplacement == EMP_TETE)
    if (emplacement == EMP_BRAS_G) ref += 1;
    else if (emplacement == EMP_MAIN_D) ref += 2;

    // emplacement dejà occupé =>
    if (attache[emplacement].pt_objet != nullptr)
    {
        // retour dans le sac d'inventaire
        //attache[emplacement].pt_objet->setConteneur(m_code);
        //attache[emplacement].pt_objet->setEmplacement(EMP_SAC);

        // retour au sol pour visualisation
        attache[emplacement].pt_objet->setConteneur(CONTENEUR_NULL);

        attache[emplacement].pt_objet = nullptr;
    }

    // --- attribution de l'objet/equipement au perso

    attache[emplacement].pt_objet = ptObjet;
    attache[emplacement].pt_objet->setConteneur(m_code);

    // --- attribution de son decalage d'axe

    // tete bras_gauche main_droite
    attache[emplacement].ref_axe = gl_equi_decalage[ref].ref_joint;
    attache[emplacement].decal_pos = gl_equi_decalage[ref].pos_decal;
    attache[emplacement].decal_rot = gl_equi_decalage[ref].rot_decal;
    // regler l'origine en 0,0,0 (point orangé dans blender + definir l'origine = 3d curseur (posé au centre !))

    return true;
}

void Tperso::setEquipement_no(int emplacement)
{
    if (emplacement < EMP_TETE || emplacement >= EMP_CEINTURE) return;

    if (attache[emplacement].pt_objet == nullptr) return;

    //ici on jete l'objet
    attache[emplacement].pt_objet->setConteneur(CONTENEUR_NULL);

    // retour dans le sac d'inventaire
    //attache[emplacement].pt_objet->setConteneur(m_code);
    //attache[emplacement].pt_objet->setEmplacement(EMP_SAC);

    attache[emplacement].pt_objet = nullptr;

}

void Tperso::_setModel()
{
    m_model.loadIdentity();
    m_model.translate(m_pos);
        // rotation de construction (face au sud) vers trigo (face à l'est)
        m_model.rotate(90.0f, 0,0,1);
    m_model.rotate(m_rot.z, 0, 0, 1);
    m_model.rotate(m_rot.y, 0, 1, 0);
    m_model.rotate(m_rot.x, 1, 0, 0);

    // pour ma verticale z
    m_model.rotate(90, 1,0,0);
}

void Tperso::setSelect()
{
    m_select = !m_select;
}

void Tperso::setState(int value)
{
    switch(value)
    {
    case PER_STATE_FREEZE_UP:
        m_state_type = PER_STATE_FREEZE_UP;
        m_state_delay = 0.0f;
        break;
    case PER_STATE_KO:
        m_state_type = PER_STATE_KO;
        mesh.anim_start("2H_Melee_Idle", 9); // (animation proche) + bloquer les autres animations
        break;
    case PER_STATE_FIRE:
        m_state_type = PER_STATE_FIRE;
        break;

    default:
        m_state_type = PER_STATE_NULL;
        break;
    }
}





