    {-# 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "add" Command -> Path -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 -> [[Char]] -> Command Id
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser Id
    p [[Char]]
    c
      where
        c :: [[Char]]
    c = [Command
    "addid" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> Path
    path Path -> Maybe Position -> Args
    forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
    <++> Maybe Position
    pos]
        p :: Parser Id
    p = do
          r <- Parser [ByteString]
    getResponse
          case toAssocList 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 a. a -> Parser a
    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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [[Char]
    "clear"]

    -- | Delete song at the given playlist position.
    delete :: Position -> Command ()
    delete :: Position -> Command ()
    delete Position
    pos = Parser () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "delete" Command -> Position -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "delete" Command -> Range -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> Range
    range]

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

    -- | Move song from one position to another.
    move :: Position -> Position -> Command ()
    move :: Position -> Position -> Command ()
    move Position
    pos Position
    to = Parser () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "move" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "move" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "moveid" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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' :: forall a. MPDArg a => Command -> a -> Command [Song]
    playlist' Command
    cmd a
    q = Parser [Song] -> [[Char]] -> Command [Song]
    forall a. Parser a -> [[Char]] -> Command a
    Command (([ByteString] -> Either [Char] [Song]) -> Parser [Song]
    forall a. ([ByteString] -> Either [Char] a) -> Parser a
    liftParser [ByteString] -> Either [Char] [Song]
    takeSongs) [Command
    cmd Command -> a -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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)] -> [[Char]] -> Command [(Position, Id)]
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser [(Position, Id)]
    p [Command
    "plchangesposid" Command -> Integer -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> Integer
    ver]
        where
            -- XXX: possibly suboptimal definition
            p :: Parser [(Position, Id)]
            p :: Parser [(Position, Id)]
    p = ([ByteString] -> Either [Char] [(Position, Id)])
    -> Parser [(Position, Id)]
    forall a. ([ByteString] -> Either [Char] a) -> Parser a
    liftParser (([ByteString] -> Either [Char] [(Position, Id)])
     -> Parser [(Position, Id)])
    -> ([ByteString] -> Either [Char] [(Position, Id)])
    -> Parser [(Position, Id)]
    forall a b. (a -> b) -> a -> b
    $ ([(ByteString, ByteString)] -> Either [Char] (Position, Id))
    -> [[(ByteString, ByteString)]] -> Either [Char] [(Position, Id)]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
    mapM [(ByteString, ByteString)] -> Either [Char] (Position, Id)
    forall {a} {a}.
    (Eq a, IsString a, IsString a) =>
    [(a, ByteString)] -> Either a (Position, Id)
    f ([[(ByteString, ByteString)]] -> Either [Char] [(Position, Id)])
    -> ([ByteString] -> [[(ByteString, ByteString)]])
    -> [ByteString]
    -> Either [Char] [(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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "prio" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "prioid" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "shuffle" Command -> Maybe Range -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> Maybe Range
    mbRange]

    -- | Swap songs by position.
    swap :: Position -> Position -> Command ()
    swap :: Position -> Position -> Command ()
    swap Position
    pos1 Position
    pos2 = Parser () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "swap" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "swapid" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "addtagid" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [Command
    "cleartagid" Command -> Args -> [Char]
    forall a. MPDArg a => Command -> a -> [Char]
    <@> 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 () -> [[Char]] -> Command ()
    forall a. Parser a -> [[Char]] -> Command a
    Command Parser ()
    emptyResponse [[Char]
    "rangeid " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ Id -> [Char]
    forall a. Show a => a -> [Char]
    show Id
    id' [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    arg ]
      where arg :: [Char]
    arg = [Char] -> (Double -> [Char]) -> Maybe Double -> [Char]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [Char]
    "" Double -> [Char]
    forall a. Show a => a -> [Char]
    show Maybe Double
    mbStart [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    ":" [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char] -> (Double -> [Char]) -> Maybe Double -> [Char]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [Char]
    "" Double -> [Char]
    forall a. Show a => a -> [Char]
    show Maybe Double
    mbEnd