-- | The Csound types.
--
-- There are several primitive types:
--
-- * @Sig@ - signals
--
-- * @D@ - numbers
--
-- * @Str@ - strings
--
-- * @Tab@ - 1-dimensional arrays
--
-- * @Spec@ and @Wspec@ - sound spectrums
--
--  A signal is a stream of numbers. Signals carry sound or time varied
--  control values. Numbers are constants. 1-dimensional arrays contain some useful
--  data which is calculated at the initial run of the program.
--
-- There is only one compound type. It's a tuple of Csound values. The empty tuple
-- is signified with special type called @Unit@.
--
module Csound.Types(
    -- * Primitive types
    Sig, D, Tab, Str, Spec, Wspec,
    BoolSig, BoolD, Val(..), SigOrD,

    Sig2, Sig3, Sig4, Sig5, Sig6, Sig8,
    Sig2_2, Sig2_3, Sig2_4, Sig2_5, Sig2_6, Sig2_7, Sig2_8,
    -- ** Constructors
    double, int, text,

    -- ** Constants
    idur, getSampleRate, getControlRate, getBlockSize,

    -- ** BPM
    getBpm, setBpm, syn, takt,

    -- ** Converters
    ar, kr, ir, sig,

    -- ** Init values
    withInits, withDs, withSigs, withTabs,
    withD, withSig, withTab, withSeed,

    -- ** Numeric functions
    quot', rem', div', mod', ceil', floor', round', int', frac',

    -- ** Logic functions
    boolSig, when1, whens, whenElse, whenD1, whenDs, whileDo, untilDo, whileDoD, untilDoD, whenElseD, compareWhenD,
    equalsTo, notEqualsTo, lessThan, greaterThan, lessThanEquals, greaterThanEquals,

    -- ** Aliases
    -- | Handy for functions that return tuples to specify the utput type
    --
    -- > (aleft, aright) = ar2 $ diskin2 "file.wav" 1
    --
    -- or
    --
    -- > asig = ar1 $ diskin2 "file.wav" 1
    ar1, ar2, ar4, ar6, ar8,

    -- * Tuples
    Tuple(..), makeTupleMethods, Unit, unit, atTuple,
    -- *** Logic functions
    ifTuple, guardedTuple, caseTuple,

    -- * Instruments

    -- | An instrument is a function that takes a tpule of csound values as an argument
    -- and returns a tuple of signals as an output. The type of the instrument is:
    --
    -- > (Arg a, Out b) => a -> b

    -- ** Arguments
    Arg, atArg,
    -- *** Logic functions
    ifArg, guardedArg, caseArg,

    -- ** Monophonic arguments
    MonoArg(..), MonoAdsr, adsrMonoSynt, monoAdsr,

    -- ** Outputs
    Sigs,

    -- * Arrays
    Arr, newLocalArr, newGlobalArr, newLocalCtrlArr, newGlobalCtrlArr,
    writeArr, readArr, modifyArr, mixArr,
    -- ** Type inference helpers
    Arr1, DArr1, Arr2, DArr2, Arr3, DArr3,
    arr1, darr1, arr2, darr2, arr3, darr3,

    -- ** Traverse and fold
    foreachArr, foreachArrD, forRowArr, forColumnArr, forRowArrD, forColumnArrD,
    foldArr, foldRowArr, foldColumnArr, foldRowsArrD, foldColumnsArrD,

    -- ** Array opcodes
    fillLocalArr, fillGlobalArr, fillLocalCtrlArr, fillGlobalCtrlArr,
    maparrayNew, lenarray, copyf2array, copya2ftab, minarray, maxarray, sumarray,
    scalearray, slicearrayNew,

    maparrayCopy, slicearrayCopy,

    -- ** Spectral opcodes
    SpecArr,

    fftNew, fftinvNew, rfftNew, rifftNew, pvs2tab, tab2pvs, cmplxprodNew,
    rect2polNew, pol2rectNew, pol2rect2New, windowArrayNew,
    r2cNew, c2rNew, magsArrayNew, phsArrayNew,

    fftCopy, fftinvCopy, rfftCopy, rifftCopy, cmplxprodCopy,
    rect2polCopy, pol2rectCopy, pol2rect2Copy, windowArrayCopy,
    r2cCopy, c2rCopy, magsArrayCopy, phsArrayCopy
) where

import Data.Boolean
import Csound.Typed.Types
import Csound.Typed.Control
import Csound.Control.SE

-- | Gets an init-rate value from the list by index.
atArg :: (Tuple a, Arg a) => [a] -> D -> a
atArg as ind = guardedArg (zip (fmap (\x -> int x ==* ind) [0 .. ]) as) (head as)

-- | Gets an control/audio-rate value from the list by index.
atTuple :: (Tuple a) => [a] -> Sig -> a
atTuple as ind = guardedTuple (zip (fmap (\x -> sig (int x) ==* ind) [0 .. ]) as) (head as)


whenElseD :: BoolD -> SE () -> SE () -> SE ()
whenElseD cond ifDo elseDo = whenDs [(cond, ifDo)] elseDo

whenElse :: BoolSig -> SE () -> SE () -> SE ()
whenElse cond ifDo elseDo = whens [(cond, ifDo)] elseDo

-- | Performs tree search f the first argument lies within the interval it performs the corresponding procedure.
compareWhenD :: D -> [(D, SE ())] -> SE ()
compareWhenD val conds = case conds of
    [] -> return ()
    [(cond, ifDo)] -> ifDo
    (cond1, do1):(cond2, do2): [] -> whenElseD (val `lessThan` cond1) do1 do2
    _ -> whenElseD (val `lessThan` rootCond) (compareWhenD val less) (compareWhenD val more)
    where
        (less, more) = splitAt (length conds `div` 2) conds
        rootCond = fst $ last less

-- | It's used to synchronize changes with BPM.
-- Useful with LFO's or delay times.
syn :: Sig -> Sig
syn x = (bpm / 60) * x
    where bpm = getBpm

-- | Calculates seconds in ratio to global BPM.
-- It measures time according to changes in the BPM.
-- It's reciprocal to @syn@.
takt :: Sig -> Sig
takt = recip . syn