{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies, NoMonoLocalBinds #-}

{-# LANGUAGE NoIncoherentInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE NoUndecidableInstances #-}

module Vivid.UGens.Buffer (

     -- * Buffer > Info

     bufChannels
   , bufDur
   , bufFrames
   , bufRateScale
   , bufSampleRate
   , bufSamples

     -- * Buffer

   , bufRd
   , bufWr
---   , dbufrd
---   , dbufwr
---   , delTapRd
---   , delTapWr
---   , detectIndex
     -- In Vivid.UGens.InOut:
   -- , diskIn
     -- In Vivid.UGens.InOut:
   -- , diskOut
     -- In Vivid.UGens.Generators.Granular:
   -- , grainBuf
---   , harmonics
---   , index
---   , indexInBetween
---   , indexL
   , localBuf
---   , multiTap
   , phasor
   , playBuf
   , recordBuf
---   , scopeOut
---   , shaper
     -- In Vivid.UGens.Generators.Granular
   -- , tGrains
---   , tap
     -- In Vivid.UGens.InOut:
   -- , vDiskIn
     -- In Vivid.UGens.Generators.Granular
   -- , warp1
---   , wrapIndex
   ) where

import Vivid.SC.SynthDef.Types (CalculationRate(..))
import Vivid.SynthDef
import Vivid.SynthDef.FromUA
import Vivid.UGens.Args

import Data.Proxy
import GHC.Real (Ratio((:%)))

-- | Add a single LocalBuf for FFT
-- 
--   Can use 'Vivid.UGens.Args.chans_' for \"numChans\"
--   and 'frames_' for \"Vivid.UGens.Args.numFrames\"
localBuf :: Args '["numChans","numFrames"] '[] a => a -> SDBody a Signal
localBuf args = do
   mlb <- addUGen $ UGen (UGName_S "MaxLocalBufs") IR [Constant 1] 1
   numChannels' <- uaArgVal args (Proxy::Proxy "numChans")
   numFrames' <- uaArgVal args (Proxy::Proxy "numFrames")
   -- Another example where the args in sclang and scsynth are in different orders:
   addUGen $ UGen (UGName_S "LocalBuf") IR [numChannels', numFrames', mlb] 1


-- | Unlike in SC, \"doneAction\\" defaults to 2
-- 
--   Also, the default rate is the 'bufRateScale' of the buffer
playBuf :: (Args '["buf"] '["rate","trigger","startPos","loop","doneAction"] a) => a -> SDBody a Signal
playBuf args = ($ args) $ makeUGen
   "PlayBuf" AR
   (Vs::Vs '["buf","rate","trigger","startPos","loop","doneAction"])
   (rate_ defaultRate, trigger_ ((1)::Float), startPos_ ((0)::Float)
   ,loop_ ((0)::Float), doneAction_ ((2)::Float))
 where
        -- TODO: maybe don't want to do this. especially cause if you set the rate it doesn't multiply by this
        -- alternative is to change 'rate_' to 'bufrate_' and have that 'arg' function do a multiply
        -- update docs too:
          -- todo : put bufratescale on all of em if we decide to keep this behavior
   defaultRate = bufRateScale $ uaArgVal args (V::V "buf")


-- | Unlike in SC, "doneAction" defaults to 2 and "loop" defaults to 0
recordBuf :: (Args '["buf","in"] '["offset","recLevel","preLevel","run","loop","trigger","doneAction"] a) => a -> SDBody a Signal
recordBuf = makeUGen
   "RecordBuf" AR
   (Vs::Vs '["buf","offset","recLevel","preLevel","run","loop","trigger","doneAction","in"])
   -- this is another example of different order:
   (offset_ ((0)::Float), recLevel_ ((1)::Float), preLevel_ ((0)::Float), run_ ((1)::Float), loop_ ((0)::Float), trigger_ ((1)::Float), doneAction_ ((2)::Float))

-- | Defaults to 'KR'. Can be 'IR' too but be careful that the buffer doesn't change if so!
bufChannels :: (Args '["buf"] '[] a) => a -> SDBody a Signal
bufChannels = makeUGen
   "BufChannels" KR
   (Vs::Vs '["buf"])
   NoDefaults

-- | Defaults to 'KR'. Can be 'IR' too but be careful that the buffer doesn't change if so!
bufDur :: (Args '["buf"] '[] a) => a -> SDBody a Signal
bufDur = makeUGen
   "BufDur" KR
   (Vs::Vs '["buf"])
   NoDefaults

-- bufFrames :: (Args '["buf"] '[] a) => a -> SDBody a Signal

-- | Defaults to 'KR'. Can be 'IR' too but be careful that the buffer doesn't change if so!
-- 
--   Note you don't need to use "buf_" when you use this
bufFrames :: ToSig s as => s -> SDBody' as Signal
bufFrames = (flip (.)) buf_ $ makeUGen
   "BufFrames" KR
   (Vs::Vs '["buf"])
   NoDefaults

-- bufRateScale :: (Args '["buf"] '[] a) => a -> SDBody a Signal

-- | Defaults to 'KR'. Can be 'IR' too but be careful that the buffer doesn't change if so!
-- 
--   Note you don't need to use "buf_" when you use this
bufRateScale :: ToSig s as => s -> SDBody' as Signal
bufRateScale = (. buf_) $ makeUGen
   "BufRateScale" KR
   (Vs::Vs '["buf"])
   NoDefaults

-- bufSampleRate :: (Args '["buf"] '[] a) => a -> SDBody a Signal

-- | Defaults to 'KR'. Can be 'IR' too but be careful that the buffer doesn't change if so!
-- 
--   Note you don't need to use "buf_" when you use this
bufSampleRate :: ToSig s as => s -> SDBody' as Signal
bufSampleRate = (flip (.)) buf_ $ makeUGen
   "BufSampleRate" KR
   (Vs::Vs '["buf"])
   NoDefaults

-- bufSamples :: (Args '["buf"] '[] a) => a -> SDBody a Signal

-- | Defaults to 'KR'. Can be 'IR' too but be careful that the buffer doesn't change if so!
-- 
--   Note you don't need to use "buf_" when you use this
bufSamples :: ToSig s as => s -> SDBody' as Signal
bufSamples = (flip (.)) buf_ $ makeUGen
   "BufSamples" KR
   (Vs::Vs '["buf"])
   NoDefaults

-- | \"phase\" must be at audio rate ('AR')
-- 
--   \"numChans\" can't be set after the synth is created, and must be a fixed integer
bufRd :: Args '["numChans", "buf", "phase"] '["loop", "interp"] a => a -> SDBody a [Signal]
bufRd args = do
   numChans <- uaArgVal args (V::V "numChans") >>= \case
      Constant x -> case toRational x of
         n :% 1 -> pure $ fromInteger n
         _ -> error $ "bufRd: numChans not an integer!: " ++ show x
      _ -> error "bufrd: not a fixed integer!"
   makePolyUGen numChans
      "BufRd" AR
      (Vs::Vs '["buf", "phase", "loop", "interp"])
      (loop_ ((1)::Float), interp_ ((2)::Float))
      args

-- | "phase" must be at audio rate ('AR')
bufWr :: (Args '["in", {- "numChans", -} "buf", "phase"] '["loop"] a) => a -> SDBody a Signal
bufWr = makeUGen
   "BufWr" AR
   -- An example of arguments in different orders in sclang and scsynth:
   (Vs::Vs '["buf", "phase", "loop", "in"])
   (loop_ ((1)::Float))

        -- returns demandrate
--- dbufrd ::
--- dbufrd =
--- dbufwr ::
--- dbufwr =

-- | "phase" must be the output of 'delTapWr'
--delTapRd :: (Args '["buf", "phase", "delSecs"] '["interp"] a) => s -> SDBody a Signal
--- delTapRd ::
--- delTapRd =

--- delTapWr ::
--- delTapWr =
--- detectIndex ::
--- detectIndex =
--- harmonics ::
--- harmonics =
--- index ::
--- index =
--- indexInBetween ::
--- indexInBetween =
--- indexL ::
--- indexL =
--- multiTap ::
--- multiTap =

phasor :: (Args '[] '["trigger", "rate", "start", "end", "resetPos"] a) => a -> SDBody a Signal
phasor = makeUGen
   "Phasor" AR
   (Vs::Vs '["trigger", "rate", "start", "end", "resetPos"])
   (trig_ ((0)::Float), rate_ ((1)::Float), start_ ((0)::Float), end_ ((1)::Float), resetPos_ ((0)::Float))

--- scopeOut ::
--- scopeOut =
--- shaper ::
--- shaper =
--- tap ::
--- tap =
--- wrapIndex ::
--- wrapIndex =