import EventBus, { Events, Event } from "../Events/EventBus";

let instance;
const instanceKey = 'evt_392994';

class Player {

    _sounds = [];
    _currentPlaylistId;
    _globalVolumeMultiplier = 1;
    _fadeOutInterval = null;

    constructor(key) {
        if (key !== instanceKey) {
            throw new Error('Cannot instantiate like this')
        }
        this._handleEvents();
    }

    _handleEvents() {
        const goToSleepModeEv = new Event(
            Events.GO_TO_SLEEP_MODE,
            (payload) => {
                if (this.areSoundsPlaying()) {
                    this.stopAllSoundsSlow();
                }
            }
        );
        EventBus.getInstance().registerEvent(goToSleepModeEv);

        const wakeUp = new Event(
            Events.WAKE_UP,
            (payload) => {
                this.gcc();
            }
        );
        EventBus.getInstance().registerEvent(wakeUp);
    }

    playSounds(sounds, playlistId = null) {
        if (!Array.isArray(sounds)) {
            return;
        }

        this.destroyAll();
        
        this._currentPlaylistId = null;
        if (playlistId) {
            this._currentPlaylistId = playlistId;
        }
        sounds.forEach(soundInfo => this.playSound(soundInfo));
    }

    playSound(soundInfo) {
        const index = this._sounds.findIndex(item => item.id === soundInfo.id);

        if (index === -1) {
            let { soundFileUrl, soundVolume } = soundInfo;
            soundVolume = Number(soundVolume);
            const audio = new Audio(soundFileUrl);
            audio.loop = true;
            audio.volume = ((soundVolume ?? 50) / 100) * this._globalVolumeMultiplier;
            audio.play();
            this._sounds.push({ ...soundInfo, audioObject: audio, isPlaying: true });
        } else {
            const existingAudio = this._sounds[index].audioObject;
            if (existingAudio.paused) {
                existingAudio.play();
                this._sounds[index].isPlaying = true;
            }
        }
    }

    pauseSound(soundInfo) {
        const index = this._sounds.findIndex(item => item.id === soundInfo.id);
        if (index !== -1) {
            const existingAudio = this._sounds[index].audioObject;
            existingAudio.pause();
            this._sounds[index].isPlaying = false;
        }
    }

    setSoundVolume(soundInfo, volume) {
        const index = this._sounds.findIndex(item => item.id === soundInfo.id);
        if (index !== -1) {
            this._sounds[index].audioObject.volume = volume * this._globalVolumeMultiplier;
            this._sounds[index].soundVolume = volume * 100;
        }
    }

    removeSound(soundInfo) {
        const index = this._sounds.findIndex(item => item.id === soundInfo.id);
        if (index !== -1) {
            const audio = this._sounds[index].audioObject;
            if (audio) {
                audio.pause();
                audio.src = '';
                audio.load();
            }
            this._sounds.splice(index, 1);
        }
    }

    getPlayingSounds() {
        return this._sounds.filter(soundData => soundData.isPlaying === true);
    }

    pauseAll() {
        for (const soundInfo of this._sounds) {
            const { audioObject } = soundInfo;
            if (audioObject) {
                audioObject.pause();
            }
        }
    }

    destroyAll() {
        for (const soundInfo of this._sounds) {
            const { audioObject } = soundInfo;
            if (audioObject) {
                audioObject.pause();
                audioObject.src = '';
                audioObject.load();
            }
        }
        this._sounds = [];
        this._currentPlaylistId = null;
    }

    setPlaylistId(id) {
        this._currentPlaylistId = id;
    }

    getPlaylistId() {
        return this._currentPlaylistId;
    }

    areSoundsPlaying() {
        const playingSounds = this.getPlayingSounds();
        return Array.isArray(playingSounds) && playingSounds.length > 0 ? true : false;
    }

    stopAllSoundsSlow() {
        // Clear any existing fade out interval
        if (this._fadeOutInterval) {
            clearInterval(this._fadeOutInterval);
            this._fadeOutInterval = null;
        }

        // Duration: 30 seconds
        const fadeDuration = 30000; // milliseconds
        // Update interval: every 100ms for smooth transition
        const updateInterval = 100; // milliseconds
        const steps = fadeDuration / updateInterval;
        const volumeDecrement = 1 / steps;

        let currentStep = 0;

        this._fadeOutInterval = setInterval(() => {
            currentStep++;

            // Calculate new global volume multiplier
            this._globalVolumeMultiplier = Math.max(0, 1 - (volumeDecrement * currentStep));

            this._sounds.forEach(soundInfo => {
                const { audioObject, soundVolume } = soundInfo;
                if (audioObject) {
                    const baseVolume = (soundVolume ?? 50) / 100;
                    audioObject.volume = baseVolume * this._globalVolumeMultiplier;
                }
            });

            // When fade out is complete
            if (currentStep >= steps) {
                clearInterval(this._fadeOutInterval);
                this._fadeOutInterval = null;
                this._globalVolumeMultiplier = 0;

                this.destroyAll();
                this._globalVolumeMultiplier = 1;
            }
        }, updateInterval);
    }

    gcc() {
        clearInterval(this._fadeOutInterval);
        this._fadeOutInterval = null;
        this.destroyAll();
        this._globalVolumeMultiplier = 1;
    }

    static getInstance() {
        if (!instance) {
            instance = new Player(instanceKey);
        }
        return instance;
    }
}
export default Player;