module Sound.Tidal.MultiMode where

import Control.Concurrent.MVar
import Control.Concurrent
import Control.Monad (forever)
import Control.Monad.Loops (iterateM_)
import Sound.OSC.FD
import Data.Time.Clock
import Data.Time.Clock.POSIX
import Data.Time.Calendar (fromGregorian)

import Sound.Tidal.Tempo
import Sound.Tidal.Time as T
import Sound.Tidal.Stream
import Sound.Tidal.Dirt
import Sound.Tidal.EspGrid
import Sound.Tidal.Transition (transition)
import Sound.Tidal.Pattern (silence)

data StreamType = Dirt | SuperDirt

data SyncType = NoSync | Esp

initializeStreamType :: IO (MVar StreamType)
initializeStreamType = newMVar SuperDirt

changeStreamType :: MVar StreamType -> StreamType -> IO (IO StreamType)
changeStreamType mvar t = return (swapMVar mvar t)

initializeSyncType :: IO (MVar SyncType)
initializeSyncType = newMVar NoSync

changeSyncType :: MVar SyncType -> SyncType -> IO (IO SyncType)
changeSyncType mvar t = return (swapMVar mvar t)

type CpsUtils = (Double -> IO(), IO Rational)

multiModeCpsUtils :: CpsUtils -> CpsUtils -> MVar SyncType -> IO CpsUtils
multiModeCpsUtils (cpsNone,getNowNone) (cpsEsp,getNowEsp) mSync = return (cps,getNow)
  where cps x = do s <- readMVar mSync
                   case s of NoSync -> cpsNone x
                             Esp -> cpsEsp x
        getNow = do s <- readMVar mSync
                    case s of NoSync -> getNowNone
                              Esp -> getNowEsp

multiModeSetters :: IO Rational -> IO Rational -> MVar SyncType -> MVar StreamType -> IO (ParamPattern -> IO ())
multiModeSetters getNowNone getNowEsp mSync mStream = do
  (classicDirt,tClassic) <- dirtSetters getNowNone
  (espDirt,tEsp) <- dirtSettersEsp getNowEsp
  (superDirt,tSuper) <- superDirtSetters getNowNone
  (espSuperDirt,tEspSuper) <- superDirtSettersEsp getNowEsp
  let f NoSync Dirt p = do classicDirt p
                           espDirt silence
                           superDirt silence
                           espSuperDirt silence
      f Esp Dirt p = do espDirt p
                        classicDirt silence
                        superDirt silence
                        espSuperDirt silence
      f NoSync SuperDirt p = do superDirt p
                                classicDirt silence
                                espDirt silence
                                espSuperDirt silence
      f Esp SuperDirt p = do espSuperDirt p
                             classicDirt silence
                             espDirt silence
                             superDirt silence
  return $ \p -> readMVar mSync >>= \s -> readMVar mStream >>= \t -> f s t p

{-

Example of using the above definitions:
(note: if using Atom, evaluate each of the lines below one by one using shift-Enter)

syncType <- initializeSyncType
nosync <- changeSyncType syncType NoSync
esp <- changeSyncType syncType Esp
(cpsNone,getNowNone) <- cpsUtils
(cpsEsp,getNowEsp,getTempoEsp) <- cpsUtilsEsp
(cps,getNow) <- multiModeCpsUtils (cpsNone,getNowNone) (cpsEsp,getNowEsp) syncType

streamType <- initializeStreamType
classicDirt <- changeStreamType streamType Dirt
superDirt <- changeStreamType streamType SuperDirt
d1 <- multiModeSetters getNowNone getNowEsp syncType streamType
d2 <- multiModeSetters getNowNone getNowEsp syncType streamType
d3 <- multiModeSetters getNowNone getNowEsp syncType streamType
d4 <- multiModeSetters getNowNone getNowEsp syncType streamType
d5 <- multiModeSetters getNowNone getNowEsp syncType streamType
d6 <- multiModeSetters getNowNone getNowEsp syncType streamType
d7 <- multiModeSetters getNowNone getNowEsp syncType streamType
d8 <- multiModeSetters getNowNone getNowEsp syncType streamType
d9 <- multiModeSetters getNowNone getNowEsp syncType streamType
d10 <- multiModeSetters getNowNone getNowEsp syncType streamType

let bps x = cps (x/2)
let hush = mapM_ ($ silence) [d1,d2,d3,d4,d5,d6,d7,d8,d9,d10]
let solo = (>>) hush

then you can evaluate "classicDirt" to switch to classic Dirt
and "superDirt" to switch back to SuperDirt (the default)
and "esp" to turn on EspGrid-aware synchronization
and "nosync" to switch off EspGrid-aware synchronization (the default)
(switching between sync types is only noticeable after the next time a pattern is redefined right now)
-}
