content: {-
      Copyright 2015-2017 Markus Ongyerth
  
      This file is part of Monky.
  
      Monky is free software: you can redistribute it and/or modify
      it under the terms of the GNU Lesser General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      Monky is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU Lesser General Public License for more details.
  
      You should have received a copy of the GNU Lesser General Public License
      along with Monky.  If not, see <http://www.gnu.org/licenses/>.
  -}
  {-# LANGUAGE OverloadedStrings #-}
  {-|
  Module      : Monky.Examples.MPD
  Description : An example module instance for the MPD module
  Maintainer  : ongy
  Stability   : testing
  Portability : Linux
  
  -}
  {-# LANGUAGE StandaloneDeriving #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE CPP #-}
  module Monky.Examples.MPD
    ( MPDHandle
    , getMPDHandle
    , getMPDHandleF
    )
  where
  
  import Data.Text (Text)
  import qualified Data.Text as T
  
  import Data.IORef
  import System.IO (hPutStrLn, stderr)
  import System.Posix.Types (Fd)
  
  import Monky.MPD
  import Monky.Modules
  import Monky.Examples.Utility
  
  #if MIN_VERSION_base(4,8,0)
  #else
  import Control.Applicative ((<$>), pure, (<*>))
  #endif
  
  type ConvertFun = (State, Maybe SongInfo) -> Text
  
  getSongTitle :: MPDSocket -> ConvertFun -> IO Text
  -- TODO: Clean this up a bit. Probably do notation?
  getSongTitle sock fun = (fmap state <$> getMPDStatus sock) >>= getSong
    where getSong (Left x) = return . T.pack $ x
          getSong (Right Playing) = do
              info <- getMPDSong sock
              case info of
                  Right x -> pure $ fun (Playing, Just x)
                  Left x -> pure $ T.pack x
          getSong (Right x) = pure $ fun (x, Nothing)
  
  
  -- |The handle for this example
  data MPDHandle = MPDHandle
      { _host :: String
      , _port :: String
      , _sock :: IORef (Maybe MPDSocket)
      , _convert :: ConvertFun
      }
  
  
  -- TODO ignoring errors is never a good idea
  getEvent :: MPDSocket -> ConvertFun -> IO Text
  getEvent s fun = do
    _ <- readOk s
    t <- getSongTitle s fun
    _ <- goIdle s " player"
    return t
  
  
  getFd :: MPDSocket -> IO [Fd]
  getFd s = do
    fd <- getMPDFd s
    _ <- goIdle s " player"
    return [fd]
  
  
  instance PollModule MPDHandle where
    getOutput (MPDHandle _ _ s f) = do
      r <- readIORef s
      case r of
        Nothing -> return [MonkyPlain "Broken"]
        (Just x) -> do
          ret <- getSongTitle x f
          return [MonkyPlain ret]
    initialize (MPDHandle h p r _) = do
      s <- getMPDSocket h p
      case s of
        (Right x) -> writeIORef r (Just x)
        (Left _) -> return ()
  
  
  instance EvtModule MPDHandle where
    startEvtLoop h@(MPDHandle _ _ s f) fun = do
      initialize h
      fun =<< getOutput h
      r <- readIORef s
      case r of
        Nothing -> hPutStrLn stderr "Could not initialize MPDHandle :("
        (Just x) -> do
          [fd] <- getFd x
          loopFd x fd fun (fmap (\y -> [MonkyPlain y]) . flip getEvent f)
  
  defaultConvert :: (State, Maybe SongInfo) -> Text
  defaultConvert (Playing, Just x) = case tagTitle . songTags $ x of
      Nothing -> "Can't extract song title"
      Just y -> y
  defaultConvert (Playing, Nothing) = "Can't extract song"
  defaultConvert _ = "Not Playing"
  
  -- |Get an 'MPDHandle' (server has to be running when this is executed)
  getMPDHandle
    :: String -- ^The host to connect to
    -> String  -- ^The port to connect to
    -> IO MPDHandle
  getMPDHandle h p =
      MPDHandle h p <$> newIORef Nothing <*> pure defaultConvert
  
  -- | Get the 'MPDHandle' with a custom conversion function. You will need to
  -- import `Monky.MPD` to get the definitions into scope
  getMPDHandleF
      :: String -- ^The host to connect to
      -> String -- ^The port to connect to
      -> ConvertFun -- ^The function to extract the text
      -> IO MPDHandle
  getMPDHandleF h p f =
      MPDHandle h p <$> newIORef Nothing <*> pure f
  