-- | **Note:** The argument format for these is a little
--   rough, and is likely to change in the future

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}

{-# LANGUAGE FlexibleContexts #-}

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

module Vivid.UGens.Envelopes (
     -- In UGens.Filters.Linear:
   -- decay
     -- In UGens.Filters.Linear:
   -- , decay2
     -- In Vivid.UGens.Demand:
   -- , demandEnvGen
     adsrGen
---   , dadsrGen
   , envGate
   , envGen
   , envGen_wGate
---   , iEnvGen
   , line
   , linen
   , percGen
   , xLine
   ) where

import Vivid.Envelopes
import Vivid.SynthDef
import Vivid.SynthDef.FromUA
-- import Vivid.SynthDef.TypesafeArgs
import Vivid.UGens.Args
import Vivid.UGens.Algebraic

import GHC.TypeLits
-- import qualified Data.Map as Map
import Data.Monoid
import Data.Proxy

-- | Defaults to 'AR'
adsrGen :: (Args '[] '["peakLevel", {- "curve", -} "bias", "gate", "doneAction"] as, ToSig attackTime (SDBodyArgs as), ToSig delayTime (SDBodyArgs as), ToSig sustainLevel (SDBodyArgs as), ToSig releaseTime (SDBodyArgs as)) => attackTime -> delayTime -> sustainLevel -> releaseTime -> EnvCurve -> as -> SDBody as Signal
adsrGen attackTime decayTime sustainLevel releaseTime curve userArgs = do
   attackTime' <- toSig attackTime
   decayTime' <- toSig decayTime
   sustainLevel' <- toSig sustainLevel
   releaseTime' <- toSig releaseTime
   peakLevel <- uaArgValWDefault (1::Float) userArgs (Proxy::Proxy "peakLevel")
   bias <- uaArgValWDefault (0::Float) userArgs (Proxy::Proxy "bias")
   doneAction <- uaArgValWDefault (0::Float) userArgs (Proxy::Proxy "doneAction")
   gate <- uaArgValWDefault (1::Float) userArgs (Proxy::Proxy "gate")

   peakXSustain <- peakLevel ~* sustainLevel'
   let plusBias :: (Signal, Signal) -> SDBody' a (Signal, Signal)
       plusBias (a, b) = do
          a' <- bias ~+ a
          return (a', b)
   biasPlusCurveSegs <- mapM plusBias [
           (peakLevel, attackTime')
         , (peakXSustain, decayTime')
         , (Constant 0, releaseTime')
         ]
-- maybe write in terms of 'dadsr' ^^
   signals <- envLiterallyToSignals $ EnvLiterally {
        _envLiterally_initialVal = bias
      , _envLiterally_releaseNode = Just 2
      , _envLiterally_offset = 0
      , _envLiterally_loopNode = Nothing
      , _envLiterally_curveSegments =
         map (\(a,b)->EnvSegment a b curve) biasPlusCurveSegs
      }
   addUGen $ UGen (UGName_S "EnvGen") AR ([
        gate
      , Constant 1 -- levelScale
      , Constant 0 -- levelBias
      , Constant 1 -- timeScale
      , doneAction -- doneActionNum doneAction
      ] <> signals) 1

--- dadsrGen ::
--- dadsrGen =


envGate :: Subset '["gate","fadeSecs"] a => SDBody' a Signal
envGate = do
   gate <- (V::V "fadeSecs") ~<= (0::Float)
   let theEnv = EnvLiterally {
            _envLiterally_initialVal = gate
          , _envLiterally_releaseNode = Just 1
          , _envLiterally_offset = 0
          , _envLiterally_loopNode = Nothing
          , _envLiterally_curveSegments = [
               EnvSegment (Constant 1) (Constant 1) Curve_Sin
             , EnvSegment (Constant 0) (Constant 1) Curve_Sin
             ]
          }

   envGen_wGate (V::V "gate") (V::V "fadeSecs") theEnv FreeEnclosing

-- | Defaults to 'AR'
envGen :: EnvLiterally a -> DoneAction -> SDBody' a Signal
envGen theEnv doneAction = do
   curveSignals <- envLiterallyToSignals theEnv
   addUGen $ UGen (UGName_S "EnvGen") AR ([
        Constant 1 -- gate
      , Constant 1 -- levelScale
      , Constant 0 -- levelBias
      , Constant 1 -- timeScale
      , Constant $ doneActionNum doneAction
      ] <> curveSignals) 1

envGen_wGate :: (ToSig gate a, ToSig timeScale a) => gate -> timeScale -> EnvLiterally a -> DoneAction -> SDBody' a Signal
envGen_wGate gate timeScale theEnv doneAction = do
   gate' <- toSig gate
   timeScale' <- toSig timeScale
   curveSignals <- envLiterallyToSignals theEnv
   addUGen $ UGen (UGName_S "EnvGen") AR ([
        gate' -- Constant 1 -- gate
      , Constant 1 -- levelScale
      , Constant 0 -- levelBias
      , timeScale' -- , Constant 1 -- timeScale
      , Constant $ doneActionNum doneAction
      ] <> curveSignals) 1


--- iEnvGen ::
--- iEnvGen =

-- | \"Generates a line from the start value to the end value.\"
-- 
--   Note this won't change after it's created, so if you'd like
--   to e.g. be able to change the \"freq\" in
-- 
--   > line (start_ 0, end_ (A::A "freq"))
-- 
--   you should write
-- 
--   > (A::A "freq") ~* line (start_ 0, end_ 1)
-- 
--   instead.
-- 
--   Defaults to KR
line :: (Args '[] '["start","end","secs","doneAction"] a) => a -> SDBody a Signal
line = makeUGen
   "Line" AR
   (Vs::Vs '["start","end","secs","doneAction"])
   (start_ (0::Float), end_ (0::Float), secs_ (1::Float), doneAction_ (0::Float))

-- | "Simple linear envelope generator"
-- 
--   Can't change after it's created -- see the note about 'line' if you want it to
-- 
--   Only computes at "KR"
linen :: (Args '[] '["gate", "attackSecs", "susLevel", "releaseSecs", "doneAction"] a) => a -> SDBody a Signal
linen = makeUGen
   "Linen" KR
   (Vs::Vs '["gate", "attackSecs", "susLevel", "releaseSecs", "doneAction"])
   (gate_ (1::Float), attackTime_ (0.01::Float), susLevel_ (1::Float), releaseTime_ (1::Float), doneAction_ (0::Float))


-- | Percussive hit
-- 
--   'doneAction' is currently 2 but may either be 0 or 2 in future versions
percGen :: (Args '[] '["attackSecs", "releaseSecs", "level", "curve", "doneAction"] a) => a -> SDBody a Signal
percGen userArgs = do
   level <- uaArgWDef_onlyConst (1::Float) userArgs (V::V "level")
   attackTime <- uaArgWDef_onlyConst (0.01::Float) userArgs (V::V "attackSecs")
   releaseTime <- uaArgWDef_onlyConst (1::Float) userArgs (V::V "releaseSecs")
   curve <- uaArgWDef_onlyConst (-4::Float) userArgs (V::V "curve")
   doneAction <- fromEnum <$> uaArgWDef_onlyConst (2::Float) userArgs (V::V "doneAction")

   envGen (env 0 [(level, attackTime), (0, releaseTime)] (Curve_Curve curve)) (DoneAction_AsNum doneAction)
 where
   uaArgWDef_onlyConst defaultVal args argName =
      uaArgValWDefault defaultVal args argName >>= \case
         Constant x -> return x
         _ -> error $ "bad argument type: "<>show (symbolVal argName)<>" wasn't a Constant"

-- | \"Generates an exponential curve from the start value to the end value. Both the start and end values must be non-zero and have the same sign.\"
-- 
-- Defaults to KR
xLine :: (Args '[] '["start","end","secs","doneAction"] a) => a -> SDBody a Signal
xLine = makeUGen
   "XLine" KR
   (Vs::Vs '["start","end","secs","doneAction"])
   (start_ (1::Float), end_ (2::Float), secs_ (1::Float), doneAction_ (0::Float))