/*
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 dbus::ffidisp::Connection;
use mpris::{FindingError, PlaybackStatus, Player, PlayerFinder, ProgressTick};
use serde::Serialize;
use std::{
    path::PathBuf,
    sync::mpsc::{channel, Receiver, Sender, TryRecvError},
    thread,
    time::Duration,
};
use thiserror::Error;

pub enum Message {
    Quit,
}

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

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

pub fn list_devices(conn: Connection) -> Result<Vec<DeviceInformation>, FindingError> {
    Ok(PlayerFinder::for_connection(conn)
        .find_all()?
        .into_iter()
        .map(|player| DeviceInformation {
            address: player.unique_name().to_owned(),
            name: player.identity().to_owned(),
        })
        .collect())
}

#[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("Unknown dbus error")]
    DbusError(dbus::Error),

    #[error("Unknown problem with mpris")]
    MprisError(mpris::DBusError),

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

impl From<dbus::Error> for AudioError {
    fn from(err: dbus::Error) -> Self {
        Self::DbusError(err)
    }
}

impl From<mpris::DBusError> for AudioError {
    fn from(err: mpris::DBusError) -> Self {
        Self::MprisError(err)
    }
}

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 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)]
pub struct TrackInfo {
    pub track_number: Option<i32>,
    pub name: Option<String>,
    pub album: Option<String>,
    pub artist: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Track {
    pub id: TrackId,
    pub track_number: Option<i32>,
    pub name: Option<String>,
    pub album: Option<String>,
    pub artist: Option<String>,
}

/*
impl From<&mpris::Metadata> for Track {
    fn from(data: &mpris::Metadata) -> Self {
        Self {
            id: data.track_id().unwrap(),
            track_number: data.track_number(),
            name: data.title().map(|s| s.to_owned()),
            album: data.album_name().map(|s| s.to_owned()),
            artist: None,
        }
    }
}

impl From<mpris::Metadata> for Track {
    fn from(data: mpris::Metadata) -> Self {
        Self::from(&data)
    }
}
*/

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

pub struct CurrentlyPlaying {
    track: Track,
    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)
    }
}
*/