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

public class HauntedForest : Script
{
    private Vector3 HauntedForestZone = new Vector3(-1434f, 4609f, 56f);
    private float zoneRadius = 250f;
    private bool isInHauntedZone = false;
    private bool timecycleActive = false;

    private bool eventTriggered = false;

    // Gestion du son "sound06.wav"
    private bool soundPlayed = false;
    private SoundPlayer spookySoundPlayer = null;

    // Gestion de la santé modifiée du joueur
    private int originalMaxHealth = 200;
    private int originalHealth = 200;
    private bool healthModified = false;

    private readonly List<Prop> ghostCops = new List<Prop>();
    private bool propsSpawned = false;
    private int propsSpawnTime = 0;

    private bool fireActive = false;
    private int fireStartTime = 0;

    private int lastSpookyEventTime = 0;
    private readonly Random random = new Random();
    private readonly string[] bloodEffects = {
        "blood_animal_maul",
        "td_blood_stab",
        "ent_sht_blood",
        "blood_fall"
    };
    private bool isPtfxActive = false;
    private string chosenEffect = "";
    private int ptfxStartTime = 0;
    private int lastPtfxSpawnTime = 0;

    private readonly List<Prop> lanterns = new List<Prop>();
    private bool lanternsSpawned = false;
    private int lanternsSpawnTime = 0;

    private readonly List<int> flameFxHandles = new List<int>();
    private bool flameFxActive = false;
    private int flameFxStartTime = 0;

    private bool skeletonSpawned = false;
    private Ped skeletonPed = null;      
    private Prop skeletonProp = null;    
    private int lastSkeletonDamageTime = 0;
    private int nextSkeletonTpTime = 0;

    private int lastTickTime = 0;
    private int accumulatedZoneTime = 0;          
    private int accumulatedSpookyEventTime = 0;   
    private int savedSkeletonHealth = 3000;       

    private bool isSkeletonDefeated = false;

    public HauntedForest()
    {
        Tick += OnTick;
        Aborted += OnAborted;
    }

    private float GetGroundZ(Vector3 pos)
    {
        OutputArgument groundZ = new OutputArgument();
        if (Function.Call<bool>(Hash.GET_GROUND_Z_FOR_3D_COORD, pos.X, pos.Y, pos.Z + 10f, groundZ, false, false))
        {
            return groundZ.GetResult<float>();
        }
        if (Function.Call<bool>(Hash.GET_GROUND_Z_FOR_3D_COORD, pos.X, pos.Y, 1000f, groundZ, false, false))
        {
            return groundZ.GetResult<float>();
        }
        return pos.Z;
    }

    private void PlaySpookySound()
    {
        try
        {
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scripts", "HauntedSounds", "sound06.wav");
            
            if (!File.Exists(path))
            {
                path = Path.Combine("scripts", "HauntedSounds", "sound06.wav");
            }

            if (File.Exists(path))
            {
                if (spookySoundPlayer == null)
                {
                    spookySoundPlayer = new SoundPlayer(path);
                    spookySoundPlayer.Load();
                }
                spookySoundPlayer.Play();
            }
        }
        catch (Exception)
        {
            // Capture silencieuse en cas d'erreur
        }
    }

    private void RestorePlayerHealth(Ped playerPed)
    {
        if (healthModified && playerPed != null && playerPed.Exists() && playerPed.IsAlive)
        {
            playerPed.MaxHealth = originalMaxHealth;
            playerPed.Health = Math.Min(originalHealth, originalMaxHealth);
            healthModified = false;
        }
    }

    private void OnTick(object sender, EventArgs e)
    {
        if (isSkeletonDefeated) return;

        Player player = Game.Player;
        Ped playerPed = player.Character;

        if (playerPed == null || !playerPed.Exists()) return;

        Vector3 playerPos = playerPed.Position;

        int currentTickTime = Game.GameTime;
        int deltaTime = lastTickTime == 0 ? 0 : currentTickTime - lastTickTime;
        lastTickTime = currentTickTime;
        
        int currentHour = Function.Call<int>(Hash.GET_CLOCK_HOURS);
        bool correctTime = currentHour >= 23 || currentHour < 4;

        bool inZone = playerPos.DistanceTo(HauntedForestZone) <= zoneRadius;

        if (correctTime)
        {
            if (inZone)
            {
                if (!isInHauntedZone)
                {
                    isInHauntedZone = true;
                }

                if (!Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, "core"))
                {
                    Function.Call(Hash.REQUEST_NAMED_PTFX_ASSET, "core");
                }

                if (!timecycleActive)
                {
                    Function.Call(Hash.SET_TIMECYCLE_MODIFIER, "casino_hotel");
                    timecycleActive = true;
                }

                if (isSkeletonDefeated)
                {
                    HandleTimedEvents(playerPed); 
                    return; 
                }

                if (!healthModified)
                {
                    originalMaxHealth = playerPed.MaxHealth;
                    originalHealth = playerPed.Health;

                    playerPed.MaxHealth = 1000;
                    playerPed.Health = 1000;
                    healthModified = true;
                }

                accumulatedZoneTime += deltaTime;
                accumulatedSpookyEventTime += deltaTime;

                if (!soundPlayed && (accumulatedZoneTime >= 20000))
                {
                    PlaySpookySound();
                    soundPlayed = true;
                }

                if (skeletonSpawned && skeletonPed == null && !isSkeletonDefeated)
                {
                    SpawnSkeletonBoss(playerPed);
                    if (skeletonPed != null && skeletonPed.Exists())
                    {
                        skeletonPed.Health = savedSkeletonHealth;
                    }
                }

                if (!eventTriggered && (accumulatedZoneTime >= 30000))
                {
                    TriggerHauntedEvent(playerPed);
                    eventTriggered = true;
                }

                bool isSkeletonActive = (skeletonPed != null && skeletonPed.Exists() && skeletonPed.IsAlive);
                int eventInterval = isSkeletonActive ? 30000 : 40000; 

                if (accumulatedSpookyEventTime >= eventInterval)
                {
                    TriggerRandomEvent(playerPed);
                    accumulatedSpookyEventTime = 0; 
                }

                if (!skeletonSpawned && (accumulatedZoneTime >= 150000))
                {
                    SpawnSkeletonBoss(playerPed);
                    skeletonSpawned = true;
                    accumulatedSpookyEventTime = 0; 
                }

                if (skeletonPed != null && skeletonPed.Exists())
                {
                    if (skeletonPed.IsAlive)
                    {
                        ManageSkeletonBoss(playerPed);
                    }
                    else
                    {
                        isSkeletonDefeated = true;
                        ResetHauntedState(playerPed);
                    }
                }
            }
            else
            {
                if (isInHauntedZone)
                {
                    isInHauntedZone = false;
                }
                PauseHauntedState(playerPed);
            }
        }
        else
        {
            if (isInHauntedZone)
            {
                isInHauntedZone = false;
            }
            ResetHauntedState(playerPed);
        }

        HandleTimedEvents(playerPed);
    }

    private void TriggerRandomEvent(Ped playerPed)
    {
        int choice = random.Next(3);
        switch (choice)
        {
            case 0:
                SpawnLanternsAroundPlayer(playerPed);
                break;
            case 1:
                TriggerFlameEvent(playerPed);
                break;
            case 2:
                StartBloodEffectSequence();
                break;
        }
    }

    private void TriggerHauntedEvent(Ped playerPed)
    {
        DeleteProps();

        Model propModel = new Model("m24_1_prop_m41_ghost_cop_01a");
        if (propModel.IsValid)
        {
            propModel.Request();
            while (!propModel.IsLoaded)
            {
                Wait(0); 
            }

            // Génération d'une seule entité à 20 mètres face au joueur
            Vector3 spawnPos = playerPed.GetOffsetPosition(new Vector3(0f, 20f, 0f));

            float groundZ = GetGroundZ(spawnPos);
            spawnPos.Z = groundZ + 0.1f;

            Prop ghost = World.CreateProp(propModel, spawnPos, playerPed.Rotation, false, false);
            if (ghost != null && ghost.Exists())
            {
                SetupGhostProp(ghost);
                ghostCops.Add(ghost);
            }

            propsSpawned = true;
            propsSpawnTime = Game.GameTime;

            propModel.MarkAsNoLongerNeeded();
        }
    }

    private void StartBloodEffectSequence()
    {
        chosenEffect = bloodEffects[random.Next(bloodEffects.Length)];
        isPtfxActive = true;
        ptfxStartTime = Game.GameTime;
        lastPtfxSpawnTime = 0; 
    }

    private void SpawnSinglePtfx(Ped playerPed, string effectName)
    {
        if (Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, "core"))
        {
            Function.Call((Hash)0x6C38AF3693A69A91, "core"); 

            Function.Call(Hash.START_PARTICLE_FX_NON_LOOPED_ON_ENTITY, 
                effectName, 
                playerPed.Handle, 
                0.0f, 2.0f, 0.3f, 
                0.0f, 0.0f, 0.0f, 
                1.5f, 
                false, false, false);
        }
    }

    private void SpawnLanternsAroundPlayer(Ped playerPed)
    {
        DeleteLanterns();

        Model lanternModel = new Model("m24_2_prop_m42_lantern_01b");
        if (lanternModel.IsValid)
        {
            lanternModel.Request();
            while (!lanternModel.IsLoaded)
            {
                Wait(0);
            }

            Vector3 centerPos = playerPed.Position;
            float radius = 10f; 
            int count = 10;     

            for (int i = 0; i < count; i++)
            {
                float angle = i * (360f / count) * ((float)Math.PI / 180f);
                float x = centerPos.X + (float)Math.Cos(angle) * radius;
                float y = centerPos.Y + (float)Math.Sin(angle) * radius;

                float groundZ = GetGroundZ(new Vector3(x, y, centerPos.Z));
                Vector3 spawnPos = new Vector3(x, y, groundZ + 0.15f); 

                Prop lantern = World.CreateProp(lanternModel, spawnPos, Vector3.Zero, false, false);
                if (lantern != null && lantern.Exists())
                {
                    lantern.HasGravity = false;
                    lantern.IsCollisionEnabled = false; 

                    Function.Call(Hash.SET_ENTITY_HAS_GRAVITY, lantern.Handle, false);
                    Function.Call(Hash.SET_ENTITY_COLLISION, lantern.Handle, false, false);

                    lanterns.Add(lantern);
                }
            }

            lanternsSpawned = true;
            lanternsSpawnTime = Game.GameTime;

            lanternModel.MarkAsNoLongerNeeded();
        }

        Function.Call(Hash.START_ENTITY_FIRE, playerPed);
        fireActive = true;
        fireStartTime = Game.GameTime;
    }

    private void TriggerFlameEvent(Ped playerPed)
    {
        if (Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, "core"))
        {
            StopFlameFx(); 

            Vector3 frontPos = playerPed.GetOffsetPosition(new Vector3(0.0f, 2.0f, 0.3f));
            Vector3 leftPos = playerPed.GetOffsetPosition(new Vector3(-2.0f, 0.0f, 0.3f));
            Vector3 rightPos = playerPed.GetOffsetPosition(new Vector3(2.0f, 0.0f, 0.3f));

            Function.Call((Hash)0x6C38AF3693A69A91, "core"); 
            int frontHandle = Function.Call<int>(Hash.START_PARTICLE_FX_LOOPED_AT_COORD, 
                "ent_sht_flame", 
                frontPos.X, frontPos.Y, frontPos.Z, 
                0.0f, 0.0f, 0.0f, 
                2.0f, 
                false, false, false, false);

            Function.Call((Hash)0x6C38AF3693A69A91, "core");
            int leftHandle = Function.Call<int>(Hash.START_PARTICLE_FX_LOOPED_AT_COORD, 
                "ent_sht_flame", 
                leftPos.X, leftPos.Y, leftPos.Z, 
                0.0f, 0.0f, 0.0f, 
                2.0f, 
                false, false, false, false);

            Function.Call((Hash)0x6C38AF3693A69A91, "core");
            int rightHandle = Function.Call<int>(Hash.START_PARTICLE_FX_LOOPED_AT_COORD, 
                "ent_sht_flame", 
                rightPos.X, rightPos.Y, rightPos.Z, 
                0.0f, 0.0f, 0.0f, 
                2.0f, 
                false, false, false, false);

            flameFxHandles.Add(frontHandle);
            flameFxHandles.Add(leftHandle);
            flameFxHandles.Add(rightHandle);

            flameFxActive = true;
            flameFxStartTime = Game.GameTime;
        }
    }

    private void SpawnSkeletonBoss(Ped playerPed)
    {
        if (skeletonPed != null && skeletonPed.Exists()) skeletonPed.Delete();
        if (skeletonProp != null && skeletonProp.Exists()) skeletonProp.Delete();

        Model pedModel = new Model("ig_skeleton_01");
        Model propModel = new Model("m23_1_prop_m31_ghostzombie_01a");

        if (pedModel.IsValid && propModel.IsValid)
        {
            pedModel.Request();
            propModel.Request();

            while (!pedModel.IsLoaded || !propModel.IsLoaded)
            {
                Wait(0);
            }

            Vector3 spawnPos = playerPed.GetOffsetPosition(new Vector3(0f, 10f, 0f));
            skeletonPed = World.CreatePed(pedModel, spawnPos, playerPed.Heading);

            if (skeletonPed != null && skeletonPed.Exists())
            {
                skeletonPed.MaxHealth = 3000;
                skeletonPed.Health = 3000;

                skeletonPed.HasGravity = false;
                Function.Call(Hash.SET_ENTITY_HAS_GRAVITY, skeletonPed.Handle, false);

                Function.Call(Hash.SET_ENTITY_ALPHA, skeletonPed.Handle, 0, false);

                Function.Call(Hash.SET_PED_SUFFERS_CRITICAL_HITS, skeletonPed.Handle, false);

                skeletonPed.IsFireProof = true;
                Function.Call(Hash.SET_ENTITY_PROOFS, skeletonPed.Handle, false, true, false, false, false, false, false, false);

                skeletonPed.CanRagdoll = false;
                Function.Call(Hash.SET_PED_CAN_RAGDOLL, skeletonPed.Handle, false);

                skeletonPed.BlockPermanentEvents = true;

                int civMaleHash = Function.Call<int>(Hash.GET_HASH_KEY, "CIVMALE");
                Function.Call(Hash.SET_PED_RELATIONSHIP_GROUP_HASH, skeletonPed.Handle, civMaleHash);

                skeletonProp = World.CreateProp(propModel, spawnPos, Vector3.Zero, false, false);
                if (skeletonProp != null && skeletonProp.Exists())
                {
                    Function.Call(Hash.SET_ENTITY_ALPHA, skeletonProp.Handle, 80, false);

                    skeletonProp.HasGravity = false;
                    Function.Call(Hash.SET_ENTITY_HAS_GRAVITY, skeletonProp.Handle, false);

                    skeletonProp.IsCollisionEnabled = false;
                    Function.Call(Hash.SET_ENTITY_COLLISION, skeletonProp.Handle, false, false);

                    Function.Call(Hash.ATTACH_ENTITY_TO_ENTITY, 
                        skeletonProp.Handle, 
                        skeletonPed.Handle, 
                        0, 
                        0.0f, 0.0f, -0.95f, 
                        0.0f, 0.0f, 0.0f, 
                        false, false, false, false, 2, true);
                }

                nextSkeletonTpTime = Game.GameTime + random.Next(2000, 4001);
            }

            pedModel.MarkAsNoLongerNeeded();
            propModel.MarkAsNoLongerNeeded();
        }
    }

    private void ManageSkeletonBoss(Ped playerPed)
    {
        if (Game.GameTime >= nextSkeletonTpTime)
        {
            TeleportSkeletonAroundPlayer(playerPed);
            nextSkeletonTpTime = Game.GameTime + random.Next(2000, 4001);
        }

        if (skeletonPed != null && skeletonPed.Exists())
        {
            float dx = playerPed.Position.X - skeletonPed.Position.X;
            float dy = playerPed.Position.Y - skeletonPed.Position.Y;
            float heading = Function.Call<float>(Hash.GET_HEADING_FROM_VECTOR_2D, dx, dy);
            skeletonPed.Heading = heading + 180.0f; 
        }

        if (Game.GameTime - lastSkeletonDamageTime >= 2000)
        {
            if (playerPed.Health > 0)
            {
                playerPed.Health = Math.Max(0, playerPed.Health - 5);
            }
            lastSkeletonDamageTime = Game.GameTime;
        }
    }

    private void TeleportSkeletonAroundPlayer(Ped playerPed)
    {
        if (skeletonPed == null || !skeletonPed.Exists()) return;

        float angle = (float)(random.NextDouble() * Math.PI * 2.0);
        float distance = (float)(random.NextDouble() * 6.0) + 2.0f;

        float xOffset = (float)Math.Cos(angle) * distance;
        float yOffset = (float)Math.Sin(angle) * distance;

        Vector3 playerPos = playerPed.Position;
        Vector3 tpPos = new Vector3(playerPos.X + xOffset, playerPos.Y + yOffset, playerPos.Z);

        float groundZ = GetGroundZ(tpPos);
        tpPos.Z = groundZ + 0.1f;

        skeletonPed.Position = tpPos;
    }

    private void TriggerPushEvent(Ped playerPed)
    {
        playerPed.Ragdoll(1500);

        Vector3 backwardDirection = -playerPed.ForwardVector;
        Vector3 pushForce = (backwardDirection * 12.0f) + new Vector3(0f, 0f, 4.0f);
        playerPed.Velocity = pushForce;
    }

    private void SetupGhostProp(Prop prop)
    {
        if (prop != null && prop.Exists())
        {
            prop.HasGravity = false;
            prop.IsCollisionEnabled = false;

            Function.Call(Hash.SET_ENTITY_HAS_GRAVITY, prop.Handle, false);
            Function.Call(Hash.SET_ENTITY_COLLISION, prop.Handle, false, false);
        }
    }

    private void HandleTimedEvents(Ped playerPed)
    {
        if (propsSpawned && (Game.GameTime - propsSpawnTime >= 2000))
        {
            DeleteProps();
        }

        if (fireActive && (Game.GameTime - fireStartTime >= 3000))
        {
            StopFire(playerPed);
        }

        if (isPtfxActive)
        {
            if (Game.GameTime - lastPtfxSpawnTime >= 400)
            {
                SpawnSinglePtfx(playerPed, chosenEffect);
                lastPtfxSpawnTime = Game.GameTime;
            }

            if (Game.GameTime - ptfxStartTime >= 3000)
            {
                isPtfxActive = false;
                TriggerPushEvent(playerPed);
            }
        }

        if (lanternsSpawned && (Game.GameTime - lanternsSpawnTime >= 3000))
        {
            DeleteLanterns();
        }

        if (flameFxActive && (Game.GameTime - flameFxStartTime >= 3000))
        {
            StopFlameFx();
        }
    }

    private void DeleteProps()
    {
        foreach (var ghost in ghostCops)
        {
            if (ghost != null && ghost.Exists())
            {
                ghost.Delete();
            }
        }
        ghostCops.Clear();
        propsSpawned = false;
    }

    private void DeleteLanterns()
    {
        foreach (var lantern in lanterns)
        {
            if (lantern != null && lantern.Exists())
            {
                lantern.Delete();
            }
        }
        lanterns.Clear();
        lanternsSpawned = false;
    }

    private void StopFlameFx()
    {
        foreach (var handle in flameFxHandles)
        {
            if (handle != -1)
            {
                Function.Call(Hash.STOP_PARTICLE_FX_LOOPED, handle, false);
            }
        }
        flameFxHandles.Clear();
        flameFxActive = false;
    }

    private void StopFire(Ped playerPed)
    {
        if (playerPed != null && playerPed.Exists())
        {
            Function.Call(Hash.STOP_ENTITY_FIRE, playerPed);
        }
        fireActive = false;
    }

    private void PauseHauntedState(Ped playerPed)
    {
        RestorePlayerHealth(playerPed);

        if (timecycleActive)
        {
            Function.Call(Hash.CLEAR_TIMECYCLE_MODIFIER);
            timecycleActive = false;
        }

        DeleteProps();
        DeleteLanterns();
        StopFlameFx();
        StopFire(playerPed);
        isPtfxActive = false;

        if (skeletonPed != null && skeletonPed.Exists())
        {
            if (skeletonPed.IsAlive)
            {
                savedSkeletonHealth = skeletonPed.Health; 
                skeletonPed.Delete();
            }
            skeletonPed = null;
        }

        if (skeletonProp != null && skeletonProp.Exists())
        {
            skeletonProp.Delete();
        }
        skeletonProp = null;
    }

    private void ResetHauntedState(Ped playerPed)
    {
        RestorePlayerHealth(playerPed);

        if (timecycleActive)
        {
            Function.Call(Hash.CLEAR_TIMECYCLE_MODIFIER);
            timecycleActive = false;
        }

        DeleteProps();
        DeleteLanterns();
        StopFlameFx();
        StopFire(playerPed);
        isPtfxActive = false;

        if (skeletonPed != null && skeletonPed.Exists())
        {
            skeletonPed.Delete();
        }
        skeletonPed = null;
        skeletonSpawned = false;

        if (skeletonProp != null && skeletonProp.Exists())
        {
            skeletonProp.Delete();
        }
        skeletonProp = null;

        accumulatedZoneTime = 0;
        accumulatedSpookyEventTime = 0;
        eventTriggered = false;
        soundPlayed = false; 
        savedSkeletonHealth = 3000;
    }

    private void OnAborted(object sender, EventArgs e)
    {
        if (Game.Player != null && Game.Player.Character != null && Game.Player.Character.Exists())
        {
            RestorePlayerHealth(Game.Player.Character);
            StopFire(Game.Player.Character);
        }

        if (timecycleActive)
        {
            Function.Call(Hash.CLEAR_TIMECYCLE_MODIFIER);
        }
        DeleteProps();
        DeleteLanterns();
        StopFlameFx();

        if (skeletonProp != null && skeletonProp.Exists())
        {
            skeletonProp.Delete();
        }
        skeletonProp = null;

        if (skeletonPed != null && skeletonPed.Exists())
        {
            skeletonPed.Delete();
        }
        skeletonPed = null;

        isPtfxActive = false;

        if (spookySoundPlayer != null)
        {
            spookySoundPlayer.Dispose();
            spookySoundPlayer = null;
        }

        if (Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, "core"))
        {
            Function.Call(Hash.REMOVE_NAMED_PTFX_ASSET, "core");
        }
    }
}