#include "potator.h"
#include "scene/playercharacternode.h"
#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>
#include "scene/scenetree.h"
#include "SparrowRenderer/parametricmesh.h"
#include "SparrowRenderer/pbrmaterial.h"
#include "scene/meshnode.h"
#include "tools/loader.h"
#include "SparrowRenderer/texture.h"
#include "scene/graphicalcontainernode.h"

#define PHYSICS_OFFSET 0.01f

Potator::Potator(PlayerCharacterNode * player,
        int cube_action,
        int sphere_action,
        int object_action) :
    m_player(player),
    m_throwCubeAction(cube_action),
    m_throwSphereAction(sphere_action),
    m_throwObjectAction(object_action)
{
    glm::vec3 cubeDim(1);
    float sphereRadius = 0.5f;
    glm::vec3 s = cubeDim*0.5f;
    float density = 1.f;

    // creating cube
    m_cubeMesh = new Mesh("woodBox");
    m_cubeMesh->addVertex(glm::vec3(-1*s.x, -1*s.y,  1*s.z),glm::vec3(0, 0,  1),glm::vec2(0, 0));
    m_cubeMesh->addVertex(glm::vec3(-1*s.x,  1*s.y,  1*s.z),glm::vec3(0, 0,  1),glm::vec2(0, 1));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x, -1*s.y,  1*s.z),glm::vec3(0, 0,  1),glm::vec2(1, 0));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x,  1*s.y,  1*s.z),glm::vec3(0, 0,  1),glm::vec2(1, 1));

    m_cubeMesh->addVertex(glm::vec3(-1*s.x, -1*s.y, -1*s.z),glm::vec3(0, 0, -1),glm::vec2(1, 0));
    m_cubeMesh->addVertex(glm::vec3(-1*s.x,  1*s.y, -1*s.z),glm::vec3(0, 0, -1),glm::vec2(1, 1));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x, -1*s.y, -1*s.z),glm::vec3(0, 0, -1),glm::vec2(0, 0));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x,  1*s.y, -1*s.z),glm::vec3(0, 0, -1),glm::vec2(0, 1));

    m_cubeMesh->addVertex(glm::vec3(-1*s.x,  1*s.y, -1*s.z),glm::vec3(0,  1, 0),glm::vec2(0, 0));
    m_cubeMesh->addVertex(glm::vec3(-1*s.x,  1*s.y,  1*s.z),glm::vec3(0,  1, 0),glm::vec2(0, 1));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x,  1*s.y, -1*s.z),glm::vec3(0,  1, 0),glm::vec2(1, 0));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x,  1*s.y,  1*s.z),glm::vec3(0,  1, 0),glm::vec2(1, 1));

    m_cubeMesh->addVertex(glm::vec3(-1*s.x, -1*s.y, -1*s.z),glm::vec3(0, -1, 0),glm::vec2(1, 0));
    m_cubeMesh->addVertex(glm::vec3(-1*s.x, -1*s.y,  1*s.z),glm::vec3(0, -1, 0),glm::vec2(1, 1));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x, -1*s.y, -1*s.z),glm::vec3(0, -1, 0),glm::vec2(0, 0));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x, -1*s.y,  1*s.z),glm::vec3(0, -1, 0),glm::vec2(0, 1));

    m_cubeMesh->addVertex(glm::vec3( 1*s.x, -1*s.y,  1*s.z),glm::vec3( 1, 0, 0),glm::vec2(0, 0));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x,  1*s.y,  1*s.z),glm::vec3( 1, 0, 0),glm::vec2(0, 1));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x, -1*s.y, -1*s.z),glm::vec3( 1, 0, 0),glm::vec2(1, 0));
    m_cubeMesh->addVertex(glm::vec3( 1*s.x,  1*s.y, -1*s.z),glm::vec3( 1, 0, 0),glm::vec2(1, 1));

    m_cubeMesh->addVertex(glm::vec3(-1*s.x, -1*s.y,  1*s.z),glm::vec3(-1, 0, 0),glm::vec2(1, 0));
    m_cubeMesh->addVertex(glm::vec3(-1*s.x,  1*s.y,  1*s.z),glm::vec3(-1, 0, 0),glm::vec2(1, 1));
    m_cubeMesh->addVertex(glm::vec3(-1*s.x, -1*s.y, -1*s.z),glm::vec3(-1, 0, 0),glm::vec2(0, 0));
    m_cubeMesh->addVertex(glm::vec3(-1*s.x,  1*s.y, -1*s.z),glm::vec3(-1, 0, 0),glm::vec2(0, 1));

    for(int id=0; id<24; id+=8)
    {
        m_cubeMesh->addTriangle(id+0, id+1, id+2);
        m_cubeMesh->addTriangle(id+2, id+1, id+3);
        m_cubeMesh->addTriangle(id+6, id+5, id+4);
        m_cubeMesh->addTriangle(id+7, id+5, id+6);
    }

    PBRMaterial *mat = new PBRMaterial();
    Image* img = Loader::loadImage("woodframe_albedo.png", 24);
    mat->setTexture(PBRMaterial::ALBEDO_SLOT, new Texture(img));
    img = Loader::loadImage("woodframe_metallic.png", 8);
    mat->setTexture(PBRMaterial::METALLIC_SLOT, new Texture(img));
    img = Loader::loadImage("woodframe_roughness.png", 8);
    mat->setTexture(PBRMaterial::ROUGHNESS_SLOT, new Texture(img));
    img = Loader::loadImage("woodframe_normal.png", 24);
    mat->setTexture(PBRMaterial::NORMALS_SLOT, new Texture(img));

    m_cubeMesh->setMaterial(mat);
    m_cubeMesh->computeTangents();
    m_cubeMesh->initGL();

    btVector3 halfExtents(s.x+PHYSICS_OFFSET, s.y+PHYSICS_OFFSET, s.z+PHYSICS_OFFSET);
    m_cubeShape = new btBoxShape(halfExtents);

    m_cubeMass = cubeDim.x*cubeDim.y*cubeDim.z*density;

    // creating sphere
    mat = new PBRMaterial();
    img = Loader::loadImage("slipperystonework_albedo.png", 24);
    mat->setTexture(PBRMaterial::ALBEDO_SLOT, new Texture(img));
    img = Loader::loadImage("slipperystonework_metallic.png", 8);
    mat->setTexture(PBRMaterial::METALLIC_SLOT, new Texture(img));
    img = Loader::loadImage("slipperystonework_roughness.png", 8);
    mat->setTexture(PBRMaterial::ROUGHNESS_SLOT, new Texture(img));
    img = Loader::loadImage("slipperystonework_normal.png", 24);
    mat->setTexture(PBRMaterial::NORMALS_SLOT, new Texture(img));

    SphereGenerator sphereGen;
    m_sphereMesh = sphereGen.generateParametricMesh(mat, 20, 20, sphereRadius);
    m_sphereMesh->computeNormals();
    m_sphereMesh->computeTangents();
    m_sphereMesh->mergeVertices();
    m_sphereMesh->initGL();

    m_sphereShape = new btSphereShape(sphereRadius+PHYSICS_OFFSET);

    float sphereVolume = 4.18879020479f*(sphereRadius*sphereRadius*sphereRadius); // (4*pi)/3 = 4.18879020479
    m_sphereMass = sphereVolume*density;

    // creating sword :
    m_swordMeshes = Loader::loadMesh("sword.obj");
    for(Mesh* m : m_swordMeshes)
        m->initGL();

    btCompoundShape* swordShape = new btCompoundShape();

    btVector3 guardBox = btVector3(0.03+PHYSICS_OFFSET, 0.04+PHYSICS_OFFSET, 0.25+PHYSICS_OFFSET);
    swordShape->addChildShape(btTransform::getIdentity(), new btBoxShape(guardBox));

    btTransform bladeTransform = btTransform::getIdentity();
    bladeTransform.setOrigin(btVector3(0.f, 0.486705f, 0.f));
    swordShape->addChildShape(bladeTransform, new btConeShape(0.07+PHYSICS_OFFSET, 2.15+PHYSICS_OFFSET));

    m_swordShape = swordShape;
    m_swordMass = 0.5f;
}

void Potator::throwCube()
{
    glm::vec3 pos(0, 10, 0);
    glm::vec3 dir(0, 1, 0);
    if(m_player != nullptr)
    {
        dir = m_player->getDirection();
        pos = m_player->getEyePosition() + dir*2.f;
    }
    float throwForce = 5.f;

    createGib(new MeshNode(m_cubeMesh), m_cubeShape, m_cubeMass, pos, dir*throwForce, 30000);
}

void Potator::throwSphere()
{
    glm::vec3 pos(0, 10, 0);
    glm::vec3 dir(0, 1, 0);
    if(m_player != nullptr)
    {
        dir = m_player->getDirection();
        pos = m_player->getEyePosition() + dir*2.f;
    }
    float throwForce = 5.f;

    createGib(new MeshNode(m_sphereMesh), m_sphereShape, m_sphereMass, pos, dir*throwForce, 30000);
}

void Potator::throwSword()
{
    glm::vec3 pos(0, 10, 0);
    glm::vec3 dir(0, 1, 0);
    if(m_player != nullptr)
    {
        dir = m_player->getDirection();
        pos = m_player->getEyePosition() + dir*2.f;
    }
    float throwForce = 5.f;

    GraphicalContainerNode *node = new GraphicalContainerNode();
    for(Mesh * m : m_swordMeshes)
        node->addChild(new MeshNode(m));
    createGib(node, m_swordShape, m_swordMass, pos, dir*throwForce, 30000);
}

void Potator::update()
{
    GibGeneratorNode::update();

    if(getEngine().isMouseGrabbed())
    {
        Input *input = getEngine().getInput();
        for(Action action : input->getActions())
        {
            if(action.action == m_throwCubeAction)
                throwCube();
            else if(action.action == m_throwSphereAction)
                throwSphere();
            else if(action.action == m_throwObjectAction)
                throwSword();
        }
    }
}
