using System;
using GTA;
using GTA.Math;
using GTA.Native;

namespace GTAV_YF12Helper
{
    public class YF12Helper : Script
    {
        private YF12 mainVehicle;

        public YF12Helper()
        {
            mainVehicle = null;
            Tick += OnTick;
        }

        private void OnTick(object sender, EventArgs e)
        {
            var player = Game.Player.Character;

            if (mainVehicle != null && !player.IsInVehicle(mainVehicle.Vehicle))
            {
                mainVehicle.KillAfterburner();
                mainVehicle = default(YF12);
            }

            if (player.IsInVehicle() && player.CurrentVehicle.Model == (VehicleHash)0x45006B42)
            {
                if ((mainVehicle == null || mainVehicle.Handle != player.CurrentVehicle.Handle))
                {
                    mainVehicle = new YF12(player.CurrentVehicle);
                    mainVehicle.InitializeAfterburner();
                }

                else
                {
                    if (mainVehicle.Vehicle.IsDriveable)
                        mainVehicle.Update();
                }
            }
        }

        protected override void Dispose(bool A_0)
        {
            if (mainVehicle != null)
            mainVehicle.KillAfterburner();
            base.Dispose(A_0);
        }
    }

    public class YF12 : Entity
    {
        private readonly Vehicle _vehicle;

        private LoopedPTFX afterburnerL = new LoopedPTFX("core", "veh_exhaust_afterburner");
        private LoopedPTFX afterburnerR = new LoopedPTFX("core", "veh_exhaust_afterburner");

        public Vehicle Vehicle { get { return _vehicle; } }

        public YF12(int handle) : base(handle)
        {
            _vehicle = new Vehicle(handle);
        }

        public YF12(Vehicle vehicle) : base(vehicle.Handle)
        {
            _vehicle = vehicle;
        }

        public void InitializeAfterburner()
        {
            if (!afterburnerL.IsLoaded)
                afterburnerL.Load();

            afterburnerL.Start(_vehicle, 1.3f, new Vector3(-4.91f, -7.78f, 0.55f), Vector3.Zero, null);
            afterburnerR.Start(_vehicle, 1.3f, new Vector3(4.91f, -7.78f, 0.55f), Vector3.Zero, null);
            Function.Call((Hash)0xDCB194B85EF7B541, afterburnerL.Handle, 3000.0f);
            Function.Call((Hash)0xDCB194B85EF7B541, afterburnerR.Handle, 3000.0f);
        }

        public void KillAfterburner()
        {
            afterburnerL.Remove();
            afterburnerR.Remove();
        }

        public void Update()
        {
            Function.Call(Hash.SET_PARTICLE_FX_LOOPED_EVOLUTION, afterburnerL.Handle, "throttle", _vehicle.Acceleration, 0);
            Function.Call(Hash.SET_PARTICLE_FX_LOOPED_EVOLUTION, afterburnerR.Handle, "throttle", _vehicle.Acceleration, 0);
        }
    }

    public class LoopedPTFX
    {
        public int Handle { get; private set; }
        public string AssetName { get; private set; }
        public string FXName { get; private set; }

        public bool Exists { get { if (Handle == -1) return false; return Function.Call<bool>(Hash.DOES_PARTICLE_FX_LOOPED_EXIST, Handle); } }

        public bool IsLoaded { get { return Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, AssetName); } }

        public LoopedPTFX(string assetName, string fxName)
        {
            Handle = -1;
            AssetName = assetName;
            FXName = fxName;
        }

        public void Load()
        {
            Function.Call(Hash.REQUEST_NAMED_PTFX_ASSET, AssetName);
            while (!Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED))
                Script.Wait(0);
        }

        public void Start(Entity entity, float scale, Vector3 offset, Vector3 rotation, Bone? bone)
        {
            if (Handle != -1) return;

            Function.Call(Hash._SET_PTFX_ASSET_NEXT_CALL, AssetName);

            Handle = bone == null ?
                Function.Call<int>(Hash.START_PARTICLE_FX_LOOPED_ON_ENTITY, FXName,
                entity, offset.X, offset.Y, offset.Z, rotation.X, rotation.Y, rotation.Z, scale, 0, 0, 1) :
                Function.Call<int>(Hash._START_PARTICLE_FX_LOOPED_ON_ENTITY_BONE, FXName,
                entity, offset.X, offset.Y, offset.Z, rotation.X, rotation.Y, rotation.Z, (int)bone, scale, 0, 0, 0);
        }

        public void Start(Entity entity, float scale)
        {
            Start(entity, scale, Vector3.Zero, Vector3.Zero, null);
        }

        public void Start(Vector3 position, float scale, Vector3 rotation)
        {
            if (Handle != -1) return;

            Function.Call(Hash._SET_PTFX_ASSET_NEXT_CALL, AssetName);

            Handle = Function.Call<int>(Hash.START_PARTICLE_FX_LOOPED_AT_COORD, FXName,
             position.X, position.Y, position.Z, rotation.X, rotation.Y, rotation.Z, scale, 0, 0, 0, 0);
        }

        public void Start(Vector3 position, float scale)
        {
            Start(position, scale, Vector3.Zero);
        }

        public void Remove()
        {
            if (Handle == -1) return;

            Function.Call(Hash.REMOVE_PARTICLE_FX, Handle, 0);
            Handle = -1;
        }

        public void Remove(Vector3 position, float radius)
        {
            if (Handle == -1) return;

            Function.Call(Hash.REMOVE_PARTICLE_FX_IN_RANGE, position.X, position.Y, position.Z, radius);
            Handle = -1;
        }

        public void Unload()
        {
            if (IsLoaded)
                Function.Call((Hash)0x5F61EBBE1A00F96D, AssetName);
        }
    }
}
