-- | Multiple channel expansion
module Sound.Sc3.Ugen.Mce where

import Control.Monad {- base -}
import Data.List {- base -}

import qualified Data.List.Split as Split {- split -}

import Sound.Sc3.Common.Uid {- hsc3 -}
import qualified Sound.Sc3.Ugen.Math as Math {- hsc3 -}
import Sound.Sc3.Ugen.Ugen {- hsc3 -}

-- | Construct a list of Ugens by applying f to consecutive identifiers (from z) and indices (from 0).
listFillId :: (Integral n, ID z, Enum z) => z -> Int -> (z -> n -> Ugen) -> [Ugen]
listFillId z n f = zipWith f [z ..] [0 .. fromIntegral n - 1]

-- | Construct a list of Ugens by applying f at consecutive indices (from 0).
listFillM :: (Uid m, Integral n) => Int -> (n -> m Ugen) -> m [Ugen]
listFillM n f = mapM f [0 .. fromIntegral n - 1]

-- | Construct a list of Ugens by applying f at consecutive indices (from 0).
listFill :: Integral n => Int -> (n -> Ugen) -> [Ugen]
listFill n f = map f [0 .. fromIntegral n - 1]

-- | 'mce' of 'replicate'
mceConst :: Int -> Ugen -> Ugen
mceConst n = mce . replicate n

-- | 'mce' of 'map' /f/ of 'id_seq' /n/.
mceGenId :: ID z => (Id -> Ugen) -> Int -> z -> Ugen
mceGenId f n = mce . map f . id_seq n

-- | Applicative variant of mceGenId.
mceGenM :: Applicative f => f Ugen -> Int -> f Ugen
mceGenM f n = fmap mce (replicateM n f)

-- | Count 'mce' channels.
mceSize :: Ugen -> Ugen
mceSize = constant . length . mceChannels

-- | Mix divided by number of inputs.
mceMean :: Ugen -> Ugen
mceMean e = let p = mceChannels e in Math.sum_opt p / constant (length p)

-- | Construct an Mce array of Ugens.
mceFill :: Integral n => Int -> (n -> Ugen) -> Ugen
mceFill n = mce . listFill n

-- | 'mce' of 'listFillId'
mceFillId :: (Integral n, ID z, Enum z) => z -> Int -> (z -> n -> Ugen) -> Ugen
mceFillId z n = mce . listFillId z n

-- | Type specialised mceFill
mceFillInt :: Int -> (Int -> Ugen) -> Ugen
mceFillInt = mceFill

-- | Collapse possible mce by summing.
mix :: Ugen -> Ugen
mix = Math.sum_opt . mceChannels

-- | Mix variant, sum to n channels.
mixTo :: Int -> Ugen -> Ugen
mixTo n u =
  let xs = transpose (Split.chunksOf n (mceChannels u))
  in mce (map Math.sum_opt xs)

-- | 'mix' of 'mceFill'
mixFill :: Integral n => Int -> (n -> Ugen) -> Ugen
mixFill n = mix . mceFill n

-- | Type specialised mixFill
mixFillInt :: Int -> (Int -> Ugen) -> Ugen
mixFillInt = mixFill

-- | Type specialised mixFill
mixFillUgen :: Int -> (Ugen -> Ugen) -> Ugen
mixFillUgen = mixFill

-- | 'mix' of 'mceFillId'
mixFillId :: (Integral n, ID z, Enum z) => z -> Int -> (z -> n -> Ugen) -> Ugen
mixFillId z n = mix . mceFillId z n

-- | Monad variant on mixFill.
mixFillM :: (Integral n, Monad m) => Int -> (n -> m Ugen) -> m Ugen
mixFillM n f = fmap Math.sum_opt (mapM f [0 .. fromIntegral n - 1])
