#include <SDL_image.h>
#include <math.h>
#include "Projectile.h"
#include "Player.h"

void Projectile::init(SDL_Surface* image, uint collWidth, uint collHeight, uint frameWidth, uint frameHeight, uint numFrames, uint animationSpeed, Sound* sound) {

    chart                 = image;
    this->sound           = sound;
    this->numFrames       = numFrames;
    this->animationSpeed  = animationSpeed;
    this->spriteDeathList = spriteDeathList;
    setCollSize(collWidth, collHeight);
    setFrameSize(frameWidth, frameHeight);
}

// Note : if projectile did not come from player, then pass NULL to player parameter
void Projectile::reset(Player* player, PVector<int> startPosition, PVector<double> startVelocity, bool useGravity, bool playerPickup, bool reverseAnimation) {

    this->player            = player;
    this->useGravity        = useGravity;
    this->playerPickup      = playerPickup;
    this->reverseAnimation  = reverseAnimation;
    velocity                = startVelocity;
    position                = (PVector<double>)startPosition;
    origY                   = startPosition.y;
    health                  = 1;
    inAir                   = true;
    active                  = true;
    frame                   = 0;
    animationCount          = 0.0;
    wavyCount               = PROJ_WAVY_FREQ * 3.14159265;        // start wavyness at middle inflection point in sine wave

    // lower velocity slightly to account for wavyness(drunkeness)
    if (player != NULL)
        velocity.x *= 1.0 - player->getDrunkeness() / PROJ_WAVY_SPEED_SCALE;
}

// this is called first by other two update functions
void Projectile::update(double delta) {

    if (!active)
        return;

    // update position
    if (useGravity)
        velocity.y += DAY_GRAVITY * delta;
    position += velocity * delta;

    // do animation
    if ( (animationCount += delta ) >= animationSpeed ) {
        animationCount = 0.0;

        if ( !reverseAnimation && ++frame >= (int)numFrames )
            frame = 0;
        else if ( reverseAnimation && --frame < 0)
            frame = numFrames - 1;
    }
}

void Projectile::checkCollidables(std::vector<Collidable>& collArray) {

    Collidable box = getCollBox();

    for (uint i = 0; i < collArray.size(); i++) {
        Collidable c(collArray[i]);

        if (!collArray[i].isLedge && checkBoxToBox(box, c)) {
            if (!playerPickup)
                hurt(NULL, HURT_BY_COLL);
            else
                inAir = false;
            sound->playBottleSmashEffect();
            break;
        }
    }
}

// this is for projectiles from player
void Projectile::updateFromPlayer(double delta, std::vector<Collidable>& collArray, std::vector<Sprite*>& enemyArray) {

    if (!active)
        return;

    // vary y position with player drunkeness
    wavyCount += delta;
    position.y = origY + sin(wavyCount / PROJ_WAVY_FREQ) * player->getDrunkeness() * PROJ_WAVY_AMP;

    // update position, check for collision with collidables
    update(delta);
    checkCollidables(collArray);

    // check for collision with enemies
    Collidable c = getCollBox();

    for (uint i = 0; i < enemyArray.size(); i++) {
        if (!enemyArray[i]->isActive())            // dont check dead enemies
            continue;

        // apply a small trim to enemy bounding boxes so that we can control the "tightness" of all enemy collisions
        Collidable e = enemyArray[i]->getCollBox();
        e.trim(ALL_ENEMY_TRIM);

        if (checkBoxToBox(c, e)) {
            enemyArray[i]->hurt(this, HURT_BY_PROJ);
            hurt(NULL, HURT_BY_ENEMY);
            sound->playBarKeepKickEffect();
            active = false;
            return;
        }
    }

}

// this is for projectiles from enemies
void Projectile::updateFromEnemy(double delta, std::vector<Collidable>& collArray, Player* player) {

    if (!active)
        return;

    if (inAir)
        update(delta);

    // check for collision with player
    Collidable c = getCollBox();

    if (checkBoxToBox( c, player->getCollBox() )) {

        if ( !playerPickup || inAir ) {
            player->hurt(this, HURT_BY_PROJ);
            hurt(player, HURT_BY_PLAYER);
        }
        else {
            player->addToAmmoCount(BOSS_PROJ_AMMO);
            sound->playBeerEffect();
        }
        active = false;
    }

    if (inAir)
        checkCollidables(collArray);
}

void Projectile::render(SDL_Surface* screen, int cameraX, SDL_Rect* clip) {

    if (!active)
        return;

    // check for going off-screen (must be done in render() so we can check against camera location)
    if (  (!playerPickup && (position.x + collWidth < cameraX || position.x > cameraX + SCREEN_WIDTH)
          || position.y + collHeight < 0 || position.y > SCREEN_HEIGHT)) {
        active = false;
        return;
    }

    // find clip for current frame
    SDL_Rect c = { frame * frameWidth, 0, frameWidth, frameHeight };
    Sprite::render(screen, cameraX, &c);
}