module Csound.Render.Options(
    CsdOptions(..), mixing, mixingBy,
    renderInstr0, renderFlags, MidiAssign(..), CtrlId
) where

import Data.List(transpose)
import Data.Default

import Csound.Exp.Wrapper(Channel, Sig, SE, Out, Outs)
import Csound.Render.Sco
import Csound.Render.Pretty

type CtrlId = Int

-- | Sums signals for every channel.
mixing :: [[Sig]] -> Outs
mixing = return . fmap sum . transpose

-- | Sums signals for every channel and the processes the output with the given function.
mixingBy :: ([Sig] -> Outs) -> ([[Sig]] -> Outs)
mixingBy f = (f =<<) . mixing 

-- | Csound options. The default value is
--
-- > instance Default CsdOptions where
-- >     def = CsdOptions 
-- >             { csdFlags = ""
-- >             , csdRate  = 44100
-- >             , csdBlockSize = 64
-- >             , csdSeed = Nothing
-- >             , csdInitc7 = []
-- >             , csdEffect = mixing
-- >             , csdKrate  = ["linseg", "expseg", "linsegr", "expsegr", "linen", "linenr", "envlpx"],
-- >             , tabResolution = 8192 }  -- should be power of 2

data CsdOptions = CsdOptions 
    { csdFlags      :: String       
    , csdRate       :: Int          
    , csdBlockSize  :: Int          
    , csdSeed       :: Maybe Int    
    , csdInitc7     :: [(Channel, CtrlId, Double)]
    , csdEffect     :: [[Sig]] -> Outs
    , csdKrate      :: [String]
    , tabResolution :: Int
    }

instance Default CsdOptions where
    def = CsdOptions 
            { csdFlags = ""
            , csdRate  = 44100
            , csdBlockSize = 64
            , csdSeed = Nothing
            , csdInitc7 = []
            , csdEffect = mixing
            , csdKrate  = ["linseg", "expseg", "linsegr", "expsegr", "linen", "linenr", "envlpx"]
            , tabResolution = 8192 }

renderFlags = text . csdFlags

type Nchnls = Int

data MidiAssign = MidiAssign 
    { midiAssignType    :: MidiType
    , midiAssignChannel :: Channel
    , midiAssignInstr   :: Int }

type InstrId = Int

renderInstr0 :: Nchnls -> [MidiAssign] -> CsdOptions -> Doc
renderInstr0 nchnls massignTable opt = ppInstr0 $ [
    stmt "sr"    $ csdRate opt,
    stmt "ksmps" $ csdBlockSize opt,
    stmt "nchnls" nchnls,   
    stmt "0dbfs" 1,
    maybe empty seed $ csdSeed opt] 
    ++ map initc7 (csdInitc7 opt)
    ++ fmap renderMidiAssign massignTable
    where stmt a b = text a $= int b
          seed n = ppProc "seed" [int n]
          initc7 (chn, ctl, val) = ppProc "initc7" [int chn, int ctl, double val]
            
  
renderMidiAssign :: MidiAssign -> Doc
renderMidiAssign a = ppProc opcode $ [int $ midiAssignChannel a, int $ midiAssignInstr a] ++ auxParams
    where opcode = case midiAssignType a of
              Massign     -> "massign"
              Pgmassign _ -> "pgmassign"
          auxParams = case midiAssignType a of 
              Pgmassign (Just n) -> [int n]
              _ -> []