module Csound.Typed.Types.MixSco(
    M(..), delayAndRescaleCsdEventListM, renderMixSco, renderMixSco_
) where

import Control.Monad

import Csound.Dynamic
import Csound.Typed.GlobalState.Elements

data M 
    = Snd InstrId (CsdEventList [E])
    | Eff InstrId (CsdEventList M) Int   

delayAndRescaleCsdEventListM :: CsdEventList M -> CsdEventList M
delayAndRescaleCsdEventListM = delayCsdEventListM . rescaleCsdEventListM

delayCsdEventListM :: CsdEventList M -> CsdEventList M
delayCsdEventListM es = 
    es { csdEventListNotes = fmap delayCsdEventM $ csdEventListNotes es }

delayCsdEventM :: CsdEvent M -> CsdEvent M
delayCsdEventM (start, dur, evt) = (start, dur, phi evt)
    where phi x = case x of
            Snd n evts          -> Snd n $ delayCsdEventList start evts
            Eff n evts arityIn  -> Eff n (delayCsdEventListM $ delayCsdEventList start evts) arityIn        

rescaleCsdEventListM :: CsdEventList M -> CsdEventList M
rescaleCsdEventListM es = 
    es { csdEventListNotes = fmap rescaleCsdEventM $ csdEventListNotes es }

rescaleCsdEventM :: CsdEvent M -> CsdEvent M
rescaleCsdEventM (start, dur, evt) = (start, dur, phi evt)
    where phi x = case x of
            Snd n evts          -> Snd n $ rescaleCsdEventList (dur/localDur) evts
            Eff n evts arityIn  -> Eff n (rescaleCsdEventListM $ rescaleCsdEventList (dur/localDur) evts) arityIn
            where localDur = case x of
                    Snd _ evts   -> csdEventListDur evts
                    Eff _ evts _ -> csdEventListDur evts

renderMixSco :: Monad m => Int -> CsdEventList M -> DepT m [E]
renderMixSco arity evts = do
    chnId <- chnRefAlloc arity
    go chnId evts
    readChn chnId
    where 
        go :: Monad m => ChnRef -> CsdEventList M -> DepT m ()
        go outId xs = mapM_ (onEvent outId) $ csdEventListNotes xs

        onEvent :: Monad m => ChnRef -> CsdEvent M -> DepT m ()
        onEvent outId (start, dur, x) = case x of
            Snd instrId es          -> onSnd instrId outId es
            Eff instrId es arityIn  -> onEff instrId start dur outId es arityIn

        onSnd instrId outId es = forM_ (csdEventListNotes es) $ \(start, dur, args) ->
            event_i $ Event instrId (double start) (double dur) (args ++ [chnRefId outId])

        onEff instrId start dur outId es arityIn = do
            inId <- chnRefAlloc arityIn
            event_i $ Event instrId (double start) (double dur) [chnRefId inId, chnRefId outId]
            go inId es

renderMixSco_ :: Monad m => CsdEventList M -> DepT m ()
renderMixSco_ evts = mapM_ onEvent $ csdEventListNotes evts
    where
        onEvent :: Monad m => CsdEvent M -> DepT m ()
        onEvent (start, dur, x) = case x of
            Snd instrId es      -> onSnd instrId es
            Eff instrId es _    -> onEff instrId start dur es

        onSnd instrId es = forM_ (csdEventListNotes es) $ \(start, dur, args) ->
            event_i $ Event instrId (double start) (double dur) args

        onEff instrId start dur es = do
            event_i $ Event instrId (double start) (double dur) []
            renderMixSco_ es