{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}

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

module Vivid.UGens.Filters.Linear (
     apf
   , bpf
   , bpz2
   , brf
   , brz2
---   , changed
   , decay
   , decay2
---   , dynKlank
   , fos
   , formlet
   , hpf
   , hpz1
   , hpz2
   , integrator
   , klank
   , lag
   , lag2
   , lag3
   , leakDC
   , lpf
   , lpz1
   , lpz2
   , midEQ
   , onePole
   , oneZero
   , rhpf
   , rlpf
   , ramp
   , resonz
   , ringz
   , sos
   , slope
   , twoPole
   , twoZero
---   , varLag
   ) where

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

import qualified Data.ByteString.UTF8 as UTF8
import Data.Monoid
import Data.Proxy

-- import Data.ByteString (ByteString)

apf :: (Args '["in"] '["freq", "radius"] a) => a -> SDBody a Signal
apf = makeUGen
   "APF" AR
   (Vs::Vs '["in", "freq", "radius"])
   (freq_ (440::Float), radius_ (0.8::Float))

-- | Band-pass filter
-- 
--   Rq: bandwidth / cutofffreq
bpf :: (Args '["in"] '["freq", "rq"] a) => a -> SDBody a Signal
bpf = makeUGen
   "BPF" AR
   (Vs::Vs '["in", "freq", "rq"])
   (freq_ (440::Float), rq_ (1::Float))

bpz2 :: (Args '["in"] '[] a) => a -> SDBody a Signal
bpz2 = makeUGen
   "BPZ2" AR
   (Vs::Vs '["in"])
   NoDefaults

brf :: (Args '["in"] '["freq", "rq"] a) => a -> SDBody a Signal
brf = makeUGen
   "BRF" AR
   (Vs::Vs '["in", "freq", "rq"])
   (freq_ (440::Float), rq_ (1::Float))

brz2 :: (Args '["in"] '[] a) => a -> SDBody a Signal
brz2 = makeUGen
   "BRZ2" AR
   (Vs::Vs '["in"])
   NoDefaults

--- changed ::
--- changed =

decay :: (Args '["in"] '["decaySecs"] a) => a -> SDBody a Signal
decay = makeUGen
   "Decay" AR
   (Vs::Vs '["in", "decaySecs"])
   (decayTime_ (1::Float))

decay2 :: (Args '["in"] '["attackSecs", "decaySecs"] a) => a -> SDBody a Signal
decay2 = makeUGen
   "Decay2" AR
   (Vs::Vs '["in", "attackSecs", "decaySecs"])
   (attackTime_ (0.01::Float), decayTime_ (1::Float))

--- dynKlank ::
--- dynKlank =

fos :: (Args '["in"] '["a0", "a1", "b1"] a) => a -> SDBody a Signal
fos = makeUGen
   "FOS" AR
   (Vs::Vs '["in", "a0", "a1", "b1"])
   (a0_ (0::Float), a1_ (0::Float), b1_ (0::Float))

formlet :: (Args '["in"] '["freq", "attackSecs", "decaySecs"] a) => a -> SDBody a Signal
formlet = makeUGen
   "Formlet" AR
   (Vs::Vs '["in", "freq", "attackSecs", "decaySecs"])
   (freq_ (440::Float), attackTime_ (1::Float), decayTime_ (1::Float))

-- | High-pass filter
hpf :: (Args '["in"] '["freq"] a) => a -> SDBody a Signal
hpf = passFilter "HPF"

hpz1 :: (Args '["in"] '[] a) => a -> SDBody a Signal
hpz1 = makeUGen
   "HPZ1" AR
   (Vs::Vs '["in"])
   NoDefaults

hpz2 :: Args '["in"] '[] a => a -> SDBody a Signal
hpz2 = makeUGen
   "HPZ2" AR
   (Vs::Vs '["in"])
   NoDefaults

integrator :: Args '["in"] '["coef"] a => a -> SDBody a Signal
integrator = makeUGen
   "Integrator" AR
   (Vs::Vs '["in", "coef"])
   (coef_ (1::Float))


-- todo: i want either 'freqScale' or a way to pass in params to the list of frequencies

-- | \"Klank is a bank of fixed frequency resonators which can be used to simulate the resonant modes of an object. Each mode is given a ring time, which is the time for the mode to decay by 60 dB\"
-- 
--   The 'in_' argument is \"the excitation input to the resonant filter bank\"
-- 
--   Each tuple in the list argument is a triple of frequency, amplitude, and ring time
-- 
--   Can only run in 'AR'
klank :: Args '["in"] '[] a => a -> [(Float, Float, Float)] -> SDBody a Signal
klank args resonators = do
   in' <- uaArgVal args (Proxy::Proxy "in")
   addMonoUGen $ UGen (UGName_S (UTF8.fromString "Klank")) AR (outs in') 1
 where
   outs inThing = [
        inThing
         -- These may be added as parameters in the future:
      , Constant 1 -- freqscale
      , Constant 0 -- freqoffset
      , Constant 1 -- decayscale
      ] <> concat [ map Constant [a,b,c] | (a,b,c) <- resonators ]

{-
3 "Klank" - AR (1 outputs)
  UGOut: (2,0) -- "excitation input" thing
  Constant: 1.0 (index 3) -- freqscale
  Constant: 0.0 (index 4) -- freqoffset
  Constant: 1.0 (index 5) -- decayscale

  Constant: 220.0 (index 6) -- frequency
  Constant: 0.5 (index 7)   -- amplitude
  Constant: 1.0 (index 8)   -- rungTime

  Constant: 440.0 (index 9)
  Constant: 1.0 (index 8)
  Constant: 1.0 (index 8)

  Constant: 880.0 (index 10)
  Constant: 0.5 (index 7)
  Constant: 1.0 (index 8)
-}

-- | The \"lagSecs\" arg is the same as the \"lagTime\" arg in SC
--   (you can use 'Vivid.UGens.Args.lagTime_' if you like)
--
--   The calculation rate of this is whatever its \"in\" is (cool, right?)
lag :: Args '["in"] '["lagSecs"] a => a -> SDBody a Signal
lag as = do
   makeThing =<< getCalcRate =<< uaArgVal as (Proxy::Proxy "in")
 where
   makeThing calcRate = ($ as) $ makeUGen
      "Lag" calcRate
      (Vs::Vs '["in", "lagSecs"])
      (lagSecs_ (0.1::Float))

-- | 'lag2 (in_ x)' is equal to 'lag (in_ (lag (in_ x)))'
--
--   The calculation rate of this is whatever its \"in\" is
lag2 :: (Args '["in"] '["lagSecs"] a) => a -> SDBody a Signal
lag2 as =
   makeThing =<< getCalcRate =<< uaArgVal as (Proxy::Proxy "in")
 where
   makeThing calcRate = ($ as) $ makeUGen
      "Lag2" calcRate
      (Vs::Vs '["in", "lagSecs"])
      (lagTime_ (0.1::Float))

-- | 'lag3 (in_ x)' is equal to 'lag (in_ $ lag (in_ $ lag (in_ x)))'
--
--   The calculation rate of this is whatever its \"in\" is
lag3 :: (Args '["in"] '["lagSecs"] a) => a -> SDBody a Signal
lag3 as =
   makeThing =<< getCalcRate =<< uaArgVal as (Proxy::Proxy "in")
 where
   makeThing calcRate = ($ as) $ makeUGen
      "Lag3" calcRate
      (Vs::Vs '["in", "lagSecs"])
      (lagTime_ (0.1::Float))

-- | Note the default for both AR and KR are the same: 0.995. In SC lang, the KR
--   one defaults to 0.9.
leakDC :: (Args '["in"] '["coef"] a) => a -> SDBody a Signal
leakDC = makeUGen
   "LeakDC" AR
   (Vs::Vs '["in", "coef"])
   (coef_ (0.995::Float))

-- also look at RLPF:
-- | Low-pass filter
lpf :: (Args '["in"] '["freq"] a) => a -> SDBody a Signal
lpf = passFilter "LPF"

lpz1 :: (Args '["in"] '[] a) => a -> SDBody a Signal
lpz1 = makeUGen
   "LPZ1" AR
   (Vs::Vs '["in"])
   NoDefaults

lpz2 :: (Args '["in"] '[] a) => a -> SDBody a Signal
lpz2 = makeUGen
   "LPZ2" AR
   (Vs::Vs '["in"])
   NoDefaults

-- | 'Db' is the boost or attenuation of the signal in decibels
midEQ :: (Args '["in", "freq", "db"] '["rq"] a) => a -> SDBody a Signal
midEQ = makeUGen
   "MidEQ" AR
   (Vs::Vs '["in", "freq", "rq", "db"])
   (rq_ (1::Float))

passFilter :: (Args '["in"] '["freq"] a) => String -> a -> SDBody a Signal
passFilter filterName = makeUGen
   filterName AR
   (Vs::Vs '["in", "freq"])
   (freq_ (440::Float))

onePole :: (Args '["in"] '["coef"] a) => a -> SDBody a Signal
onePole = makeUGen
   "OnePole" AR
   (Vs::Vs '["in", "coef"])
   (coef_ (0.5::Float))

oneZero :: (Args '["in"] '["coef"] a) => a -> SDBody a Signal
oneZero = makeUGen
   "OneZero" AR
   (Vs::Vs '["in", "coef"])
   (coef_ (0.5::Float))

rhpf :: (Args '["in"] '["freq", "rq"] a) => a -> SDBody a Signal
rhpf = makeUGen
   "RHPF" AR
   (Vs::Vs '["in", "freq", "rq"])
   (freq_ (440::Float), rq_ (1::Float))

rlpf :: (Args '["in"] '["freq", "rq"] a) => a -> SDBody a Signal
rlpf = makeUGen
   "RLPF" AR
   (Vs::Vs '["in", "freq", "rq"])
   (freq_ (440::Float), rq_ (1::Float))

ramp :: (Args '["in"] '["lagSecs"] a) => a -> SDBody a Signal
ramp = makeUGen
   "Ramp" AR
   (Vs::Vs '["in", "lagSecs"])
   (lagTime_ (0.1::Float))

resonz :: (Args '["in"] '["freq", "bwr"] a) => a -> SDBody a Signal
resonz = makeUGen
   "Resonz" AR
   (Vs::Vs '["in", "freq", "bwr"])
   (freq_ (440::Float), bwr_ (1::Float))

ringz :: (Args '["in"] '["freq", "decaySecs"] a) => a -> SDBody a Signal
ringz = makeUGen
   "Ringz" AR
   (Vs::Vs '["in", "freq", "decaySecs"])
   (freq_ (440::Float), decaySecs_ (1::Float))

slope :: (Args '["in"] '[] a) => a -> SDBody a Signal
slope = makeUGen
   "Slope" AR
   (Vs::Vs '["in"])
   NoDefaults

sos :: (Args '["in"] '["a0", "a1", "a2", "b1", "b2"] a) => a -> SDBody a Signal
sos = makeUGen
   "SOS" AR
   (Vs::Vs '["in", "a0", "a1", "a2", "b1", "b2"])
   (a0_ (0::Float), a1_ (0::Float), a2_ (0::Float), b1_ (0::Float), b2_ (0::Float))

twoPole :: (Args '["in"] '["freq", "radius"] a) => a -> SDBody a Signal
twoPole = makeUGen
   "TwoPole" AR
   (Vs::Vs '["in", "freq", "radius"])
   (freq_ (440::Float), radius_ (0.8::Float))

twoZero :: (Args '["in"] '["freq", "radius"] a) => a -> SDBody a Signal
twoZero = makeUGen
   "TwoZero" AR
   (Vs::Vs '["in", "freq", "radius"])
   (freq_ (440::Float), radius_ (0.8::Float))

--- varLag ::
--- varLag =