extern crate serenity;

extern crate parking_lot;
extern crate serde_json;
extern crate typemap;

mod confighandler;
mod macros;

// Import the client's bridge to the voice manager. Since voice is a standalone
// feature, it's not as ergonomic to work with as it could be. The client
// provides a clean bridged integration with voice.
use serenity::client::bridge::voice::ClientVoiceManager;
use serenity::client::{Client, Context, EventHandler};
use serenity::framework::StandardFramework;
use serenity::model::channel::Message;
use serenity::model::gateway::Ready;
// Import the `Context` from the client and `parking_lot`'s `Mutex`.
//
// `parking_lot` offers much more efficient implementations of `std::sync`'s
// types. You can read more about it here:
//
// <https://github.com/Amanieu/parking_lot#features>
use serenity::prelude::Mutex;
use serenity::Result as SerenityResult;
use std::sync::Arc;
use typemap::Key;

use confighandler::*;

mod player;
mod youtube;

use player::*;

/*
const fn empty_vec<T>() -> Vec<T> {
    Vec::new()
}
*/

pub struct VoiceManager;

impl Key for VoiceManager {
    type Value = Arc<Mutex<ClientVoiceManager>>;
}

struct Handler;

impl EventHandler for Handler {
    fn ready(&self, _: Context, ready: Ready) {
        println!("{} is connected!", ready.user.name);
    }
}

struct Config {
    token: String,
    prefix: String,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            token: String::new(),
            prefix: String::new(),
        }
    }
}

fn main() {
    // read config file
    let config_file = check_result_return!(read_config("bot.conf"));

    let mut config = Config::default();

    match config_file.get("Meta") {
        Some(info) => {
            match info.get("token") {
                Some(token_pair) => {
                    display_error!(token_pair.set_value(&mut config.token));
                }
                None => {
                    println!("couldn't find token inside meta section");
                    return;
                }
            }
            match info.get("prefix") {
                Some(prefix_pair) => {
                    display_error!(prefix_pair.set_value(&mut config.prefix));
                }
                None => {
                    println!("couldn't find prefix inside meta section");
                    return;
                }
            }
        }
        None => {
            println!("couldn't find Meta section in config file");
            return;
        }
    };

    let mut client = Client::new(&config.token, Handler).expect("Err creating client");

    // Obtain a lock to the data owned by the client, and insert the client's
    // voice manager into it. This allows the voice manager to be accessible by
    // event handlers and framework commands.
    {
        let mut data = client.data.lock();
        data.insert::<VoiceManager>(Arc::clone(&client.voice_manager));
    }

    let media_data = Arc::new(MediaData::default());

    client.with_framework(
        StandardFramework::new()
            .configure(|c| c.prefix(&config.prefix).on_mention(true))
            .cmd("play", Play::new(media_data.clone()))
            .cmd("pause", Pause::new(media_data.clone()))
            .cmd(
                "help",
                Help::new(
                    &config.prefix,
                    vec![
                        "play".to_string(),
                        "pause".to_string(),
                        "stop".to_string(),
                        "help".to_string(),
                        "list".to_string(),
                        "help".to_string(),
                    ],
                ),
            )
            .cmd("stop", Stop::new(media_data.clone()))
            .cmd("list", List::new(media_data.clone()))
            .cmd("skip", Skip::new(media_data.clone())),
    );

    let _ = client
        .start()
        .map_err(|why| println!("Client ended: {:?}", why));
}

/// Checks that a message successfully sent; if not, then logs why to stdout.
fn check_msg(result: SerenityResult<Message>) {
    if let Err(why) = result {
        println!("Error sending message: {:?}", why);
    }
}