{-# LANGUAGE OverloadedStrings #-}

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

Maintainer  : joachifm@fastmail.fm
Stability   : stable
Portability : unportable

The current playlist.
-}

module Network.MPD.Applicative.CurrentPlaylist
    ( add
    , addId
    , clear
    , delete
    , deleteRange
    , deleteId
    , move
    , moveId
    , moveRange
    , playlistFind
    , playlistInfo
    , playlistInfoRange
    , playlistId
    , playlistSearch
    , plChanges
    , plChangesPosId
    , prio
    , prioId
    , shuffle
    , swap
    , swapId
    , addTagId
    , clearTagId
    , rangeId
    ) where

import           Network.MPD.Commands.Arg hiding (Command)
import qualified Network.MPD.Commands.Arg as Arg
import           Network.MPD.Util
import           Network.MPD.Commands.Query
import           Network.MPD.Commands.Parse
import           Network.MPD.Commands.Types
import           Network.MPD.Applicative.Internal
import           Network.MPD.Applicative.Util

-- | Add a song (or a whole directory) to the current playlist.
add :: Path -> Command ()
add :: Path -> Command ()
add Path
path = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"add" Command -> Path -> String
forall a. MPDArg a => Command -> a -> String
<@> Path
path]

-- | Add a song (non-recursively) and return its id.
addId :: Path -> Maybe Position -> Command Id
addId :: Path -> Maybe Position -> Command Id
addId Path
path Maybe Position
pos = Parser Id -> [String] -> Command Id
forall a. Parser a -> [String] -> Command a
Command Parser Id
p [String]
c
  where
    c :: [String]
c = [Command
"addid" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Path
path Path -> Maybe Position -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Maybe Position
pos]
    p :: Parser Id
p = do
      [ByteString]
r <- Parser [ByteString]
getResponse
      case [ByteString] -> [(ByteString, ByteString)]
toAssocList [ByteString]
r of
        [(ByteString
"Id", ByteString
n)] -> Parser Id -> (Position -> Parser Id) -> Maybe Position -> Parser Id
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([ByteString] -> Parser Id
forall a. [ByteString] -> Parser a
unexpected [ByteString]
r) (Id -> Parser Id
forall (m :: * -> *) a. Monad m => a -> m a
return (Id -> Parser Id) -> (Position -> Id) -> Position -> Parser Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Position -> Id
Id) (ByteString -> Maybe Position
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum ByteString
n)
        [(ByteString, ByteString)]
_         -> [ByteString] -> Parser Id
forall a. [ByteString] -> Parser a
unexpected [ByteString]
r

-- | Clear the current playlist.
clear :: Command ()
clear :: Command ()
clear = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [String
"clear"]

-- | Delete song at the given playlist position.
delete :: Position -> Command ()
delete :: Position -> Command ()
delete Position
pos = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"delete" Command -> Position -> String
forall a. MPDArg a => Command -> a -> String
<@> Position
pos]

-- | Delete a range of songs from the playlist.
--
-- @since 0.10.0.0
deleteRange :: Range -> Command ()
deleteRange :: Range -> Command ()
deleteRange Range
range = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"delete" Command -> Range -> String
forall a. MPDArg a => Command -> a -> String
<@> Range
range]

-- | Delete song by id.
deleteId :: Id -> Command ()
deleteId :: Id -> Command ()
deleteId Id
i = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"deleteid" Command -> Id -> String
forall a. MPDArg a => Command -> a -> String
<@> Id
i]

-- | Move song from one position to another.
move :: Position -> Position -> Command ()
move :: Position -> Position -> Command ()
move Position
pos Position
to = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"move" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Position
pos Position -> Position -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Position
to]

-- | Move a range of songs.
--
-- @since 0.10.0.0
moveRange :: Range -> Position -> Command ()
moveRange :: Range -> Position -> Command ()
moveRange Range
range Position
to = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"move" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Range
range Range -> Position -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Position
to]

-- | Move song id to position.
-- If the position is negative, it is relative to the current song.
moveId :: Id -> Position -> Command ()
moveId :: Id -> Position -> Command ()
moveId Id
i Position
to = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"moveid" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Id
i Id -> Position -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Position
to]

-- Note: 'playlist' deliberately not defined here

-- Internal helper for playlist* commands
playlist' :: MPDArg a => Arg.Command -> a -> Command [Song]
playlist' :: Command -> a -> Command [Song]
playlist' Command
cmd a
q = Parser [Song] -> [String] -> Command [Song]
forall a. Parser a -> [String] -> Command a
Command (([ByteString] -> Either String [Song]) -> Parser [Song]
forall a. ([ByteString] -> Either String a) -> Parser a
liftParser [ByteString] -> Either String [Song]
takeSongs) [Command
cmd Command -> a -> String
forall a. MPDArg a => Command -> a -> String
<@> a
q]

-- | Find songs in current playlist with strict matching.
playlistFind :: Query -> Command [Song]
playlistFind :: Query -> Command [Song]
playlistFind = Command -> Query -> Command [Song]
forall a. MPDArg a => Command -> a -> Command [Song]
playlist' Command
"playlistfind"

-- | Get song metadata for all items in the current playlist.
-- Optionally restrict listing the song at the given position.
playlistInfo :: Maybe Position -> Command [Song]
playlistInfo :: Maybe Position -> Command [Song]
playlistInfo = Command -> Maybe Position -> Command [Song]
forall a. MPDArg a => Command -> a -> Command [Song]
playlist' Command
"playlistinfo"

-- | Like 'playlistInfo' but can restrict listing to a range
-- of songs.
--
-- @since 0.10.0.0
playlistInfoRange :: Maybe Range -> Command [Song]
playlistInfoRange :: Maybe Range -> Command [Song]
playlistInfoRange = Command -> Maybe Range -> Command [Song]
forall a. MPDArg a => Command -> a -> Command [Song]
playlist' Command
"playlistinfo"

-- | Get song metadata for all items in the current playlist.
-- Optionally restrict selection to a single song id.
playlistId :: Maybe Id -> Command [Song]
playlistId :: Maybe Id -> Command [Song]
playlistId = Command -> Maybe Id -> Command [Song]
forall a. MPDArg a => Command -> a -> Command [Song]
playlist' Command
"playlistid"

-- | Search case-insensitively for partial matches in current playlist.
playlistSearch :: Query -> Command [Song]
playlistSearch :: Query -> Command [Song]
playlistSearch = Command -> Query -> Command [Song]
forall a. MPDArg a => Command -> a -> Command [Song]
playlist' Command
"playlistsearch"

-- | Get song metadata for items that have changed in the playlist since
-- the given playlist version.
plChanges :: Integer -> Command [Song]
plChanges :: Integer -> Command [Song]
plChanges = Command -> Integer -> Command [Song]
forall a. MPDArg a => Command -> a -> Command [Song]
playlist' Command
"plchanges"

-- | Get positions and ids of songs that have changed in the playlist
-- since the given playlist version.
plChangesPosId :: Integer -> Command [(Position, Id)]
plChangesPosId :: Integer -> Command [(Position, Id)]
plChangesPosId Integer
ver = Parser [(Position, Id)] -> [String] -> Command [(Position, Id)]
forall a. Parser a -> [String] -> Command a
Command Parser [(Position, Id)]
p [Command
"plchangesposid" Command -> Integer -> String
forall a. MPDArg a => Command -> a -> String
<@> Integer
ver]
    where
        -- XXX: possibly suboptimal definition
        p :: Parser [(Position, Id)]
        p :: Parser [(Position, Id)]
p = ([ByteString] -> Either String [(Position, Id)])
-> Parser [(Position, Id)]
forall a. ([ByteString] -> Either String a) -> Parser a
liftParser (([ByteString] -> Either String [(Position, Id)])
 -> Parser [(Position, Id)])
-> ([ByteString] -> Either String [(Position, Id)])
-> Parser [(Position, Id)]
forall a b. (a -> b) -> a -> b
$ ([(ByteString, ByteString)] -> Either String (Position, Id))
-> [[(ByteString, ByteString)]] -> Either String [(Position, Id)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM [(ByteString, ByteString)] -> Either String (Position, Id)
forall a a.
(Eq a, IsString a, IsString a) =>
[(a, ByteString)] -> Either a (Position, Id)
f ([[(ByteString, ByteString)]] -> Either String [(Position, Id)])
-> ([ByteString] -> [[(ByteString, ByteString)]])
-> [ByteString]
-> Either String [(Position, Id)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString]
-> [(ByteString, ByteString)] -> [[(ByteString, ByteString)]]
splitGroups [ByteString
"cpos"] ([(ByteString, ByteString)] -> [[(ByteString, ByteString)]])
-> ([ByteString] -> [(ByteString, ByteString)])
-> [ByteString]
-> [[(ByteString, ByteString)]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [(ByteString, ByteString)]
toAssocList
        f :: [(a, ByteString)] -> Either a (Position, Id)
f [(a, ByteString)]
xs | [(a
"cpos", ByteString
x), (a
"Id", ByteString
y)] <- [(a, ByteString)]
xs
             , Just (Position
x', Position
y') <- (ByteString -> Maybe Position)
-> (ByteString, ByteString) -> Maybe (Position, Position)
forall a.
(ByteString -> Maybe a) -> (ByteString, ByteString) -> Maybe (a, a)
pair ByteString -> Maybe Position
forall a. (Read a, Integral a) => ByteString -> Maybe a
parseNum (ByteString
x, ByteString
y)
             = (Position, Id) -> Either a (Position, Id)
forall a b. b -> Either a b
Right (Position
x', Position -> Id
Id Position
y')
             | Bool
otherwise = a -> Either a (Position, Id)
forall a b. a -> Either a b
Left a
""

-- | Set the priority of the specified songs.
--
-- @since 0.10.0.0
prio :: Priority -> Range -> Command ()
prio :: Priority -> Range -> Command ()
prio Priority
p Range
range = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"prio" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Priority
p Priority -> Range -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Range
range]

-- | Set priority by song id.
prioId :: Priority -> Id -> Command ()
prioId :: Priority -> Id -> Command ()
prioId Priority
p Id
ids = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"prioid" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Priority
p Priority -> Id -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Id
ids]

-- | Shuffle the current playlist.
-- Optionally restrict to a range of songs.
--
-- @since 0.10.0.0
shuffle :: Maybe Range -> Command ()
shuffle :: Maybe Range -> Command ()
shuffle Maybe Range
mbRange = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"shuffle" Command -> Maybe Range -> String
forall a. MPDArg a => Command -> a -> String
<@> Maybe Range
mbRange]

-- | Swap songs by position.
swap :: Position -> Position -> Command ()
swap :: Position -> Position -> Command ()
swap Position
pos1 Position
pos2 = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"swap" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Position
pos1 Position -> Position -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Position
pos2]

-- | Swap songs by id.
swapId :: Id -> Id -> Command ()
swapId :: Id -> Id -> Command ()
swapId Id
id1 Id
id2 = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"swapid" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Id
id1 Id -> Id -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Id
id2]

-- | Add tag to specified (remote) song.
addTagId :: Id -> Metadata -> Value -> Command ()
addTagId :: Id -> Metadata -> Value -> Command ()
addTagId Id
id' Metadata
tag Value
val = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"addtagid" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Id
id' Id -> Metadata -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Metadata
tag Args -> Value -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Value
val]

-- | Remove tag from specified (remote) song.
clearTagId :: Id -> Metadata -> Command ()
clearTagId :: Id -> Metadata -> Command ()
clearTagId Id
id' Metadata
tags = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [Command
"cleartagid" Command -> Args -> String
forall a. MPDArg a => Command -> a -> String
<@> Id
id' Id -> Metadata -> Args
forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
<++> Metadata
tags]

-- | Specify portion of song that shall be played.
-- Both ends of the range are optional; omitting both plays everything.
--
-- Since MPD 0.19.
rangeId :: Id -> (Maybe Double, Maybe Double) -> Command ()
rangeId :: Id -> (Maybe Double, Maybe Double) -> Command ()
rangeId Id
id' (Maybe Double
mbStart, Maybe Double
mbEnd) = Parser () -> [String] -> Command ()
forall a. Parser a -> [String] -> Command a
Command Parser ()
emptyResponse [String
"rangeid " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Id -> String
forall a. Show a => a -> String
show Id
id' String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
arg ]
  where arg :: String
arg = String -> (Double -> String) -> Maybe Double -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"" Double -> String
forall a. Show a => a -> String
show Maybe Double
mbStart String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> (Double -> String) -> Maybe Double -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"" Double -> String
forall a. Show a => a -> String
show Maybe Double
mbEnd