-- | The fixities of the operators ("~+", "~*", "~<", etc) are the same as those of
--   their non-'~' equivalents (+, *, < etc)
--
--   (So you can e.g. multiply then add without parens!)

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies, NoMonoLocalBinds #-}

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

module Vivid.UGens.Algebraic (
     (~+)
   , (~-)
   , (~*)
   , (~**)
   , (~/)

{- -- removed these to be compatible with tidal, which also uses some of these:
   , (~>)
   , (~>=)
   , (~<)
   , (~<=)
-}
   , binaryOp
   , biOp
   , unaryOp
   , uOp
   , midiCPS
   , cpsMIDI
   , abs'
   , neg
   , tanh'
   , clip2
   , xor
   ) where

import Vivid.SynthDef
-- import Vivid.UGens.Args

import Prelude

infixl 7 ~*
-- | Multiply signals
(~*) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~*) = binaryOp Mul

infixr 8 ~**
-- | Exponentiation of signals
(~**) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~**) = binaryOp Pow

infixl 6 ~+
-- | Add signals
(~+) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~+) = binaryOp Add

infixl 7 ~/
-- | Divide signals
(~/) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~/) = binaryOp FDiv

{-
infix 4 ~>
-- | Test signals for left greater than right
(~>) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~>) = binaryOp Gt

infix 4 ~>=
-- | Test signals for left greater than or equal to right
(~>=) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~>=) = binaryOp Ge

infix 4 ~<
-- | Test signals for left less than right
(~<) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~<) = binaryOp Lt

infix 4 ~<=
-- | Test signals for left less than or equal to right
(~<=) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~<=) = binaryOp Le
-}

infixl 6 ~-
-- | Subtract signals
(~-) :: (ToSig i0 a, ToSig i1 a) => i0 -> i1 -> SDBody' a Signal
(~-) = binaryOp Sub

-- | Build your own!
-- 
--   The calculation rate of the result is the larger (more frequent) of the 2 input
--   signals
--   (So you shouldn't need to use "?" very much!)
binaryOp :: (ToSig s0 a, ToSig s1 a) => BinaryOp -> s0 -> s1 -> SDBody' a Signal
binaryOp theBiOp s0 s1 = do
   s0' <- toSig s0
   s1' <-  toSig s1
   calcRate <- max <$> getCalcRate s0' <*> getCalcRate s1'
   let sigs = [s0', s1']
   addUGen $ UGen (UGName_B theBiOp) calcRate sigs 1

-- | Alias of 'binaryOp'. Shorter, fer livecodin
biOp :: (ToSig s0 a, ToSig s1 a) => BinaryOp -> s0 -> s1 -> SDBody' a Signal
biOp = binaryOp

-- | Build your own, from 'UnaryOp's
unaryOp :: (ToSig sig a) => UnaryOp -> sig -> SDBody' a Signal
unaryOp theUOp sig = do
   sig' <- toSig sig
   calcRate <- getCalcRate sig'
   addUGen $ UGen (UGName_U theUOp) calcRate [sig'] 1

-- | Alias of 'unaryOp'
uOp :: (ToSig sig a) => UnaryOp -> sig -> SDBody' a Signal
uOp = unaryOp

-- | Convert from a midi note number (0-127, each representing a musical half step) to a
--   frequency in hz (cycles per second)
midiCPS :: (ToSig i a) => i -> SDBody' a Signal
midiCPS = unaryOp MIDICPS

-- | Inverse of 'midiCPS'
cpsMIDI :: (ToSig i a) => i -> SDBody' a Signal
cpsMIDI = unaryOp CPSMIDI



-- | The prime is to not conflict with \"abs\" in the prelude. May just use
--   \"uOp Abs\" in the future
abs' :: (ToSig i a) => i -> SDBody' a Signal
abs' = unaryOp Abs

neg :: (ToSig i a) => i -> SDBody' a Signal
neg = unaryOp Neg

-- | The prime, like 'abs'', is to not conflict with a prelude definition.
-- 
--   Remember you can always just use:
-- 
--   > uOp TanH
tanh' :: ToSig i a => i -> SDBody' a Signal
tanh' i = uOp TanH i

-- | Like 'Vivid.UGens.Maths.clip' but the lo value is always negative the hi value
clip2 :: (ToSig s0 a, ToSig s1 a) => s0 -> s1 -> SDBody' a Signal
clip2 = biOp Clip2

-- | Bitwise xor. Short for @biOp BitXor@
xor :: (ToSig s0 a, ToSig s1 a) => s0 -> s1 -> SDBody' a Signal
xor = biOp BitXor