    {-# LANGUAGE OverloadedStrings #-}

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

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

    The music database.
    -}

    module Network.MPD.Applicative.Database
        ( count
        , find
        , findAdd
        , list
        , listAll
        , listAllInfo
        , lsInfo
        , readComments
        , search
        , searchAdd
        , searchAddPl
        , update
        , rescan
        ) where

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

    -- | Get a count of songs and their total playtime that exactly match the
    -- query.
    count :: Query -> Command Count
    count :: Query -> Command Count
    count Query
    q = Parser Count -> [String] -> Command Count
    forall a. Parser a -> [String] -> Command a
    Command (([ByteString] -> Either String Count) -> Parser Count
    forall a. ([ByteString] -> Either String a) -> Parser a
    liftParser [ByteString] -> Either String Count
    parseCount) [Command
    "count" Command -> Query -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Query
    q]

    -- | Find songs matching the query exactly.
    find :: Query -> Command [Song]
    find :: Query -> Command [Song]
    find Query
    q = Parser [Song] -> [String] -> Command [Song]
    forall a. Parser a -> [String] -> Command a
    Command Parser [Song]
    p [Command
    "find" Command -> Query -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Query
    q]
        where
            p :: Parser [Song]
            p :: Parser [Song]
    p = ([ByteString] -> Either String [Song]) -> Parser [Song]
    forall a. ([ByteString] -> Either String a) -> Parser a
    liftParser [ByteString] -> Either String [Song]
    takeSongs

    -- | Like 'find' but adds the results to the current playlist.
    findAdd :: Query -> Command ()
    findAdd :: Query -> Command ()
    findAdd Query
    q = Parser () -> [String] -> Command ()
    forall a. Parser a -> [String] -> Command a
    Command Parser ()
    emptyResponse [Command
    "findadd" Command -> Query -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Query
    q]

    -- | List all tags of the specified type of songs that that satisfy the query.
    --
    -- @since 0.10.0.0
    list :: Metadata -> Query -> Command [Value]
    list :: Metadata -> Query -> Command [Value]
    list Metadata
    m Query
    q = Parser [Value] -> [String] -> Command [Value]
    forall a. Parser a -> [String] -> Command a
    Command Parser [Value]
    p [String]
    c
        where
            p :: Parser [Value]
    p = (ByteString -> Value) -> [ByteString] -> [Value]
    forall a b. (a -> b) -> [a] -> [b]
    map ByteString -> Value
    Value ([ByteString] -> [Value])
    -> ([ByteString] -> [ByteString]) -> [ByteString] -> [Value]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [ByteString] -> [ByteString]
    takeValues ([ByteString] -> [Value]) -> Parser [ByteString] -> Parser [Value]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Parser [ByteString]
    getResponse
            c :: [String]
    c = [Command
    "list" Command -> Args -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Metadata
    m Metadata -> Query -> Args
    forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
    <++> Query
    q]

    -- | List all songs and directories in a database path.
    listAll :: Path -> Command [Path]
    listAll :: Path -> Command [Path]
    listAll Path
    path = Parser [Path] -> [String] -> Command [Path]
    forall a. Parser a -> [String] -> Command a
    Command Parser [Path]
    p [Command
    "listall" Command -> Path -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Path
    path]
        where
            p :: Parser [Path]
            p :: Parser [Path]
    p = ((ByteString, ByteString) -> Path)
    -> [(ByteString, ByteString)] -> [Path]
    forall a b. (a -> b) -> [a] -> [b]
    map (ByteString -> Path
    Path (ByteString -> Path)
    -> ((ByteString, ByteString) -> ByteString)
    -> (ByteString, ByteString)
    -> Path
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ByteString, ByteString) -> ByteString
    forall a b. (a, b) -> b
    snd) ([(ByteString, ByteString)] -> [Path])
    -> ([ByteString] -> [(ByteString, ByteString)])
    -> [ByteString]
    -> [Path]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((ByteString, ByteString) -> Bool)
    -> [(ByteString, ByteString)] -> [(ByteString, ByteString)]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ((ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == ByteString
    "file") (ByteString -> Bool)
    -> ((ByteString, ByteString) -> ByteString)
    -> (ByteString, ByteString)
    -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ByteString, ByteString) -> ByteString
    forall a b. (a, b) -> a
    fst)
                ([(ByteString, ByteString)] -> [(ByteString, ByteString)])
    -> ([ByteString] -> [(ByteString, ByteString)])
    -> [ByteString]
    -> [(ByteString, ByteString)]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [ByteString] -> [(ByteString, ByteString)]
    toAssocList ([ByteString] -> [Path]) -> Parser [ByteString] -> Parser [Path]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Parser [ByteString]
    getResponse

    -- Internal helper
    lsInfo' :: Arg.Command -> Path -> Command [LsResult]
    lsInfo' :: Command -> Path -> Command [LsResult]
    lsInfo' Command
    cmd Path
    path = Parser [LsResult] -> [String] -> Command [LsResult]
    forall a. Parser a -> [String] -> Command a
    Command Parser [LsResult]
    p [Command
    cmd Command -> Path -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Path
    path]
        where
            p :: Parser [LsResult]
            p :: Parser [LsResult]
    p = ([ByteString] -> Either String [LsResult]) -> Parser [LsResult]
    forall a. ([ByteString] -> Either String a) -> Parser a
    liftParser [ByteString] -> Either String [LsResult]
    takeEntries

    -- | Same as 'listAll' but also returns metadata.
    listAllInfo :: Path -> Command [LsResult]
    listAllInfo :: Path -> Command [LsResult]
    listAllInfo = Command -> Path -> Command [LsResult]
    lsInfo' Command
    "listallinfo"

    -- | List the contents of a database directory.
    lsInfo :: Path -> Command [LsResult]
    lsInfo :: Path -> Command [LsResult]
    lsInfo = Command -> Path -> Command [LsResult]
    lsInfo' Command
    "lsinfo"

    -- | Read comments from the file at the specified path.
    readComments :: Path -> Command [(String, String)]
    readComments :: Path -> Command [(String, String)]
    readComments Path
    uri = Parser [(String, String)] -> [String] -> Command [(String, String)]
    forall a. Parser a -> [String] -> Command a
    Command Parser [(String, String)]
    p [Command
    "readcomments" Command -> Path -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Path
    uri]
      where p :: Parser [(String, String)]
    p = ((ByteString, ByteString) -> (String, String))
    -> [(ByteString, ByteString)] -> [(String, String)]
    forall a b. (a -> b) -> [a] -> [b]
    map (ByteString, ByteString) -> (String, String)
    decodePair ([(ByteString, ByteString)] -> [(String, String)])
    -> ([ByteString] -> [(ByteString, ByteString)])
    -> [ByteString]
    -> [(String, String)]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [ByteString] -> [(ByteString, ByteString)]
    toAssocList ([ByteString] -> [(String, String)])
    -> Parser [ByteString] -> Parser [(String, String)]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Parser [ByteString]
    getResponse

    -- | Like 'find' but with case insensitive matching.
    search :: Query -> Command [Song]
    search :: Query -> Command [Song]
    search Query
    q = Parser [Song] -> [String] -> Command [Song]
    forall a. Parser a -> [String] -> Command a
    Command Parser [Song]
    p [Command
    "search" Command -> Query -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Query
    q]
        where
            p :: Parser [Song]
            p :: Parser [Song]
    p = ([ByteString] -> Either String [Song]) -> Parser [Song]
    forall a. ([ByteString] -> Either String a) -> Parser a
    liftParser [ByteString] -> Either String [Song]
    takeSongs

    -- | Like 'search' but adds the results to the current playlist.
    searchAdd :: Query -> Command ()
    searchAdd :: Query -> Command ()
    searchAdd Query
    q = Parser () -> [String] -> Command ()
    forall a. Parser a -> [String] -> Command a
    Command Parser ()
    emptyResponse [Command
    "searchadd" Command -> Query -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Query
    q]

    -- | Like 'searchAdd' but adds results to the named playlist.
    searchAddPl :: PlaylistName -> Query -> Command ()
    searchAddPl :: PlaylistName -> Query -> Command ()
    searchAddPl PlaylistName
    pl Query
    q = Parser () -> [String] -> Command ()
    forall a. Parser a -> [String] -> Command a
    Command Parser ()
    emptyResponse [Command
    "searchaddpl" Command -> Args -> String
    forall a. MPDArg a => Command -> a -> String
    <@> PlaylistName
    pl PlaylistName -> Query -> Args
    forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
    <++> Query
    q]

    -- | Update the music database.
    -- If no path is supplied, the entire database is updated.
    update :: Maybe Path -> Command Integer
    update :: Maybe Path -> Command Integer
    update = Command -> Maybe Path -> Command Integer
    update_ Command
    "update"

    -- | Like 'update' but also rescan unmodified files.
    rescan :: Maybe Path -> Command Integer
    rescan :: Maybe Path -> Command Integer
    rescan = Command -> Maybe Path -> Command Integer
    update_ Command
    "rescan"

    -- A helper for 'update' and 'rescan.
    update_ :: Arg.Command -> Maybe Path -> Command Integer
    update_ :: Command -> Maybe Path -> Command Integer
    update_ Command
    cmd Maybe Path
    mPath = Parser Integer -> [String] -> Command Integer
    forall a. Parser a -> [String] -> Command a
    Command Parser Integer
    p [Command
    cmd Command -> Maybe Path -> String
    forall a. MPDArg a => Command -> a -> String
    <@> Maybe Path
    mPath]
        where
            p :: Parser Integer
            p :: Parser Integer
    p = do
                r <- Parser [ByteString]
    getResponse
                case toAssocList r of
                    [(ByteString
    "updating_db", ByteString
    id_)] -> Parser Integer
    -> (Integer -> Parser Integer) -> Maybe Integer -> Parser Integer
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe ([ByteString] -> Parser Integer
    forall a. [ByteString] -> Parser a
    unexpected [ByteString]
    r)
                                                    Integer -> Parser Integer
    forall a. a -> Parser a
    forall (m :: * -> *) a. Monad m => a -> m a
    return
                                                    (ByteString -> Maybe Integer
    forall a. (Read a, Integral a) => ByteString -> Maybe a
    parseNum ByteString
    id_)
                    [(ByteString, ByteString)]
    _                      -> [ByteString] -> Parser Integer
    forall a. [ByteString] -> Parser a
    unexpected [ByteString]
    r