<commit_before>// The MIT License (MIT)
// Copyright (c) 2017 <NAME>

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without
// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

import Dispatch
import Foundation

/// A delegate for a VoiceManager.
public protocol DiscordVoiceManagerDelegate : class, DiscordTokenBearer {
    // MARK: Methods

    /**
        Called when an engine disconnects.

        - parameter manager: The manager.
        - parameter engine: The engine that disconnected.
    */
    func voiceManager(_ manager: DiscordVoiceManager, didDisconnectEngine engine: DiscordVoiceEngine)

    /**
        Called when a voice engine receives voice data.

        - parameter manager: The manager.
        - parameter didReceiveVoiceData: The data received.
        - parameter fromEngine: The engine that received the data.
    */
    func voiceManager(_ manager: DiscordVoiceManager, didReceiveVoiceData data: DiscordVoiceData,
                      fromEngine engine: DiscordVoiceEngine)

    /**
        Called when a voice engine is ready.

        - parameter manager: The manager.
        - parameter engine: The engine that's ready.
    */
    func voiceManager(_ manager: DiscordVoiceManager, engineIsReady engine: DiscordVoiceEngine)

    /**
        Called when a voice engine needs an encoder.

        - parameter manager: The manager that is requesting an encoder.
        - parameter engine: The engine that needs an encoder
        - returns: An encoder.
    */
    func voiceManager(_ manager: DiscordVoiceManager,
                      needsEncoderForEngine engine: DiscordVoiceEngine) throws -> DiscordVoiceEncoder?
}

/// A manager for voice engines.
open class DiscordVoiceManager : DiscordVoiceEngineDelegate, Lockable {
    // MARK: Properties

    /// The delegate for this manager.
    public weak var delegate: DiscordVoiceManagerDelegate?

    /// The token for the user.
    public var token: DiscordToken {
        return delegate!.token
    }

    /// The voice engines, indexed by guild id.
    public private(set) var voiceEngines = [GuildID: DiscordVoiceEngine]()

    /// The voice states for this user, if they are in any voice channels.
    public internal(set) var voiceStates = [GuildID: DiscordVoiceState]()

    let lock = DispatchSemaphore(value: 1)

    var voiceServerInformations = [GuildID: DiscordVoiceServerInformation]()

    private var logType: String { return "DiscordVoiceManager" }

    // MARK: Initializers

    /**
        Creates a new manager with the delegate set.
    */
    public init(delegate: DiscordVoiceManagerDelegate) {
        self.delegate = delegate
    }

    // MARK: Methods

    /**
        Leaves the voice channel that is associated with the guild specified.

        - parameter onGuild: The snowflake of the guild that you want to leave.
    */
    open func leaveVoiceChannel(onGuild guildId: GuildID) {
        guard let engine = get(voiceEngines[guildId]) else { return }

        protected {
            voiceStates[guildId] = nil
            voiceServerInformations[guildId] = nil
        }

        engine.disconnect()

        // Make sure everything is cleaned out

        DefaultDiscordLogger.Logger.verbose("Rejoining voice channels after leave", type: logType)

        for (guildId, _) in voiceEngines {
            startVoiceConnection(guildId)
        }
    }

    /**
        Tries to open a voice connection to the specified guild.
        Only succeeds if we have both a voice state and the voice server info for this guild.

        - parameter guildId: The id of the guild to connect to.
    */
    open func startVoiceConnection(_ guildId: GuildID) {
        protected {
            _startVoiceConnection(guildId)
        }
    }

    /// Tries to create a voice engine for a guild, and connect.
    /// **Not thread safe.**
    private func _startVoiceConnection(_ guildId: GuildID) {
        // We need both to start the connection
        guard let voiceState = voiceStates[guildId], let serverInfo = voiceServerInformations[guildId] else {
            return
        }<commit_msg>More debugging<commit_after>// The MIT License (MIT)
// Copyright (c) 2017 <NAME>

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without
// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

import Dispatch
import Foundation

/// A delegate for a VoiceManager.
public protocol DiscordVoiceManagerDelegate : class, DiscordTokenBearer {
    // MARK: Methods

    /**
        Called when an engine disconnects.

        - parameter manager: The manager.
        - parameter engine: The engine that disconnected.
    */
    func voiceManager(_ manager: DiscordVoiceManager, didDisconnectEngine engine: DiscordVoiceEngine)

    /**
        Called when a voice engine receives voice data.

        - parameter manager: The manager.
        - parameter didReceiveVoiceData: The data received.
        - parameter fromEngine: The engine that received the data.
    */
    func voiceManager(_ manager: DiscordVoiceManager, didReceiveVoiceData data: DiscordVoiceData,
                      fromEngine engine: DiscordVoiceEngine)

    /**
        Called when a voice engine is ready.

        - parameter manager: The manager.
        - parameter engine: The engine that's ready.
    */
    func voiceManager(_ manager: DiscordVoiceManager, engineIsReady engine: DiscordVoiceEngine)

    /**
        Called when a voice engine needs an encoder.

        - parameter manager: The manager that is requesting an encoder.
        - parameter engine: The engine that needs an encoder
        - returns: An encoder.
    */
    func voiceManager(_ manager: DiscordVoiceManager,
                      needsEncoderForEngine engine: DiscordVoiceEngine) throws -> DiscordVoiceEncoder?
}

/// A manager for voice engines.
open class DiscordVoiceManager : DiscordVoiceEngineDelegate, Lockable {
    // MARK: Properties

    /// The delegate for this manager.
    public weak var delegate: DiscordVoiceManagerDelegate?

    /// The token for the user.
    public var token: DiscordToken {
        return delegate!.token
    }

    /// The voice engines, indexed by guild id.
    public private(set) var voiceEngines = [GuildID: DiscordVoiceEngine]()

    /// The voice states for this user, if they are in any voice channels.
    public internal(set) var voiceStates = [GuildID: DiscordVoiceState]()

    let lock = DispatchSemaphore(value: 1)

    var voiceServerInformations = [GuildID: DiscordVoiceServerInformation]()

    private var logType: String { return "DiscordVoiceManager" }

    // MARK: Initializers

    /**
        Creates a new manager with the delegate set.
    */
    public init(delegate: DiscordVoiceManagerDelegate) {
        self.delegate = delegate
    }

    // MARK: Methods

    /**
        Leaves the voice channel that is associated with the guild specified.

        - parameter onGuild: The snowflake of the guild that you want to leave.
    */
    open func leaveVoiceChannel(onGuild guildId: GuildID) {
        guard let engine = get(voiceEngines[guildId]) else {
            DefaultDiscordLogger.Logger.error("Could not find a voice engine for guild \(guildId)", type: logType)

            return
        }

        protected {
            voiceStates[guildId] = nil
            voiceServerInformations[guildId] = nil
        }

        DefaultDiscordLogger.Logger.verbose("Disconnecting voice engine for guild \(guildId)", type: logType)

        engine.disconnect()

        // Make sure everything is cleaned out

        DefaultDiscordLogger.Logger.verbose("Rejoining voice channels after leave", type: logType)

        for (guildId, _) in voiceEngines {
            startVoiceConnection(guildId)
        }
    }

    /**
        Tries to open a voice connection to the specified guild.
        Only succeeds if we have both a voice state and the voice server info for this guild.

        - parameter guildId: The id of the guild to connect to.
    */
    open func startVoiceConnection(_ guildId: GuildID) {
        protected {
            _startVoiceConnection(guildId)
        }
    }

    /// Tries to create a voice engine for a guild, and connect.
    /// **Not thread safe.**
    private func _startVoiceConnection(_ guildId: GuildID) {
        // We need both to start the connection
        guard let voiceState = voiceStates[guildId], let serverInfo = voiceServerInformations[guildId] else {
            return
        }
{'fraction_non_alphanumeric': 0.06767208043310131, 'fraction_numerical': 0.0009667440061871616, 'mean_word_length': 2.7577188521612785, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4038108', 'n_tokens_mistral': 2720, 'n_tokens_neox': 2487, 'n_words': 1096}