"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const EventEmitter = require("events");
const cassette_1 = require("cassette");
const Error_1 = require("./Error");
class Playlist extends cassette_1.Playlist {
    constructor(guild) {
        super();
        this.events = new EventEmitter();
        this._playing = false;
        this.guild = guild;
    }
    static ensureVoiceConnection(channel) {
        if (channel.connection)
            return Promise.resolve(channel.connection);
        if (!channel)
            throw new Error_1.default(Error_1.Code.NO_VOICE_CHANNEL);
        if (!channel.joinable)
            throw new Error_1.default(Error_1.Code.NOT_JOINABLE);
        if (!channel.speakable)
            throw new Error_1.default(Error_1.Code.NOT_SPEAKABLE);
        return channel.join();
    }
    get _dispatcher() {
        return this.guild.voiceConnection ? this.guild.voiceConnection.dispatcher : null;
    }
    get playing() {
        return this._playing;
    }
    stop() {
        return this._end('temp');
    }
    destroy() {
        return this._end('terminal');
    }
    pause() {
        if (this._dispatcher)
            this._dispatcher.pause();
        this.events.emit('pause');
    }
    resume() {
        if (this._dispatcher)
            this._dispatcher.resume();
        this.events.emit('resume');
    }
    start(channel, options) {
        return __awaiter(this, void 0, void 0, function* () {
            yield Playlist.ensureVoiceConnection(channel);
            yield this._start(options);
        });
    }
    _start(options) {
        return __awaiter(this, void 0, void 0, function* () {
            this.stop();
            if (!this.current) {
                this.events.emit('error', new Error_1.default(Error_1.Code.NO_CURRENT_SONG));
                return;
            }
            const stream = yield this.current.stream();
            stream.once('error', (e) => {
                this._playing = false;
                this.events.emit('streamError', e);
                this._end();
            });
            if (!this.guild.voiceConnection) {
                this.events.emit('error', new Error_1.default(Error_1.Code.NO_VOICE_CONNECTION));
                return;
            }
            const dispatcher = this.guild.voiceConnection.playStream(stream, options);
            this._playing = true;
            this.events.emit('playing');
            dispatcher.once('end', (reason) => __awaiter(this, void 0, void 0, function* () {
                this._playing = false;
                this.events.emit('ended', reason);
                if (reason === 'temp')
                    return;
                if (reason === 'terminal')
                    return this._destroy();
                const next = yield this.next();
                if (!next)
                    return this._destroy();
                yield this._start(options);
            }));
        });
    }
    _end(reason = 'terminal') {
        if (this._dispatcher)
            this._dispatcher.end(reason);
    }
    _destroy() {
        if (this.guild.voiceConnection)
            this.guild.voiceConnection.disconnect();
        this.guild.playlist.reset();
        this.events.emit('destroyed');
    }
}
exports.default = Playlist;
