use serenity;
use serenity::model::id::ChannelId;
use serenity::voice::ffmpeg;
use serenity::voice::LockedAudio;

use std::sync::Arc;

use super::prelude::*;

use rusqlite::{params, Connection};

pub struct Song {
    pub name: String,
}

pub struct MediaData {
    playlist: Vec<Song>,
    current_song: Option<LockedAudio>,
    song_name: String,
    pub next_callback: Option<Arc<Fn() -> ()>>,

    sql_data_base: Connection,
}

impl MediaData {
    pub fn new(file: &str) -> Result<MediaData, String> {
        let connection = match Connection::open(file) {
            Ok(file) => file,
            Err(_) => return Err(format!("can't open {}", file)),
        };

        match connection.execute(
            "CREATE TABLE IF NOT EXISTS Vulva3 (
                name            TEXT PRIMARY KEY,
                link            TEXT NOT NULL
            )",
            params![],
        ) {
            Ok(_) => (),
            Err(err) => {
                println!("{}", err);
                return Err(format!("can't create table"));
            }
        };

        Ok(MediaData {
            playlist: Vec::new(),
            current_song: None,
            song_name: String::new(),
            next_callback: None,

            sql_data_base: connection,
        })
    }

    pub fn reset(
        &mut self,
        ctx: &mut serenity::client::Context,
        msg: &serenity::model::channel::Message,
    ) -> Result<(), String> {
        self.playlist().clear();
        self.song = None;
        self.name = String::new();
        self.next_callback = None();

        if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
            let mut manager = manager_lock.lock();

            let guild_id = match guild_id(msg.channel_id) {
                Some(guild_id) => guild_id,
                None => return Ok(()),
            };

            {
                let handler = match handler(guild_id, &mut manager) {
                    Some(handler) => handler,
                    None => return Ok(()),
                };

                println!("stopped handler");

                handler.stop();
            }

            manager.remove(guild_id);
        }

        Ok(())
    }

    pub fn db(&self) -> &Connection {
        &self.sql_data_base
    }

    pub fn song(&self) -> &Option<LockedAudio> {
        &self.current_song
    }

    pub fn song_mut(&mut self) -> &mut Option<LockedAudio> {
        &mut self.current_song
    }

    pub fn playlist(&self) -> &Vec<Song> {
        &self.playlist
    }

    pub fn playlist_mut(&mut self) -> &mut Vec<Song> {
        &mut self.playlist
    }

    pub fn song_name(&self) -> &String {
        &self.name
    }

    pub fn song_name_mut(&mut self) -> &mut String {
        &mut self.name
    }

    pub fn start_playing(
        mediadata: &Arc<MediaData>,
        callback: Arc<Fn() -> ()>,
        channel_id: ChannelId,
        manager_lock: &Arc<
            serenity::prelude::Mutex<serenity::client::bridge::voice::ClientVoiceManager>,
        >,
    ) {
        // check if there is already playing
        let already_started = {
            let song_lock = match mediadata.song_mut() {
                Ok(song) => song,
                Err(msg) => {
                    println!("{}", msg);
                    return;
                }
            };

            let song = song_lock.borrow();
            song.is_some()
        };

        // if there isnt already a song playing, start a new one
        if !already_started {
            *mediadata.next_callback.borrow_mut() = Some(callback.clone());

            Self::next_song(mediadata, channel_id, manager_lock, callback);
        }
    }

    pub fn next_song(
        mediadata: &Arc<MediaData>,
        channel_id: ChannelId,
        manager_lock: &Arc<
            serenity::prelude::Mutex<serenity::client::bridge::voice::ClientVoiceManager>,
        >,
        callback: Arc<Fn() -> ()>,
    ) {
        let mut manager = manager_lock.lock();

        let guild_id = match guild_id(channel_id) {
            Some(id) => id,
            None => {
                println!("error getting guild_id");
                return;
            }
        };

        let mut need_to_leave = false;

        {
            let handler = {
                match handler(guild_id, &mut manager) {
                    Some(handler) => handler,
                    None => {
                        println!("error getting handler");
                        return;
                    }
                }
            };

            let playlist_lock = match mediadata.playlist_mut() {
                Ok(playlist) => playlist,
                Err(msg) => {
                    println!("{}", msg);
                    return;
                }
            };
            let mut playlist = playlist_lock.borrow_mut();

            let song_name_lock = match mediadata.name_mut() {
                Ok(name) => name,
                Err(msg) => {
                    println!("{}", msg);
                    return;
                }
            };
            let mut name = song_name_lock.borrow_mut();

            let song_lock = match mediadata.song_mut() {
                Ok(song) => song,
                Err(msg) => {
                    println!("{}", msg);
                    return;
                }
            };
            let mut song = song_lock.borrow_mut();

            if playlist.is_empty() {
                need_to_leave = true;
                handler.stop();
                *song = None;
                *name = String::new();
            } else {
                handler.stop();

                let first = playlist.remove(0);

                let source = match ffmpeg(first.name.clone()) {
                    Ok(mpeg) => mpeg,
                    Err(_) => {
                        playlist.clear();

                        return;
                    }
                };

                *song = Some(handler.play_returning_and_callback(source, callback));
                *name = first.name.clone();

                print_error!(channel_id.say(format!("Playing song: {}", first.name)));
            }
        }

        if need_to_leave {
            manager.remove(guild_id);
        }
    }
}

unsafe impl Send for MediaData {}
unsafe impl Sync for MediaData {}