{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{- |
Copyright   :  (c) Henning Thielemann 2009
License     :  GPL

Maintainer  :  synthesizer@henning-thielemann.de
Stability   :  provisional
Portability :  requires multi-parameter type classes

Class similar to "Synthesizer.Dimensional.Abstraction.Homogeneous"
but it can be used for different storage types.
-}
module Synthesizer.Dimensional.Abstraction.HomogeneousGen where

import Synthesizer.Dimensional.Amplitude (Flat(Flat))
import qualified Synthesizer.Dimensional.Amplitude as Amp
import qualified Synthesizer.State.Signal as Sig
import qualified Synthesizer.Storable.Signal as SigSt
import qualified Synthesizer.Basic.WaveSmoothed as WaveSmooth
import qualified Synthesizer.Basic.Wave         as Wave
import qualified Synthesizer.Dimensional.RatePhantom as RP
import qualified Synthesizer.Dimensional.Straight.Signal as SigS
import qualified Synthesizer.Dimensional.Amplitude.Signal as SigA

{-
import qualified Number.DimensionTerm        as DN
import qualified Algebra.DimensionTerm       as Dim

import qualified Algebra.Module         as Module
import qualified Algebra.Field          as Field
import qualified Algebra.Ring           as Ring
-}

-- import Number.DimensionTerm ((&/&))

import Data.Tuple.HT (mapSnd, )

-- import NumericPrelude
-- import PreludeBase
-- import Prelude ()

{-# INLINE processSamples #-}
processSamples ::
   (C amp storage0 signal0, C amp storage1 signal1) =>
   (storage0 y0 -> storage1 y1) -> RP.T s signal0 y0 -> RP.T s signal1 y1
processSamples f =
   RP.fromSignal . plainProcessSamples f . RP.toSignal


plainProcessSamples ::
   (C amp storage0 signal0, C amp storage1 signal1) =>
   (storage0 y0 -> storage1 y1) ->
   (signal0 y0 -> signal1 y1)
plainProcessSamples f =
   plainWrap . mapSnd f . plainUnwrap


wrap ::
   (C amp storage signal) =>
   (amp, storage y) -> RP.T s signal y
wrap =
   RP.fromSignal . plainWrap

unwrap ::
   (C amp storage signal) =>
   RP.T s signal y -> (amp, storage y)
unwrap =
   plainUnwrap . RP.toSignal


{- |
Functions using this class might define their own class with functional dependencies,
that allow to infer automatically, say,
that an amplitude input signal requires an amplitude output signal.
-}
class C amp storage signal |
     signal -> amp storage where
   plainWrap   :: (amp, storage y) -> signal y
   plainUnwrap :: signal y -> (amp, storage y)

instance C Flat Sig.T Sig.T where
   plainWrap = snd
   plainUnwrap = (,) Flat

instance C Flat SigSt.T SigSt.T where
   plainWrap = snd
   plainUnwrap = (,) Flat

instance C Flat sig (SigS.T sig) where
   plainWrap = SigS.Cons . snd
   plainUnwrap = (,) Flat . SigS.samples

instance (Amp.C amp) => C amp sig (SigA.T amp (SigS.T sig)) where
   plainWrap = uncurry SigA.Cons . mapSnd SigS.Cons
   plainUnwrap (SigA.Cons amp sig) = (amp, SigS.samples sig)





{- |
These instances are used in oscillator
where we even do not need homogenity,
since values from the waveform
go untouched to the output signal.
-}

instance C Flat (Wave.T t) (Wave.T t) where
   plainWrap = snd
   plainUnwrap = (,) Flat

instance C Flat (WaveSmooth.T t) (WaveSmooth.T t) where
   plainWrap = snd
   plainUnwrap = (,) Flat

instance (Amp.C amp) => C amp (Wave.T t) (SigA.T amp (Wave.T t)) where
   plainWrap = uncurry SigA.Cons
   plainUnwrap (SigA.Cons amp sig) = (amp, sig)

instance (Amp.C amp) => C amp (WaveSmooth.T t) (SigA.T amp (WaveSmooth.T t)) where
   plainWrap = uncurry SigA.Cons
   plainUnwrap (SigA.Cons amp sig) = (amp, sig)