/**
 @file Q2Character.cpp

 Dojo Game Engine

 Copyright 2005, Morgan McGuire
 All rights reserved.
 */

#include "dojo/Q2Character.h"

namespace dojo {

EntityRef Q2Character::create(
    const std::string& name,
    MD2ModelRef& model0,
    TextureRef&  tex0,
    MD2ModelRef& model1,
    TextureRef&  tex1) {

    Q2Character* c = new Q2Character(name);
    c->addGraphicsGeometry("Root", model0, tex0, model1, tex1);
    
    // Total height
    float levitateHeight = 0.1f;
    float height = 1.8f - levitateHeight;
    float rad = 0.3f;

    float mass = 60;

    // Take the caps into account
    float y = height / 2 - rad;

    Capsule capsule(
        Vector3(0, -y + levitateHeight, 0), 
        Vector3(0, y + levitateHeight, 0), 
        rad);

    c->addPhysicsGeometry(
        "Root",
        new CapsuleShape(capsule),
        mass / capsule.volume());

    return EntityRef(c);
}


void Q2Character::onSimulation(SimTime dt) {
    applyBalanceTorque(dt);

    // Height of steps that the character can stand on without sliding down.
    // Can go up steeper slopes thanks to rounded capsule bottom.
    float lHeight = (1.8f - 0.1f) / 2;
    applyLevitateForce(dt, lHeight);

    {
        // Apply (limited) torque to stop any spinning that is occuring; this is
        // a friction-like force.
        Vector3 T = angularVelocity("Root");

        // Project onto vertical axis
        float L = T.dot(Vector3::unitY());

        if (abs(L) > 0.000001f) {
            const float maxVel = 5;
            float mag = G3D::min(abs(L), maxVel) * G3D::sign(L) * mass() * 0.01f / dt;
            applyTorque("Root", -Vector3::unitY() * mag);
        }
    }

    {
        // Apply (limited) force to stop any sliding that is occuring; this is
        // a friction-like force.
        Vector3 F = velocity("Root");
        // Remove vertical component
        F.y = 0;
        if (F.squaredMagnitude() > 0.00001f) {
            float L = F.magnitude();
            F /= L;

            const float maxVel = 10;
            float mag = G3D::min(L, maxVel) * mass() * 0.1f / dt;

            applyForce("Root", -F * mag);
        }
    }

    // Animate
    {
        Part* root = part("Root");
        GraphicsData* data = root->graphicsData();
        // GameTime deltaTime, 
        // bool crouching, 
        // bool movingForward, 
        // bool movingBackward, 
        // bool attack, 
        // bool jump, 
        // bool flip, 
        // bool salute, 
        // bool fallback, 
        // bool wave, 
        // bool point, 
        // bool death1, 
        // bool death2, 
        // bool death3, 
        // bool pain1, 
        // bool pain2, 
        // bool pain3)
        data->md2pose.doSimulation(
            dt,
            m_crouching,
            m_movingForward,
            m_movingBackward, 
            m_attacking, 
            false, false, false, false, false, false, 
            false, false, false, false, false, false);
    }

    m_crouching = false;
    m_movingForward = false;
    m_movingBackward = false; 
    m_attacking = false;
}


void Q2Character::move(const Vector2& mv) {
    if (mv.y > 0.01) {
        m_movingForward = true;
    } else if (mv.y < -0.01f) {
        m_movingBackward = true;
    }

    // TODO: Seek a desired velocity instead of applying arbitrary force and depending on
    // friction.
    applyForce("Root", frame().lookVector() * mv.y * 50 * mass());
    applyTorque("Root", 3 * Vector3::unitY() * -mv.x * mass());
}

}
