{-# LANGUAGE DeriveAnyClass #-}

-- | Utility code to gather MIDI system information.
module Sound.RtMidi.Report
  ( ApiReport (..)
  , Report (..)
  , buildApiReport
  , buildCustomReport
  , buildReport
  ) where

import Control.DeepSeq (NFData)
import Control.Monad.IO.Class (MonadIO)
import Data.List (nub)
import GHC.Generics (Generic)
import Sound.RtMidi

-- | MIDI system information specific to a particular API.
data ApiReport = ApiReport
  { ApiReport -> Api
apiRepApi :: !Api
  , ApiReport -> String
apiRepName :: !String
  , ApiReport -> String
apiRepDisplayName :: !String
  , ApiReport -> [(Int, String)]
apiInPorts :: ![(Int, String)]
  , ApiReport -> [(Int, String)]
apiOutPorts :: ![(Int, String)]
  } deriving stock (ApiReport -> ApiReport -> Bool
(ApiReport -> ApiReport -> Bool)
-> (ApiReport -> ApiReport -> Bool) -> Eq ApiReport
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ApiReport -> ApiReport -> Bool
$c/= :: ApiReport -> ApiReport -> Bool
== :: ApiReport -> ApiReport -> Bool
$c== :: ApiReport -> ApiReport -> Bool
Eq, Int -> ApiReport -> ShowS
[ApiReport] -> ShowS
ApiReport -> String
(Int -> ApiReport -> ShowS)
-> (ApiReport -> String)
-> ([ApiReport] -> ShowS)
-> Show ApiReport
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ApiReport] -> ShowS
$cshowList :: [ApiReport] -> ShowS
show :: ApiReport -> String
$cshow :: ApiReport -> String
showsPrec :: Int -> ApiReport -> ShowS
$cshowsPrec :: Int -> ApiReport -> ShowS
Show, (forall x. ApiReport -> Rep ApiReport x)
-> (forall x. Rep ApiReport x -> ApiReport) -> Generic ApiReport
forall x. Rep ApiReport x -> ApiReport
forall x. ApiReport -> Rep ApiReport x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep ApiReport x -> ApiReport
$cfrom :: forall x. ApiReport -> Rep ApiReport x
Generic)
    deriving anyclass (ApiReport -> ()
(ApiReport -> ()) -> NFData ApiReport
forall a. (a -> ()) -> NFData a
rnf :: ApiReport -> ()
$crnf :: ApiReport -> ()
NFData)

-- | MIDI system information for any number of APIs.
data Report = Report
  { Report -> Api
defaultInApi :: !Api
  , Report -> Api
defaultOutApi :: !Api
  , Report -> [ApiReport]
apiReports :: ![ApiReport]
  } deriving stock (Report -> Report -> Bool
(Report -> Report -> Bool)
-> (Report -> Report -> Bool) -> Eq Report
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Report -> Report -> Bool
$c/= :: Report -> Report -> Bool
== :: Report -> Report -> Bool
$c== :: Report -> Report -> Bool
Eq, Int -> Report -> ShowS
[Report] -> ShowS
Report -> String
(Int -> Report -> ShowS)
-> (Report -> String) -> ([Report] -> ShowS) -> Show Report
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Report] -> ShowS
$cshowList :: [Report] -> ShowS
show :: Report -> String
$cshow :: Report -> String
showsPrec :: Int -> Report -> ShowS
$cshowsPrec :: Int -> Report -> ShowS
Show, (forall x. Report -> Rep Report x)
-> (forall x. Rep Report x -> Report) -> Generic Report
forall x. Rep Report x -> Report
forall x. Report -> Rep Report x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Report x -> Report
$cfrom :: forall x. Report -> Rep Report x
Generic)
    deriving anyclass (Report -> ()
(Report -> ()) -> NFData Report
forall a. (a -> ()) -> NFData a
rnf :: Report -> ()
$crnf :: Report -> ()
NFData)

-- | Gather information about the given 'Api', including port information.
buildApiReport :: MonadIO m => Api -> m ApiReport
buildApiReport :: Api -> m ApiReport
buildApiReport Api
api = do
  String
name <- Api -> m String
forall (m :: * -> *). MonadIO m => Api -> m String
apiName Api
api
  String
displayName <- Api -> m String
forall (m :: * -> *). MonadIO m => Api -> m String
apiDisplayName Api
api
  InputDevice
inDev <- Api -> String -> Int -> m InputDevice
forall (m :: * -> *).
MonadIO m =>
Api -> String -> Int -> m InputDevice
createInput Api
api (String
"rtmidi-report-input-" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
name) Int
0
  [(Int, String)]
inPorts <- InputDevice -> m [(Int, String)]
forall (m :: * -> *) d.
(MonadIO m, IsDevice d) =>
d -> m [(Int, String)]
listPorts InputDevice
inDev
  OutputDevice
outDev <- Api -> String -> m OutputDevice
forall (m :: * -> *). MonadIO m => Api -> String -> m OutputDevice
createOutput Api
api (String
"rtmidi-report-output-" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
name)
  [(Int, String)]
outPorts <- OutputDevice -> m [(Int, String)]
forall (m :: * -> *) d.
(MonadIO m, IsDevice d) =>
d -> m [(Int, String)]
listPorts OutputDevice
outDev
  ApiReport -> m ApiReport
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Api
-> String
-> String
-> [(Int, String)]
-> [(Int, String)]
-> ApiReport
ApiReport Api
api String
name String
displayName [(Int, String)]
inPorts [(Int, String)]
outPorts)

-- | Variant of 'buildReport' that allows you to restrict it to the default APIs.
buildCustomReport :: MonadIO m
                  => Bool  -- ^ True to report on default APIs, False to report on all compiled APIs.
                  -> m Report
buildCustomReport :: Bool -> m Report
buildCustomReport Bool
defaultOnly = do
  InputDevice
inDev <- m InputDevice
forall (m :: * -> *). MonadIO m => m InputDevice
defaultInput
  Api
defInApi <- InputDevice -> m Api
forall (m :: * -> *) d. (MonadIO m, IsDevice d) => d -> m Api
currentApi InputDevice
inDev
  OutputDevice
outDev <- m OutputDevice
forall (m :: * -> *). MonadIO m => m OutputDevice
defaultOutput
  Api
defOutApi <- OutputDevice -> m Api
forall (m :: * -> *) d. (MonadIO m, IsDevice d) => d -> m Api
currentApi OutputDevice
outDev
  [Api]
apis <- if Bool
defaultOnly then [Api] -> m [Api]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Api] -> [Api]
forall a. Eq a => [a] -> [a]
nub [Api
defInApi, Api
defOutApi]) else m [Api]
forall (m :: * -> *). MonadIO m => m [Api]
compiledApis
  [ApiReport]
apiReps <- (Api -> m ApiReport) -> [Api] -> m [ApiReport]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse Api -> m ApiReport
forall (m :: * -> *). MonadIO m => Api -> m ApiReport
buildApiReport [Api]
apis
  Report -> m Report
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Api -> Api -> [ApiReport] -> Report
Report Api
defInApi Api
defOutApi [ApiReport]
apiReps)

-- | Gather information about all compiled APIs.
buildReport :: MonadIO m => m Report
buildReport :: m Report
buildReport = Bool -> m Report
forall (m :: * -> *). MonadIO m => Bool -> m Report
buildCustomReport Bool
False