module Play (play, play', loop') where

import Data.Ratio
import Control.Monad
import Control.Monad.State
import Control.Lens
import Control.Concurrent
import System.MIDI

import Types
import Drum
import Interpret

-- | convert a `Hit` to a `MidiEvent`
hitToMidiEvent :: Hit -> MidiEvent
hitToMidiEvent h = MidiEvent d (MidiMessage 1 (NoteOn t v))
  where
    t  = 35 + fromEnum (h ^. tone)
    d  = fromR $ h ^. dur
    v  = fromR $ h ^. vol
    fromR r = fromIntegral $ numerator r `div` denominator r

getConnection :: IO Connection
getConnection = do
    dstlist <- enumerateDestinations
    case dstlist of
      [] -> error "No MIDI Devices found."
      (dst:_) -> openDestination dst

-- | play a composition at a given tempo
play :: Composition a -> Rational -> IO ()
play comp n = do
    conn <- getConnection
    start conn
    evalStateT runComposition (conn, interpret n comp)
    close conn

play' :: Composition a -> IO ()
play' = flip play 120

-- | loop a composition at a given tempo
loop' :: Song -> Rational -> IO ()
loop' s = play (loop s)

runComposition :: StateT (Connection, [Hit]) IO ()
runComposition = do
  (conn, comp) <- get
  t <- lift $ currentTime conn
  case comp of
    []     -> return ()
    (h:hs) -> do
      let (MidiEvent s ev) = hitToMidiEvent h
      when (s < t) $ do
        put (conn, hs)
        lift $ send conn ev
      lift $ threadDelay 250
      runComposition