{-# 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
            [ByteString]
r <- Parser [ByteString]
getResponse
            case [ByteString] -> [(ByteString, ByteString)]
toAssocList [ByteString]
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 (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