
using JetBrains.Annotations;
using Marro.PacManUdon;
using UnityEngine;

public class SoundManager : SyncedObject
{
    [SerializeField] private AudioSource audioSourcePacMan;
    [SerializeField] private AudioSource audioSourceGhosts;
    [SerializeField] private AudioSource audioSourceExtraLife;

    [SerializeField] private AudioClip pacStart;
    [SerializeField] private AudioClip intermission;

    [SerializeField] private AudioClip pacDot1;
    [SerializeField] private AudioClip pacDot2;
    [SerializeField] private AudioClip pacDie1;
    [SerializeField] private AudioClip pacFruit;
    [SerializeField] private AudioClip pacCoin;
    [SerializeField] private AudioClip pacGhostEat;
    [SerializeField] private AudioClip pacGhostBlue;
    [SerializeField] private AudioClip pacGhostRetreat;
    [SerializeField] private AudioClip pacExtraLife;

    [SerializeField] private AudioClip siren0;
    [SerializeField] private AudioClip siren1;
    [SerializeField] private AudioClip siren2;
    [SerializeField] private AudioClip siren3;
    [SerializeField] private AudioClip siren4;

    private bool alternatePelletSound;
    private bool ghostRetreating;
    private bool ghostBlue;
    private int ghostSoundLevel;
    private bool currentlyPlayingSiren;

    private bool suppress;

    public void Initialize()
    {
        Reset();
    }

    public void Reset()
    {
        StopAllSound();

        alternatePelletSound = false;

        ghostRetreating = false;
        ghostBlue = false;
        ghostSoundLevel = 0;
        currentlyPlayingSiren = false;

        suppress = true;
    }

    public void SuppressSound(bool suppress)
    {
        this.suppress = suppress;

        if (suppress)
        {
            StopAllSound();
        }
    }

    public void PlayGameStartSound()
    {
        PlaySound(audioSourcePacMan, pacStart);
    }

    public void StartIntermissionSound()
    {
        PlaySound(audioSourcePacMan, intermission, true);
    }

    public void PlayPelletSound()
    {
        PlaySound(audioSourcePacMan, alternatePelletSound ? pacDot2 : pacDot1);
        alternatePelletSound = !alternatePelletSound;
    }

    public void PlayDeathSound()
    {
        PlaySound(audioSourcePacMan, pacDie1);
    }

    public void PlayFruitSound()
    {
        PlaySound(audioSourcePacMan, pacFruit);
    }

    public void PlayCoinSound()
    {
        PlaySound(audioSourcePacMan, pacCoin);
    }

    public void PlayGhostEatSound()
    {
        PlaySound(audioSourcePacMan, pacGhostEat);
    }

    public void PlayExtraLifeSound()
    {
        PlaySound(audioSourceExtraLife, pacExtraLife);
    }

    public void StartGhostSound()
    {
        UpdateGhostSound();
    }

    public void SetGhostBlue(bool isBlue)
    {
        ghostBlue = isBlue;
        UpdateGhostSound();
    }

    public void SetGhostRetreat(bool isRetreating)
    {
        ghostRetreating = isRetreating;
        UpdateGhostSound();
    }

    public void StopAllSound()
    {
        audioSourcePacMan.Stop();
        audioSourceGhosts.Stop();
        // audioSourceExtraLife is not stopped on purpose, this sound is never interrupted
    }

    public void UpdatePelletCount(int pelletCount)
    {
        int level = 0;
        if (pelletCount < 16)
        {
            level = 4;
        } 
        else if (pelletCount < 32)
        {
            level = 3;
        }
        else if (pelletCount < 64)
        {
            level = 2;
        }
        else if (pelletCount < 128)
        {
            level = 1;
        }

        // Debug.Log($"UpdatePelletCount: {pelletCount}, level: {level}");

        if (ghostSoundLevel >= level)
        {
            return;
        }

        ghostSoundLevel = level;
        UpdateGhostSound();
    }

    private void PlaySound(AudioSource audioSource, AudioClip audioClip, bool loop = false)
    {
        // Debug.Log($"PlaySound, audioSource: {audioSource}, audioClip: {audioClip}, loop: {loop}, suppress: {_suppress}");
        if (suppress)
        {
            return;
        }

        if (loop && audioSource.clip == audioClip && audioSource.isPlaying)
        {
            // Don't restart a looping sound
            return;
        }

        audioSource.clip = audioClip;
        audioSource.Play();
        audioSource.loop = loop;
    }

    private void SwitchSound(AudioSource audioSource, AudioClip audioClip, bool loop)
    {
        if (suppress)
        {
            return;
        }

        if (!audioSource.isPlaying || audioSource.clip == null)
        {
            PlaySound(audioSource, audioClip, loop);
        }
        
        if (audioSource.clip == audioClip)
        {
            // No need to switch
            return;
        }

        var newTimeSamples = (int)(audioSource.timeSamples / (double)audioSource.clip.samples * audioClip.samples);

        audioSource.clip = audioClip;
        audioSource.timeSamples = newTimeSamples;

        audioSource.Play();
    }

    private void UpdateGhostSound()
    {
        if (ghostRetreating)
        {
            PlaySound(audioSourceGhosts, pacGhostRetreat, true);
            currentlyPlayingSiren = false;
            return;
        }

        if (ghostBlue)
        {
            PlaySound(audioSourceGhosts, pacGhostBlue, true);
            currentlyPlayingSiren = false;
            return;
        }

        var currentGhostLevelSound = GetSoundForGhostLevel(ghostSoundLevel);
        if (currentlyPlayingSiren)
        {
            SwitchSound(audioSourceGhosts, currentGhostLevelSound, true);
        }
        else
        {
            PlaySound(audioSourceGhosts, currentGhostLevelSound, true);
        }
        
        currentlyPlayingSiren = true;
    }

    private AudioClip GetSoundForGhostLevel(int ghostLevel)
    {
        switch (ghostLevel)
        {
            case 1:
                return siren1;
            case 2: 
                return siren2;
            case 3:
                return siren3;
            case 4:
                return siren4;
            default:
                return siren0;
        };
    }

    public override void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType)
    {
        data.Append(alternatePelletSound, ref index);
        data.Append(ghostRetreating, ref index);
        data.Append(ghostBlue, ref index);
        data.AppendAsByte(ghostSoundLevel, ref index);
        data.Append(currentlyPlayingSiren, ref index);
    }

    public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType)
    {
        alternatePelletSound = data.ReadBool(ref index);
        ghostRetreating = data.ReadBool(ref index);
        ghostBlue = data.ReadBool(ref index);
        ghostSoundLevel = data.ReadByte(ref index);
        currentlyPlayingSiren = data.ReadBool(ref index);

        UpdateGhostSound();

        return true;
    }
}
