    {-# LANGUAGE OverloadedStrings #-}

    {- |
    Module      : Network.MPD.Commands.Extensions
    Copyright   : (c) Joachim Fasting 2012
    License     : MIT

    Maintainer  : Joachim Fasting <joachifm@fastmail.fm>
    Stability   : unstable
    Portability : unportable

    Extensions and shortcuts to the standard MPD command set.
    -}

    module Network.MPD.Commands.Extensions where

    import           Network.MPD.Core
    import           Network.MPD.Commands
    import qualified Network.MPD.Applicative.Internal as A
    import qualified Network.MPD.Applicative.CurrentPlaylist as A
    import qualified Network.MPD.Applicative.StoredPlaylists as A

    import           Control.Monad (liftM)
    import           Data.Traversable (for)
    import           Data.Foldable (for_)

    -- | Add a list of songs\/folders to a playlist.
    -- Should be more efficient than running 'add' many times.
    addMany :: MonadMPD m => PlaylistName -> [Path] -> m ()
    addMany :: forall (m :: * -> *). MonadMPD m => PlaylistName -> [Path] -> m ()
    addMany PlaylistName
    plname [Path]
    xs = Command () -> m ()
    forall (m :: * -> *) a. MonadMPD m => Command a -> m a
    A.runCommand ([Path] -> (Path -> Command ()) -> Command ()
    forall (t :: * -> *) (f :: * -> *) a b.
    (Foldable t, Applicative f) =>
    t a -> (a -> f b) -> f ()
    for_ [Path]
    xs Path -> Command ()
    cmd)
        where cmd :: Path -> Command ()
    cmd | PlaylistName
    plname PlaylistName -> PlaylistName -> Bool
    forall a. Eq a => a -> a -> Bool
    == PlaylistName
    "" = Path -> Command ()
    A.add
                  | Bool
    otherwise    = PlaylistName -> Path -> Command ()
    A.playlistAdd PlaylistName
    plname

    -- | Recursive 'addId'. For directories, it will use the given position
    -- for the first file in the directory and use the successor for the remaining
    -- files. It returns a list of playlist ids for the songs added.
    addIdMany :: MonadMPD m => Path -> Maybe Position -> m [Id]
    addIdMany :: forall (m :: * -> *).
    MonadMPD m =>
    Path -> Maybe Position -> m [Id]
    addIdMany Path
    x (Just Position
    p) = do
        fs <- Path -> m [Path]
    forall (m :: * -> *). MonadMPD m => Path -> m [Path]
    listAll Path
    x
        let fs' = ((Path, Position) -> (Path, Maybe Position))
    -> [(Path, Position)] -> [(Path, Maybe Position)]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(Path
    a, Position
    b) -> (Path
    a, Position -> Maybe Position
    forall a. a -> Maybe a
    Just Position
    b)) ([(Path, Position)] -> [(Path, Maybe Position)])
    -> [(Path, Position)] -> [(Path, Maybe Position)]
    forall a b. (a -> b) -> a -> b
    $ [Path] -> [Position] -> [(Path, Position)]
    forall a b. [a] -> [b] -> [(a, b)]
    zip [Path]
    fs [Position
    p ..]
        A.runCommand $ for fs' (uncurry A.addId)
    addIdMany Path
    x Maybe Position
    Nothing = do
        fs <- Path -> m [Path]
    forall (m :: * -> *). MonadMPD m => Path -> m [Path]
    listAll Path
    x
        A.runCommand $ for fs (`A.addId` Nothing)

    {-
    -- | Returns all songs and directories that match the given partial
    -- path name.
    complete :: MonadMPD m => String -> m [Either Path Song]
    complete path = do
        xs <- liftM matches . lsInfo $ dropFileName path
        case xs of
            [Left dir] -> complete $ dir ++ "/"
            _          -> return xs
        where
            matches = filter (isPrefixOf path . takePath)
            takePath = either id sgFilePath
    -}

    -- | List the artists in the database.
    listArtists :: MonadMPD m => m [Artist]
    listArtists :: forall (m :: * -> *). MonadMPD m => m [Artist]
    listArtists = Metadata -> Query -> m [Artist]
    forall (m :: * -> *). MonadMPD m => Metadata -> Query -> m [Artist]
    list Metadata
    Artist Query
    forall a. Monoid a => a
    mempty

    -- | List the albums in the database, optionally matching a given
    -- artist.
    listAlbums :: MonadMPD m => Maybe Artist -> m [Album]
    listAlbums :: forall (m :: * -> *). MonadMPD m => Maybe Artist -> m [Artist]
    listAlbums Maybe Artist
    ma = Metadata -> Query -> m [Artist]
    forall (m :: * -> *). MonadMPD m => Metadata -> Query -> m [Artist]
    list Metadata
    Album (case Maybe Artist
    ma of
                                  Maybe Artist
    Nothing -> Query
    forall a. Monoid a => a
    mempty
                                  Just Artist
    a -> Metadata
    Artist Metadata -> Artist -> Query
    =? Artist
    a)

    -- | List the songs in an album of some artist.
    listAlbum :: MonadMPD m => Artist -> Album -> m [Song]
    listAlbum :: forall (m :: * -> *). MonadMPD m => Artist -> Artist -> m [Song]
    listAlbum Artist
    artist Artist
    album = Query -> m [Song]
    forall (m :: * -> *). MonadMPD m => Query -> m [Song]
    find (Metadata
    Artist Metadata -> Artist -> Query
    =? Artist
    artist Query -> Query -> Query
    forall a. Semigroup a => a -> a -> a
    <> Metadata
    Album Metadata -> Artist -> Query
    =? Artist
    album)

    -- | Retrieve the current playlist.
    -- Equivalent to @playlistinfo Nothing@.
    getPlaylist :: MonadMPD m => m [Song]
    getPlaylist :: forall (m :: * -> *). MonadMPD m => m [Song]
    getPlaylist = Maybe Position -> m [Song]
    forall (m :: * -> *). MonadMPD m => Maybe Position -> m [Song]
    playlistInfo Maybe Position
    forall a. Maybe a
    Nothing

    -- | Increase or decrease volume by a given percent, e.g.
    -- 'volume 10' will increase the volume by 10 percent, while
    -- 'volume (-10)' will decrease it by the same amount.
    volume :: MonadMPD m => Int -> m ()
    volume :: forall (m :: * -> *). MonadMPD m => Position -> m ()
    volume Position
    n = do
        cur <- Status -> Maybe Volume
    stVolume (Status -> Maybe Volume) -> m Status -> m (Maybe Volume)
    forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
    `liftM` m Status
    forall (m :: * -> *). MonadMPD m => m Status
    status
        case cur of
            Maybe Volume
    Nothing -> () -> m ()
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
            Just Volume
    v  -> Volume -> m ()
    forall (m :: * -> *). MonadMPD m => Volume -> m ()
    setVolume (Volume -> Volume
    forall {b} {p}. (Integral b, Integral p) => p -> b
    adjust Volume
    v)
        where
            adjust :: p -> b
    adjust p
    x = Double -> b
    forall b. Integral b => Double -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    round (Double -> b) -> Double -> b
    forall a b. (a -> b) -> a -> b
    $ (Position -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Position
    n Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / (Double
    100 :: Double)) Double -> Double -> Double
    forall a. Num a => a -> a -> a
    * Double
    x' Double -> Double -> Double
    forall a. Num a => a -> a -> a
    + Double
    x'
              where x' :: Double
    x' = p -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral p
    x