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

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

    Query interface.
    -}

    module Network.MPD.Commands.Query (Query, (=?) ,(/=?), (%?), (~?), (/~?), qNot, qModSince, qFile, qBase, anything) where

    import           Network.MPD.Commands.Arg
    import           Network.MPD.Commands.Types
    import           Data.Time (UTCTime,formatTime,defaultTimeLocale)

    -- | An interface for creating MPD queries.
    --
    -- For example, to match any song where the value of artist is \"Foo\", we
    -- use:
    --
    -- > Artist =? "Foo"
    --
    -- We can also compose queries, thus narrowing the search. For example, to
    -- match any song where the value of artist is \"Foo\" and the value of album
    -- is \"Bar\", we use:
    --
    -- > Artist =? "Foo" <> Album =? "Bar"
    data Query = Query [Match] | Filter Expr
      deriving Int -> Query -> ShowS
    [Query] -> ShowS
    Query -> String
    (Int -> Query -> ShowS)
    -> (Query -> String) -> ([Query] -> ShowS) -> Show Query
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Query -> ShowS
    showsPrec :: Int -> Query -> ShowS
    $cshow :: Query -> String
    show :: Query -> String
    $cshowList :: [Query] -> ShowS
    showList :: [Query] -> ShowS
    Show

    -- A single query clause, comprising a metadata key and a desired value.
    data Match = Match Metadata Value

    instance Show Match where
      show :: Match -> String
    show (Match Metadata
    meta Value
    query) = Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    meta String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " \"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Value -> String
    forall a. ToString a => a -> String
    toString Value
    query String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\""
      showList :: [Match] -> ShowS
    showList [Match]
    xs String
    _ = [String] -> String
    unwords ([String] -> String) -> [String] -> String
    forall a b. (a -> b) -> a -> b
    $ (Match -> String) -> [Match] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Match -> String
    forall a. Show a => a -> String
    show [Match]
    xs

    -- A general MPD 0.21 style filter.
    data Expr = Exact Match
              | ExactNot Match
              | Contains Match
              | Regex Match
              | RegexNot Match
              | File Path
              | Base Path
              | ModifiedSince UTCTime
              | ExprNot Expr
              | ExprAnd Expr Expr
              | ExprEmpty
    instance Show Expr where
     show :: Expr -> String
    show (Exact (Match Metadata
    meta Value
    query)) = String
    "(" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    meta String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " == " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
                                       String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Value -> String
    forall a. ToString a => a -> String
    toString Value
    query String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (ExactNot (Match Metadata
    meta Value
    query)) = String
    "(" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    meta String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " != " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
                                       String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Value -> String
    forall a. ToString a => a -> String
    toString Value
    query String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (Contains (Match Metadata
    meta Value
    query)) = String
    "(" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    meta String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " contains " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
                                       String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Value -> String
    forall a. ToString a => a -> String
    toString Value
    query String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (Regex (Match Metadata
    meta Value
    query)) = String
    "(" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    meta String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " =~ " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
                                       String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Value -> String
    forall a. ToString a => a -> String
    toString Value
    query String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (RegexNot (Match Metadata
    meta Value
    query)) = String
    "(" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    meta String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " !~ " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
                                       String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Value -> String
    forall a. ToString a => a -> String
    toString Value
    query String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (File Path
    file) = String
    "(file == " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Path -> String
    forall a. ToString a => a -> String
    toString Path
    file String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (Base Path
    dir) = String
    "(base " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Path -> String
    forall a. ToString a => a -> String
    toString Path
    dir String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (ModifiedSince UTCTime
    time) = String
    "(modified-since " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++  String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
                               TimeLocale -> String -> UTCTime -> String
    forall t. FormatTime t => TimeLocale -> String -> t -> String
    formatTime TimeLocale
    defaultTimeLocale String
    "%Y-%m-%dT%H:%M:%SZ" UTCTime
    time String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++
                               String
    "\\\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (ExprNot Expr
    expr) = String
    "(!" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Expr -> String
    forall a. Show a => a -> String
    show Expr
    expr String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show (ExprAnd Expr
    e1 Expr
    e2) = String
    "(" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Expr -> String
    forall a. Show a => a -> String
    show Expr
    e1 String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    " AND " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Expr -> String
    forall a. Show a => a -> String
    show Expr
    e2 String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
     show Expr
    ExprEmpty = String
    ""

    toExpr :: [Match] -> Expr
    toExpr :: [Match] -> Expr
    toExpr [] = Expr
    ExprEmpty
    toExpr (Match
    m:[]) = Match -> Expr
    Exact Match
    m
    toExpr (Match
    m:[Match]
    ms) = Expr -> Expr -> Expr
    ExprAnd (Match -> Expr
    Exact Match
    m) ([Match] -> Expr
    toExpr [Match]
    ms)

    instance Monoid Query where
        mempty :: Query
    mempty  = [Match] -> Query
    Query []
        Query  [Match]
    a  mappend :: Query -> Query -> Query
    `mappend` Query    [Match]
    b  = [Match] -> Query
    Query ([Match]
    a [Match] -> [Match] -> [Match]
    forall a. [a] -> [a] -> [a]
    ++ [Match]
    b)
        Query  [] `mappend` Filter   Expr
    b  = Expr -> Query
    Filter Expr
    b
        Filter Expr
    a  `mappend` Query    [] = Expr -> Query
    Filter Expr
    a
        Query  [Match]
    a  `mappend` Filter   Expr
    b  = Expr -> Query
    Filter (Expr -> Expr -> Expr
    ExprAnd ([Match] -> Expr
    toExpr [Match]
    a) Expr
    b)
        Filter Expr
    a  `mappend` Query    [Match]
    b  = Expr -> Query
    Filter (Expr -> Expr -> Expr
    ExprAnd Expr
    a ([Match] -> Expr
    toExpr [Match]
    b))
        Filter Expr
    a  `mappend` Filter   Expr
    b  = Expr -> Query
    Filter (Expr
    a Expr -> Expr -> Expr
    forall a. Semigroup a => a -> a -> a
    <> Expr
    b)

    instance Semigroup Query where
        <> :: Query -> Query -> Query
    (<>) = Query -> Query -> Query
    forall a. Monoid a => a -> a -> a
    mappend
    instance Semigroup Expr where
      Expr
    ex1 <> :: Expr -> Expr -> Expr
    <> Expr
    ex2 = Expr -> Expr -> Expr
    ExprAnd Expr
    ex1 Expr
    ex2

    instance MPDArg Query where
        prep :: Query -> Args
    prep (Query [Match]
    ms) = (Args -> Args -> Args) -> Args -> [Args] -> Args
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl Args -> Args -> Args
    forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
    (<++>) ([String] -> Args
    Args [])
                            ((Match -> Args) -> [Match] -> [Args]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (\(Match Metadata
    m Value
    q) -> [String] -> Args
    Args [Metadata -> String
    forall a. Show a => a -> String
    show Metadata
    m] Args -> Value -> Args
    forall a b. (MPDArg a, MPDArg b) => a -> b -> Args
    <++> Value
    q) [Match]
    ms)
        prep (Filter Expr
    expr) = [String] -> Args
    Args [String
    "\"" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Expr -> String
    forall a. Show a => a -> String
    show Expr
    expr String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\""]

    -- | An empty query. Matches anything.
    anything :: Query
    anything :: Query
    anything = Query
    forall a. Monoid a => a
    mempty

    -- | Create a query matching a tag with a value.
    (=?) :: Metadata -> Value -> Query
    Metadata
    m =? :: Metadata -> Value -> Query
    =? Value
    s = [Match] -> Query
    Query [Metadata -> Value -> Match
    Match Metadata
    m Value
    s]

    -- | Create a query matching a tag with anything but a value.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    (/=?) :: Metadata -> Value -> Query
    Metadata
    m /=? :: Metadata -> Value -> Query
    /=? Value
    s = Expr -> Query
    Filter (Match -> Expr
    ExactNot (Metadata -> Value -> Match
    Match Metadata
    m Value
    s))

    -- | Create a query for a tag containing a value.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    (%?) :: Metadata -> Value -> Query
    Metadata
    m %? :: Metadata -> Value -> Query
    %? Value
    s = Expr -> Query
    Filter (Match -> Expr
    Contains (Metadata -> Value -> Match
    Match Metadata
    m Value
    s))

    -- | Create a query matching a tag with regexp.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    (~?) :: Metadata -> Value -> Query
    Metadata
    m ~? :: Metadata -> Value -> Query
    ~? Value
    s = Expr -> Query
    Filter (Match -> Expr
    Regex (Metadata -> Value -> Match
    Match Metadata
    m Value
    s))

    -- | Create a query matching a tag with anything but a regexp.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    (/~?) :: Metadata -> Value -> Query
    Metadata
    m /~? :: Metadata -> Value -> Query
    /~? Value
    s = Expr -> Query
    Filter (Match -> Expr
    RegexNot (Metadata -> Value -> Match
    Match Metadata
    m Value
    s))

    -- | Negate a Query.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    qNot :: Query -> Query
    qNot :: Query -> Query
    qNot (Query [Match]
    ms) = Expr -> Query
    Filter (Expr -> Expr
    ExprNot ([Match] -> Expr
    toExpr [Match]
    ms))
    qNot (Filter (ExprNot Expr
    ex)) = Expr -> Query
    Filter Expr
    ex
    qNot (Filter Expr
    ex) = Expr -> Query
    Filter (Expr -> Expr
    ExprNot Expr
    ex)

    -- | Create a query for songs modified since a date.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    qModSince :: UTCTime -> Query
    qModSince :: UTCTime -> Query
    qModSince UTCTime
    time = Expr -> Query
    Filter (UTCTime -> Expr
    ModifiedSince UTCTime
    time)

    -- | Create a query for the full song URI relative to the music directory.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    qFile :: Path -> Query
    qFile :: Path -> Query
    qFile Path
    file = Expr -> Query
    Filter (Path -> Expr
    File Path
    file)

    -- | Limit the query to the given directory, relative to the music directory.
    --
    -- Since MPD 0.21.
    --
    -- @since 0.9.3.0
    qBase :: Path -> Query
    qBase :: Path -> Query
    qBase Path
    dir = Expr -> Query
    Filter (Path -> Expr
    Base Path
    dir)