using System;
using System.Collections.Generic;
using System.IO;
using System.Media;
using GTA;
using GTA.Math;
using GTA.Native;

public class HauntedMine : Script
{
    private class FireTrap
    {
        public Vector3 Position;
        public DateTime NextAllowedTime;

        public FireTrap(Vector3 pos)
        {
            Position = pos;
            NextAllowedTime = DateTime.MinValue;
        }
    }

    private readonly Vector3 targetPosition = new Vector3(-487f, 1985f, 124f);
    private readonly float checkRadius = 129f;
    private bool wasInRange = false;
    private int baseMaxHealth = 200;

    private readonly string soundFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "HauntedSounds");
    private readonly string[] soundFiles = { "sound01.wav", "sound02.wav" };
    private DateTime nextSoundTime = DateTime.MinValue;

    private readonly List<FireTrap> fireTraps = new List<FireTrap>
    {
        new FireTrap(new Vector3(-542.67f, 1970.65f, 126.97f)),
        new FireTrap(new Vector3(-559.05f, 1889.38f, 123.06f)),
        new FireTrap(new Vector3(-487.94f, 1894.51f, 120.13f))
    };
    private readonly float fireRadius = 1.2f;
    private bool isPlayerOnFire = false;
    private DateTime fireEndTime = DateTime.MinValue;

    private readonly Vector3 propZonePos = new Vector3(-534.79f, 1923.17f, 124.27f);
    private readonly Vector3 propSpawnPos = new Vector3(-538.69f, 1905.81f, 122.70f);
    private readonly Model lanternModel = new Model("m24_2_prop_m42_lantern_01b");
    private readonly Model ghostModel = new Model("m23_1_prop_m31_ghostzombie_01a");
    private readonly float propZoneRadius = 1.0f;
    private Prop spawnedProp = null;
    private DateTime propDeleteTime = DateTime.MinValue;
    private DateTime nextPropAllowed = DateTime.MinValue;

    private readonly Vector3 filmNoirTrigger1 = new Vector3(-542.95f, 1902.80f, 123.09f);
    private readonly Vector3 filmNoirSpawn1 = new Vector3(-565.47f, 1886.02f, 122.60f);
    private readonly Vector3 filmNoirTarget1 = new Vector3(-556.11f, 1891.17f, 122.60f);
    private readonly float filmNoirHeading1 = 303f;

    private readonly Vector3 filmNoirTrigger2 = new Vector3(-499.86f, 1893.59f, 120.85f);
    private readonly Vector3 filmNoirSpawn2 = new Vector3(-481.09f, 1894.92f, 119.64f);
    private readonly Vector3 filmNoirTarget2 = new Vector3(-491.98f, 1894.38f, 120.29f);
    private readonly float filmNoirHeading2 = 91.37f;

    private Vector3 activeFilmTrigger;
    private Vector3 activeFilmSpawn;
    private Vector3 activeFilmTarget;
    private float activeFilmHeading;

    private readonly Model filmNoirModel = new Model("u_m_o_filmnoir");
    private readonly float filmNoirRadius = 2.0f;
    private Ped spawnedFilmNoir = null;
    private bool isFilmNoirRunning = false;
    private DateTime filmNoirSpawnTime = DateTime.MinValue; 
    private bool isFilmNoirSoundTriggered = false;
    private DateTime nextFilmNoirAllowed = DateTime.MinValue;

    private readonly Vector3 juggTriggerPos = new Vector3(-458.51f, 2001.05f, 123.54f);
    private readonly Vector3 juggSpawnPos = new Vector3(-448.38f, 2011.73f, 122.90f);
    private readonly Vector3 juggRunTarget = new Vector3(-455.24f, 2004.81f, 123.0f);
    private readonly Model juggModel = new Model("u_m_y_juggernaut_01");
    private readonly float juggTriggerRadius = 2.0f;
    private Ped spawnedJuggernaut = null;
    private bool isJuggernautFinished = false;
    private DateTime juggSpawnTime = DateTime.MinValue;
    private bool isJuggOnFireTriggered = false;

    private readonly Vector3 petrolTriggerPos = new Vector3(-454.60f, 2041.99f, 122.79f);
    private readonly float petrolTriggerRadius = 2.0f;
    private bool isPetrolAssetRequested = false;
    private bool isPetrolFireFinished = false;
    private int petrolPtfxHandle = 0;

    private readonly Vector3 skelTriggerPos1 = new Vector3(-470.79f, 2080.15f, 120.27f);
    private readonly Vector3 skelSpawnPos1 = new Vector3(-472.97f, 2090.60f, 119.60f);
    private readonly Vector3 skelTriggerPos2 = new Vector3(-431.98f, 2062.78f, 121.03f);
    private readonly Vector3 skelSpawnPos2 = new Vector3(-423.00f, 2064.89f, 120.00f);
    private Vector3 activeSkelTrigger; 
    private Vector3 activeSkelSpawn;   
    private readonly Model skelModel = new Model("ig_skeleton_01");
    private readonly float skelTriggerRadius = 2.0f;
    private Ped spawnedSkeleton = null;
    private bool isSkelEventActive = false;
    private bool isSkelOnFireTriggered = false;
    private DateTime skelSpawnTime = DateTime.MinValue;
    private DateTime skelEventEndTime = DateTime.MinValue;
    private DateTime nextSkelThrowTime = DateTime.MinValue;
    private DateTime nextSkelEventAllowed = DateTime.MinValue;
    private DateTime nextSkelDamageTime = DateTime.MinValue;

    private readonly HashSet<int> launchedBarrels = new HashSet<int>();
    private readonly Random random = new Random();
    private DateTime nextLaunchAllowed = DateTime.MinValue;

    public HauntedMine()
    {
        Tick += OnTick;
        Aborted += OnAborted;
        Interval = 5;
        RandomizeSkeletonLocation();
        RandomizeFilmNoirLocation();
    }

    private void RandomizeSkeletonLocation()
    {
        if (random.Next(2) == 0)
        {
            activeSkelTrigger = skelTriggerPos1;
            activeSkelSpawn = skelSpawnPos1;
        }
        else
        {
            activeSkelTrigger = skelTriggerPos2;
            activeSkelSpawn = skelSpawnPos2;
        }
    }

    private void RandomizeFilmNoirLocation()
    {
        if (random.Next(2) == 0)
        {
            activeFilmTrigger = filmNoirTrigger1;
            activeFilmSpawn = filmNoirSpawn1;
            activeFilmTarget = filmNoirTarget1;
            activeFilmHeading = filmNoirHeading1;
        }
        else
        {
            activeFilmTrigger = filmNoirTrigger2;
            activeFilmSpawn = filmNoirSpawn2;
            activeFilmTarget = filmNoirTarget2;
            activeFilmHeading = filmNoirHeading2;
        }
    }

    private void PlayRandomSound()
    {
        string fileName = soundFiles[random.Next(soundFiles.Length)];
        PlaySpecificSound(fileName);
    }

    private void PlaySpecificSound(string fileName)
    {
        try
        {
            string fullPath = Path.Combine(soundFolder, fileName);
            if (File.Exists(fullPath))
            {
                using (SoundPlayer player = new SoundPlayer(fullPath))
                {
                    player.Play();
                }
            }
        }
        catch { }
    }

    private void OnTick(object sender, EventArgs e)
    {
        Ped player = Game.Player.Character;
        if (player == null || !player.Exists()) return;

        float distance = player.Position.DistanceTo(targetPosition);

        if (distance <= checkRadius)
        {
            if (!wasInRange)
            {
                baseMaxHealth = player.MaxHealth;
                player.MaxHealth = 2000;
                player.Health = 2000;
                PlayRandomSound();
                nextSoundTime = DateTime.Now.AddMinutes(4);
                wasInRange = true;
            }

            if (DateTime.Now >= nextSoundTime)
            {
                PlayRandomSound();
                nextSoundTime = DateTime.Now.AddMinutes(4);
            }

            if (DateTime.Now >= nextLaunchAllowed)
            {
                Prop[] nearbyProps = World.GetNearbyProps(player.Position, 2f, new Model("prop_wooden_barrel"));
                foreach (Prop prop in nearbyProps)
                {
                    if (prop == null || !prop.Exists()) continue;
                    if (!launchedBarrels.Contains(prop.Handle))
                    {
                        launchedBarrels.Add(prop.Handle);
                        if (random.Next(3) == 0)
                        {
                            Vector3 launchDirection = (prop.Position - player.Position);
                            launchDirection.Z = 0;
                            launchDirection = (launchDirection.Normalized + Vector3.WorldUp).Normalized;
                            prop.ApplyForce(launchDirection * 40f);
                            nextLaunchAllowed = DateTime.Now.AddSeconds(30);
                            break;
                        }
                    }
                }
            }
        }
        else
        {
            if (wasInRange)
            {
                player.MaxHealth = baseMaxHealth;
                if (player.Health > baseMaxHealth) player.Health = baseMaxHealth;
                wasInRange = false;
                launchedBarrels.Clear();
                if (isSkelEventActive)
                {
                    if (spawnedSkeleton != null && spawnedSkeleton.Exists()) spawnedSkeleton.Delete();
                    spawnedSkeleton = null;
                    isSkelEventActive = false;
                    nextSkelEventAllowed = DateTime.Now.AddSeconds(80);
                    RandomizeSkeletonLocation();
                }
            }
        }

        if (!isPlayerOnFire)
        {
            foreach (var trap in fireTraps)
            {
                if (player.Position.DistanceTo(trap.Position) <= fireRadius && DateTime.Now >= trap.NextAllowedTime)
                {
                    Function.Call(Hash.START_ENTITY_FIRE, player);
                    isPlayerOnFire = true;
                    fireEndTime = DateTime.Now.AddSeconds(3);
                    trap.NextAllowedTime = DateTime.Now.AddSeconds(60); 
                    break;
                }
            }
        }
        else if (DateTime.Now >= fireEndTime)
        {
            Function.Call(Hash.STOP_ENTITY_FIRE, player);
            isPlayerOnFire = false;
        }

        if (spawnedProp == null)
        {
            if (player.Position.DistanceTo(propZonePos) <= propZoneRadius && DateTime.Now >= nextPropAllowed)
            {
                Model selectedModel = (random.Next(2) == 0) ? lanternModel : ghostModel;
                if (!selectedModel.IsLoaded) selectedModel.Request();
                else
                {
                    spawnedProp = World.CreateProp(selectedModel, propSpawnPos, false, false);
                    propDeleteTime = DateTime.Now.AddSeconds(1);
                }
            }
        }
        else if (DateTime.Now >= propDeleteTime)
        {
            if (spawnedProp.Exists()) spawnedProp.Delete();
            spawnedProp = null;
            nextPropAllowed = DateTime.Now.AddSeconds(70);
        }

        if (spawnedFilmNoir == null || !spawnedFilmNoir.Exists())
        {
            if (DateTime.Now >= nextFilmNoirAllowed && player.Position.DistanceTo(activeFilmTrigger) <= filmNoirRadius)
            {
                if (!filmNoirModel.IsLoaded) filmNoirModel.Request();
                else
                {
                    spawnedFilmNoir = World.CreatePed(filmNoirModel, activeFilmSpawn, activeFilmHeading);
                    if (spawnedFilmNoir != null && spawnedFilmNoir.Exists())
                    {
                        filmNoirSpawnTime = DateTime.Now;
                        isFilmNoirSoundTriggered = false;
                        spawnedFilmNoir.BlockPermanentEvents = true;
                        spawnedFilmNoir.MaxHealth = 50000;
                        spawnedFilmNoir.Health = 50000;
                        Function.Call(Hash.SET_ENTITY_ALPHA, spawnedFilmNoir, 30, false);
                        Function.Call(Hash.SET_ENTITY_PROOFS, spawnedFilmNoir, true, false, true, true, true, true, true, true);
                        Function.Call(Hash.FREEZE_ENTITY_POSITION, spawnedFilmNoir, true);
                        isFilmNoirRunning = false;
                    }
                }
            }
        }
        else
        {
            if (!isFilmNoirSoundTriggered && DateTime.Now >= filmNoirSpawnTime.AddMilliseconds(500))
            {
                PlaySpecificSound("sound05.wav");
                isFilmNoirSoundTriggered = true;
            }

            if (!isFilmNoirRunning)
            {
                bool isFireNear = Function.Call<int>(Hash.GET_NUMBER_OF_FIRES_IN_RANGE, spawnedFilmNoir.Position.X, spawnedFilmNoir.Position.Y, spawnedFilmNoir.Position.Z, 2.0f) > 0;
                if (isFireNear)
                {
                    isFilmNoirRunning = true;
                    Function.Call(Hash.START_ENTITY_FIRE, spawnedFilmNoir);
                    Function.Call(Hash.SET_ENTITY_PROOFS, spawnedFilmNoir, true, true, true, true, true, true, true, true);
                    Function.Call(Hash.SET_ENTITY_INVINCIBLE, spawnedFilmNoir, true);
                    Function.Call(Hash.FREEZE_ENTITY_POSITION, spawnedFilmNoir, false);
                    spawnedFilmNoir.Task.ClearAll();
                    Function.Call(Hash.TASK_GO_STRAIGHT_TO_COORD, spawnedFilmNoir, activeFilmTarget.X, activeFilmTarget.Y, activeFilmTarget.Z, 4.0f, -1, 0.0f, 0.0f);
                }
            }

            if (isFilmNoirRunning && spawnedFilmNoir.Position.DistanceTo(activeFilmTarget) <= 1.0f)
            {
                spawnedFilmNoir.Delete();
                spawnedFilmNoir = null;
                nextFilmNoirAllowed = DateTime.Now.AddSeconds(120);
                RandomizeFilmNoirLocation();
            }
        }

        if (!isJuggernautFinished)
        {
            if (player.Position.DistanceTo(juggTriggerPos) <= juggTriggerRadius)
            {
                if (spawnedJuggernaut == null || !spawnedJuggernaut.Exists())
                {
                    if (!juggModel.IsLoaded) juggModel.Request();
                    else
                    {
                        spawnedJuggernaut = World.CreatePed(juggModel, juggSpawnPos, 140f);
                        if (spawnedJuggernaut != null && spawnedJuggernaut.Exists())
                        {
                            PlaySpecificSound("sound04.wav");
                            spawnedJuggernaut.BlockPermanentEvents = true;
                            spawnedJuggernaut.MaxHealth = 50000;
                            spawnedJuggernaut.Health = 50000;
                            Function.Call(Hash.SET_ENTITY_PROOFS, spawnedJuggernaut, true, false, true, true, true, true, true, true);
                            Function.Call(Hash.SET_ENTITY_ALPHA, spawnedJuggernaut, 30, false);
                            spawnedJuggernaut.Task.ClearAll();
                            Function.Call(Hash.TASK_GO_STRAIGHT_TO_COORD, spawnedJuggernaut, juggRunTarget.X, juggRunTarget.Y, juggRunTarget.Z, 4.0f, -1, 0.0f, 0.0f);
                            juggSpawnTime = DateTime.Now;
                            isJuggOnFireTriggered = false;
                        }
                    }
                }
            }
            if (spawnedJuggernaut != null && spawnedJuggernaut.Exists())
            {
                if (spawnedJuggernaut.IsAlive) spawnedJuggernaut.Health = 50000;
                if (!isJuggOnFireTriggered && DateTime.Now >= juggSpawnTime.AddMilliseconds(500))
                {
                    Function.Call(Hash.START_ENTITY_FIRE, spawnedJuggernaut);
                    Function.Call(Hash.SET_ENTITY_PROOFS, spawnedJuggernaut, true, true, true, true, true, true, true, true);
                    Function.Call(Hash.SET_ENTITY_INVINCIBLE, spawnedJuggernaut, true);
                    isJuggOnFireTriggered = true;
                }
                if (spawnedJuggernaut.Position.DistanceTo(juggRunTarget) <= 1.0f)
                {
                    spawnedJuggernaut.Delete();
                    spawnedJuggernaut = null;
                    isJuggernautFinished = true;
                }
            }
        }

        if (!isPetrolFireFinished && player.Position.DistanceTo(petrolTriggerPos) <= petrolTriggerRadius)
        {
            if (!isPetrolAssetRequested)
            {
                Function.Call(Hash.REQUEST_NAMED_PTFX_ASSET, "scr_finale1");
                isPetrolAssetRequested = true;
            }
            else if (Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, "scr_finale1"))
            {
                Function.Call(Hash.USE_PARTICLE_FX_ASSET, "scr_finale1");
                petrolPtfxHandle = Function.Call<int>(Hash.START_PARTICLE_FX_LOOPED_AT_COORD, "scr_fin_fire_petrol_trev", -457.72f, 2047.93f, 122.30f, 0.0f, 0.0f, 0.0f, 1.0f, false, false, false, false);
                isPetrolFireFinished = true;
            }
        }

        if (!isSkelEventActive)
        {
            if (DateTime.Now >= nextSkelEventAllowed && player.Position.DistanceTo(activeSkelTrigger) <= skelTriggerRadius)
            {
                if (!skelModel.IsLoaded) skelModel.Request();
                else
                {
                    spawnedSkeleton = World.CreatePed(skelModel, activeSkelSpawn, 198.30f);
                    if (spawnedSkeleton != null && spawnedSkeleton.Exists())
                    {
                        PlaySpecificSound("sound03.wav");
                        spawnedSkeleton.BlockPermanentEvents = true;
                        spawnedSkeleton.MaxHealth = 50000;
                        spawnedSkeleton.Health = 50000;
                        Function.Call(Hash.SET_ENTITY_PROOFS, spawnedSkeleton, true, false, true, true, true, true, true, true);
                        Function.Call(Hash.SET_ENTITY_ALPHA, spawnedSkeleton, 30, false);
                        Function.Call(Hash.SET_ENTITY_COLLISION, spawnedSkeleton, false, false);
                        Function.Call(Hash.FREEZE_ENTITY_POSITION, spawnedSkeleton, true);
                        isSkelEventActive = true;
                        skelSpawnTime = DateTime.Now;
                        isSkelOnFireTriggered = false;
                        skelEventEndTime = DateTime.Now.AddSeconds(30);
                        nextSkelThrowTime = DateTime.Now.AddSeconds(10);
                        nextSkelDamageTime = DateTime.Now.AddSeconds(1);
                    }
                }
            }
        }
        else
        {
            if (spawnedSkeleton != null && spawnedSkeleton.Exists())
            {
                if (!isSkelOnFireTriggered && DateTime.Now >= skelSpawnTime.AddMilliseconds(500))
                {
                    Function.Call(Hash.START_ENTITY_FIRE, spawnedSkeleton);
                    Function.Call(Hash.SET_ENTITY_PROOFS, spawnedSkeleton, true, true, true, true, true, true, true, true);
                    Function.Call(Hash.SET_ENTITY_INVINCIBLE, spawnedSkeleton, true);
                    isSkelOnFireTriggered = true;
                }
                if (player.Position.DistanceTo(spawnedSkeleton.Position) >= 10.0f)
                {
                    spawnedSkeleton.Position = player.Position + player.ForwardVector * 6.0f;
                    spawnedSkeleton.Heading = player.Heading + 180f;
                }
                if (DateTime.Now >= nextSkelDamageTime)
                {
                    player.Health -= 25;
                    nextSkelDamageTime = DateTime.Now.AddSeconds(1);
                }
            }
            if (DateTime.Now >= nextSkelThrowTime)
            {
                player.CanRagdoll = true;
                Function.Call(Hash.SET_PED_TO_RAGDOLL, player, 2000, 2000, 0, true, true, false);
                player.ApplyForce(-player.ForwardVector * 20f + Vector3.WorldUp * 5f);
                nextSkelThrowTime = DateTime.Now.AddSeconds(10);
            }
            if (DateTime.Now >= skelEventEndTime)
            {
                if (spawnedSkeleton != null && spawnedSkeleton.Exists()) spawnedSkeleton.Delete();
                spawnedSkeleton = null;
                isSkelEventActive = false;
                nextSkelEventAllowed = DateTime.Now.AddSeconds(80);
                RandomizeSkeletonLocation();
            }
        }
    }

    private void OnAborted(object sender, EventArgs e)
    {
        if (spawnedProp != null && spawnedProp.Exists()) spawnedProp.Delete();
        if (spawnedFilmNoir != null && spawnedFilmNoir.Exists()) spawnedFilmNoir.Delete();
        if (spawnedJuggernaut != null && spawnedJuggernaut.Exists()) spawnedJuggernaut.Delete();
        if (spawnedSkeleton != null && spawnedSkeleton.Exists()) spawnedSkeleton.Delete();
        if (petrolPtfxHandle != 0) Function.Call(Hash.REMOVE_PARTICLE_FX, petrolPtfxHandle, false);
    }
}