/*
Copyright 2023, Savanni D'Gerinel <savanni@luminescent-dreams.com>

This file is part of the Luminescent Dreams Tools.

Luminescent Dreams Tools is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

Luminescent Dreams Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with Lumeto. If not, see <https://www.gnu.org/licenses/>.
*/

use serde::{Serialize, Serializer};
use std::time::Duration;
use thiserror::Error;

pub enum Message {
    Quit,
}

#[derive(Clone, Debug)]
pub enum Event {
    Paused(TrackId, Duration),
    Playing(TrackId, Duration),
    Stopped,
    Position(TrackId, Duration),
}

#[derive(Debug)]
pub struct DeviceInformation {
    pub address: String,
    pub name: String,
}

#[derive(Debug, Error)]
pub enum AudioError {
    #[error("DBus device was not found")]
    DeviceNotFound,

    #[error("DBus connection lost or otherwise failed")]
    ConnectionLost,

    #[error("Specified media cannot be found")]
    MediaNotFound,

    #[error("Play was ordered, but nothing is in the queue")]
    NothingInQueue,

    #[error("url parse error {0}")]
    UrlError(url::ParseError),
}

impl From<url::ParseError> for AudioError {
    fn from(err: url::ParseError) -> Self {
        Self::UrlError(err)
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
pub struct TrackId(String);

impl TrackId {
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl Default for TrackId {
    fn default() -> Self {
        Self(uuid::Uuid::new_v4().as_hyphenated().to_string())
    }
}

impl From<String> for TrackId {
    fn from(id: String) -> Self {
        Self(id)
    }
}

impl AsRef<String> for TrackId {
    fn as_ref<'a>(&'a self) -> &'a String {
        &self.0
    }
}

#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct TrackInfo {
    pub id: TrackId,
    pub track_number: Option<i32>,
    pub name: Option<String>,
    pub album: Option<String>,
    pub artist: Option<String>,
    pub duration: Option<u32>,
    #[serde(serialize_with = "serialize_mime")]
    pub filetype: mime::Mime,
}

fn serialize_mime<S>(val: &mime::Mime, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    s.serialize_str(val.essence_str())
}

#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum State {
    Playing(TrackInfo),
    Paused(TrackInfo),
    Stopped,
}

/*
pub struct CurrentlyPlaying {
    track: TrackInfo,
    position: Duration,
}
*/

#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Capabilities {
    pub can_control: bool,
    pub can_pause: bool,
    pub can_play: bool,
    pub supports_track_lists: bool,
}

pub trait AudioPlayer {
    fn capabilities(&self) -> Result<Capabilities, AudioError>;
    fn state(&self) -> Result<State, AudioError>;
    fn play(&self, trackid: url::Url) -> Result<State, AudioError>;
    fn play_pause(&self) -> Result<State, AudioError>;
}

/*
pub struct GStreamerPlayer {
    url: url::Url,
}

impl AudioPlayer for GStreamerPlayer {
    fn capabilities(&self) -> Result<Capabilities, AudioError> {
        unimplemented!()
    }

    fn state(&self) -> Result<State, AudioError> {
        unimplemented!()
    }

    fn play(&self, trackid: url::Url) -> Result<State, AudioError> {
        unimplemented!()
    }

    fn play_pause(&self) -> Result<State, AudioError> {
        unimplemented!()
    }
}
*/

/*
pub struct MprisDevice {
    device_id: String,
    player: Player,
    state: State,
}

impl MprisDevice {
    pub fn new(device_id: String) -> Result<MprisDevice, AudioError> {
        let connection = Connection::new_session()?;
        Ok(MprisDevice {
            device_id: device_id.clone(),
            player: mpris::Player::new(connection, device_id, 1000)?,
            state: State::Stopped,
        })
    }

    pub fn monitor(&mut self, control_channel: Receiver<Message>) {
        let (tx, rx) = channel();
        {
            let device_id = self.device_id.clone();
            let tx = tx.clone();
            thread::spawn(move || {
                MprisDevice::new(device_id)
                    .expect("connect to bus")
                    .monitor_progress(tx);
            });
        };

        loop {
            match control_channel.try_recv() {
                Ok(Message::Quit) => return,
                Err(TryRecvError::Empty) => {}
                Err(TryRecvError::Disconnected) => return,
            }
            let event = rx.recv().expect("receive should never fail");
            println!("event received: {:?}", event);
        }
    }

    pub fn monitor_progress(&self, tx: Sender<Event>) {
        let mut tracker = self
            .player
            .track_progress(1000)
            .expect("can get an event stream");
        loop {
            let ProgressTick { progress, .. } = tracker.tick();
            match progress.playback_status() {
                PlaybackStatus::Playing => {
                    tx.send(Event::Playing(
                        Track::from(progress.metadata()),
                        progress.position(),
                    ))
                    .expect("send to succeed");
                }
                PlaybackStatus::Paused => {
                    tx.send(Event::Paused(
                        Track::from(progress.metadata()),
                        progress.position(),
                    ))
                    .expect("send to succeed");
                }
                PlaybackStatus::Stopped => {
                    tx.send(Event::Stopped).expect("send to succeed");
                }
            }
        }
    }
}

impl AudioPlayer for MprisDevice {
    fn capabilities(&self) -> Result<Capabilities, AudioError> {
        Ok(Capabilities {
            can_control: self.player.can_control()?,
            can_pause: self.player.can_pause()?,
            can_play: self.player.can_play()?,
            supports_track_lists: self.player.supports_track_lists(),
        })
    }

    fn state(&self) -> Result<State, AudioError> {
        println!(
            "supports track lists: {:?}",
            self.player.supports_track_lists()
        );
        let metadata = self.player.get_metadata()?;
        println!("{:?}", metadata);
        unimplemented!("AudioPlayer state")
    }

    fn play(&self, track_id: String) -> Result<State, AudioError> {
        println!("playing: {}", track_id);
        self.player
            .go_to(&mpris::TrackID::from(dbus::Path::from(track_id)))?;
        self.player.play();
        Ok(State::Stopped)
    }

    fn play_pause(&self) -> Result<State, AudioError> {
        self.player.play_pause()?;
        Ok(State::Stopped)
    }
}
*/
