//
// Copyright(C) 2014-2015 Samuel Villarreal
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//      Riding Gun Logic
//

const float RIDING_GUN_MIN_SPEED                        = 0.5f;
const float RIDING_GUN_TIME_TO_REFIRE                   = 0.7f;
const float RIDING_GUN_STOMP_TIME                       = 100.0f;
const int   RIDING_GUN_MACHINE_GUN_REFIRE_ITERATION     = ((1 << 4) - 1);
const kVec3 RIDING_GUN_POSITION                         = kVec3(40.0f, -80.0f, -150.0f);

/*
==============================================================
TurokRidingGun
==============================================================
*/

final class TurokRidingGun : TurokWeapon
{   
    int m_nWeaponSide;
    float m_fStompTime;
    
    TurokRidingGun(kWeapon @actor)
    {
        super(actor);
        m_nWeaponSide = 1;
        m_fStompTime = 0;
    }
    
    /*
    ==============================================================
    OnBeginFire
    ==============================================================
    */
    
    void OnBeginFire(void)
    {
        if(m_nWeaponSide > 0)
        {
            self.AnimTrackComponent().Blend(ANIM_WPN_FIRE_ARTILLERY_LEFT, 0, 8, 0);
        }
        else
        {
            self.AnimTrackComponent().Blend(ANIM_WPN_FIRE_ARTILLERY_RIGHT, 0, 8, 0);
        }
        
        m_nWeaponSide = -m_nWeaponSide;
    }
    
    /*
    ==============================================================
    ShakeScreen
    ==============================================================
    */
    
    void ShakeScreen(void)
    {
        kVec3 vVel = self.Owner().Actor().MovementComponent().Velocity();
        float vx = Math::Fabs(vVel.x);
        float vy = Math::Fabs(vVel.y);
        float fSpeed = Math::Sqrt(vx*vx+vy*vy);
        
        m_fStompTime += fSpeed;
        
        if(m_fStompTime > RIDING_GUN_STOMP_TIME)
        {
            m_fStompTime -= RIDING_GUN_STOMP_TIME;
            
            // shake the screen
            float fShake = fSpeed * 50.0f;
            if(fShake > 50.0f)
            {
                fShake = 50.0f;
            }
            
            SpawnShake(kVec3(0, 0, fShake), self.Owner().Actor().Origin(), 10000, true);
        }
    }
    
    /*
    ==============================================================
    CheckChargeForward
    ==============================================================
    */
    
    bool CheckChargeForward(kActor@ pActor)
    {
        if(pActor.AnimTrackComponent().PlayingID() == ANIM_GROUND_ATTACK_COMBAT1 &&
            !pActor.AnimTrackComponent().CycleCompleted())
        {
            float sy = Math::Sin(self.Owner().Actor().Yaw());
            float cy = Math::Cos(self.Owner().Actor().Yaw());
            
            kVec3 vForward(sy, cy, 0.0f);
            vForward.Normalize();
            
            float fScale = 1.0f - pActor.AnimTrackComponent().TrackTime();
            
            ShakeScreen();
            
            self.Owner().Actor().MovementComponent().Velocity().x = vForward.x * (GAME_SCALE*fScale);
            self.Owner().Actor().MovementComponent().Velocity().y = vForward.y * (GAME_SCALE*fScale);
            return true;
        }
        
        return false;
    }
    
    /*
    ==============================================================
    DoMovementAnimations
    ==============================================================
    */
    
    void DoMovementAnimations(kActor@ pActor)
    {
        kVec3 vVel = self.Owner().Actor().MovementComponent().Velocity();
        float vx = Math::Fabs(vVel.x);
        float vy = Math::Fabs(vVel.y);
        
        if(vx >= RIDING_GUN_MIN_SPEED || vy >= RIDING_GUN_MIN_SPEED)
        {
            if(pActor.AnimTrackComponent().PlayingID() != ANIM_GROUND_MOVE1)
            {
                pActor.AnimTrackComponent().Blend(ANIM_GROUND_MOVE1, 0, 32, ANF_LOOP);
            }
            else
            {
                ShakeScreen();
            }
        }
        else if(pActor.AnimTrackComponent().PlayingID() != ANIM_GROUND_IDLE1)
        {
            pActor.AnimTrackComponent().Blend(ANIM_GROUND_IDLE1, 0, 32, ANF_LOOP);
        }
    }
    
    /*
    ==============================================================
    CheckChargeAttack
    ==============================================================
    */
    
    bool CheckChargeAttack(kActor@ pActor)
    {
        if((self.Owner().Buttons() & BC_ALTFIRE) != 0)
        {
            pActor.AnimTrackComponent().Blend(ANIM_GROUND_ATTACK_COMBAT1, 2, 8, 0);
            return true;
        }
        
        return false;
    }
    
    /*
    ==============================================================
    UpdateArtilleryAnimations
    ==============================================================
    */
    
    void UpdateArtilleryAnimations(void)
    {
        int animID = self.AnimTrackComponent().PlayingID();
        
        if(animID == ANIM_WPN_FIRE_ARTILLERY_LEFT || animID == ANIM_WPN_FIRE_ARTILLERY_RIGHT)
        {
            if(self.AnimTrackComponent().TrackTime() >= RIDING_GUN_TIME_TO_REFIRE)
            {
                self.AnimTrackComponent().Flags() |= ANF_CYCLECOMPLETED;
            }
        }
        
        if((self.Owner().Buttons() & BC_JUMP) != 0)
        {
            if((self.GameTicks() & RIDING_GUN_MACHINE_GUN_REFIRE_ITERATION) == 0)
            {
                self.AnimTrackComponent().Blend(ANIM_WPN_FIRE_MACHINE_GUN, 0, 8, 0);
            }
        }
    }
    
    /*
    ==============================================================
    OnTick
    ==============================================================
    */
    
    void OnTick(void)
    {
        kActor@ pActor = self.GetTarget();
        
        if(!(pActor is null))
        {
            float zoffset = Math::Sin(Math::Min(self.Owner().Actor().Pitch(), 0.0f)) * (GAME_SCALE*7.5f);
            
            pActor.Yaw() = self.Yaw();
            pActor.Origin().z = RIDING_GUN_POSITION.z + zoffset;
            
            if(CheckChargeForward(pActor))
            {
                return;
            }
            
            if(CheckChargeAttack(pActor))
            {
                return;
            }
            
            DoMovementAnimations(pActor);
        }
        
        UpdateArtilleryAnimations();
    }
    
    /*
    ==============================================================
    OnRaise
    ==============================================================
    */
    
    void OnRaise(void)
    {
        kActor@ pTriceratop =
        ActorFactory.Spawn(
            kActor_Monster_Triceratop_Head,
            RIDING_GUN_POSITION,
            0.0f,
            0.0f,
            0.0f);
            
        self.SetTarget(pTriceratop);
    }
    
    /*
    ==============================================================
    OnLower
    ==============================================================
    */
    
    void OnLower(void)
    {
        kActor@ pActor = self.GetTarget();
        if(pActor is null)
        {
            return;
        }
        
        pActor.Remove();
        
        @pActor = null;
        self.SetTarget(pActor);
    }
}
