//
// Created by jovian on 18/07/17.
//

#include <iostream>
#include "Soldier.h"

Soldier::Soldier(Faction faction, Controller *ctrl, TinyWorld *tinyWorld,
                 unsigned int imgId, unsigned int camp, unsigned int life)
        : Entity(faction, imgId, tinyWorld),
          m_ctrl(ctrl), m_camp(camp), m_life(life), m_tinyWorld(tinyWorld), m_homeFixture(nullptr),
          m_forceXScale(22.0f), m_forceYScale(5.0f), m_jumpVec(0.0f, -500.0f), m_jumpVelocityLimit(-0.5f),
          m_cool(0), m_coolCeil(10) {
    // Set the correct image
    if (imgId == 0) {
        if (m_camp == 0)
            m_imgId = 1;
        else if (m_camp == 1)
            m_imgId = 13;
    }

}

void Soldier::shoot(const b2Vec2 &dir) {
    m_cool = 0;
    m_tinyWorld->addEntity(new Bullet(m_body->GetPosition(), m_tinyWorld, dir, m_camp));
}

void Soldier::update() {
    // Life exist
    if (m_life == 0)
        m_exist = false;

    // A soldier fell out the world
    if (m_body->GetPosition().y > 100.0f)
        m_exist = false;

    // Firing
    m_cool++;
    if (m_ctrl->isFiring() && m_cool > m_coolCeil)
        shoot(m_ctrl->getVisor());

    // todo Shield activation

    // Movement
    b2Vec2 move(m_ctrl->getMove());
    move.x *= m_forceXScale;
    move.y *= m_forceYScale;
    m_body->ApplyForceToCenter(move, true);

    // Jumping
    if (isTouching()
        && m_ctrl->isJumping()
        && m_body->GetLinearVelocity().y > m_jumpVelocityLimit) {

        m_body->ApplyForceToCenter(m_jumpVec, true);
    }

    // Damage taken by bullet
    for (b2ContactEdge *ce = m_body->GetContactList(); ce; ce = ce->next) {
        b2Contact *c = ce->contact;

        // Get incoming fixture
        b2Fixture *incomingFixture(c->GetFixtureA());

        if (incomingFixture == m_homeFixture)
            incomingFixture = c->GetFixtureB();

        // Detect bullet presence
        Entity *incomingEntity((Entity *) incomingFixture->GetBody()->GetUserData().pointer);

        if (incomingEntity->getFaction() == BULLET) {
            // Damage
            Bullet *incomingBullet((Bullet*)incomingEntity);
            int damage(incomingBullet->getDamage());

            if (m_life < damage)
                m_life = 0;
            else
                m_life -= damage;

            // Bullet destroyed
            incomingEntity->setExistence(false);
        }
    }
}

void Soldier::createPhysicalShape(b2Vec2 spawn) {
    // Already exists ?
    if (m_body != nullptr) {
        std::cout << "Soldier::createPhysicalShape() > Body non null pointer. Creation aborted." << std::endl;
        return;
    }

    // Creation of physical body
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position = spawn;
    bodyDef.fixedRotation = true;
    m_body = m_tinyWorld->CreateBody(&bodyDef);

    // Creation of super shape
    b2Vec2 vertices[5];
    vertices[0].Set(0.0f, 40.0f / DEFAULT_ZOOM);
    vertices[1].Set(20.0f / DEFAULT_ZOOM, -2.0f / DEFAULT_ZOOM);
    vertices[2].Set(20.0f / DEFAULT_ZOOM, -38.0f / DEFAULT_ZOOM);
    vertices[3].Set(-20.0f / DEFAULT_ZOOM, -38.0f / DEFAULT_ZOOM);
    vertices[4].Set(-20.0f / DEFAULT_ZOOM, -2.0f / DEFAULT_ZOOM);

    b2PolygonShape allyShape;
    allyShape.Set(vertices, 5);

    // Definition of fixture
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &allyShape;
    fixtureDef.density = 6.0f;
    fixtureDef.friction = 0.3f;

    // Fixture filtering
    if (m_camp == 0) {
        fixtureDef.filter.categoryBits = 0b0001;
        fixtureDef.filter.maskBits = 0b1101;
    } else if (m_camp == 1) {
        fixtureDef.filter.categoryBits = 0b0100;
        fixtureDef.filter.maskBits = 0b0111;
    }

    // Creation of fixture
    m_homeFixture = m_body->CreateFixture(&fixtureDef);

    // Link this soldier
    establishPhysicalLink();
}