{-# LANGUAGE TypeFamilies #-}
module Synthesizer.Dimensional.Sample where

import qualified Synthesizer.Dimensional.Amplitude as Amp

{- |
The constructor is only needed for 'arr',
which is a kind of a hack.
-}
data T amp yv = Cons amp yv

cons :: Amp.C amp => amp -> yv -> T amp yv
cons :: forall amp yv. C amp => amp -> yv -> T amp yv
cons = forall amp yv. amp -> yv -> T amp yv
Cons

type Dimensional v y yv = T (Amp.Dimensional v y) yv
type Numeric     amp yv = T (Amp.Numeric amp) yv
type Flat     y = T (Amp.Flat y) y
type Abstract y = T Amp.Abstract y


{- |
When you define additional instances,
take care that displacements and amplitudes cannot be brought out of order!
-}
type family Amplitude sample
type instance Amplitude (T amp yv) = amp
type instance Amplitude (sample0, sample1) =
   (Amplitude sample0, Amplitude sample1)
type instance Amplitude (sample0, sample1, sample2) =
   (Amplitude sample0, Amplitude sample1, Amplitude sample2)

type family Displacement sample
type instance Displacement (T amp yv) = yv
type instance Displacement (sample0, sample1) =
   (Displacement sample0, Displacement sample1)
type instance Displacement (sample0, sample1, sample2) =
   (Displacement sample0, Displacement sample1, Displacement sample2)


class Build sample where
   build :: Amplitude sample -> Displacement sample -> sample

instance Build (T amp yv) where
   {-# INLINE build #-}
   build :: Amplitude (T amp yv) -> Displacement (T amp yv) -> T amp yv
build = forall amp yv. amp -> yv -> T amp yv
Cons

instance
   (Build sample0, Build sample1) =>
      Build (sample0, sample1) where
   {-# INLINE build #-}
   build :: Amplitude (sample0, sample1)
-> Displacement (sample0, sample1) -> (sample0, sample1)
build (Amplitude sample0
amp0,Amplitude sample1
amp1) (Displacement sample0
yv0,Displacement sample1
yv1) =
      (forall sample.
Build sample =>
Amplitude sample -> Displacement sample -> sample
build Amplitude sample0
amp0 Displacement sample0
yv0, forall sample.
Build sample =>
Amplitude sample -> Displacement sample -> sample
build Amplitude sample1
amp1 Displacement sample1
yv1)

instance
   (Build sample0, Build sample1, Build sample2) =>
      Build (sample0, sample1, sample2) where
   {-# INLINE build #-}
   build :: Amplitude (sample0, sample1, sample2)
-> Displacement (sample0, sample1, sample2)
-> (sample0, sample1, sample2)
build (Amplitude sample0
amp0,Amplitude sample1
amp1,Amplitude sample2
amp2) (Displacement sample0
yv0,Displacement sample1
yv1,Displacement sample2
yv2) =
      (forall sample.
Build sample =>
Amplitude sample -> Displacement sample -> sample
build Amplitude sample0
amp0 Displacement sample0
yv0, forall sample.
Build sample =>
Amplitude sample -> Displacement sample -> sample
build Amplitude sample1
amp1 Displacement sample1
yv1, forall sample.
Build sample =>
Amplitude sample -> Displacement sample -> sample
build Amplitude sample2
amp2 Displacement sample2
yv2)


class Inspect sample where
   {- method names are chosen analogously to the type functions -}
   amplitude :: sample -> Amplitude sample
   displacement :: sample -> Displacement sample

instance Inspect (T amp yv) where
   {-# INLINE amplitude #-}
   {-# INLINE displacement #-}
   amplitude :: T amp yv -> Amplitude (T amp yv)
amplitude (Cons amp
amp yv
_) = amp
amp
   displacement :: T amp yv -> Displacement (T amp yv)
displacement (Cons amp
_ yv
yv) = yv
yv

instance
   (Inspect sample0, Inspect sample1) =>
      Inspect (sample0, sample1) where
   {-# INLINE amplitude #-}
   {-# INLINE displacement #-}
   amplitude :: (sample0, sample1) -> Amplitude (sample0, sample1)
amplitude (sample0
sample0, sample1
sample1) =
      (forall sample. Inspect sample => sample -> Amplitude sample
amplitude sample0
sample0, forall sample. Inspect sample => sample -> Amplitude sample
amplitude sample1
sample1)
   displacement :: (sample0, sample1) -> Displacement (sample0, sample1)
displacement (sample0
sample0, sample1
sample1) =
      (forall sample. Inspect sample => sample -> Displacement sample
displacement sample0
sample0, forall sample. Inspect sample => sample -> Displacement sample
displacement sample1
sample1)

instance
   (Inspect sample0, Inspect sample1, Inspect sample2) =>
      Inspect (sample0, sample1, sample2) where
   {-# INLINE amplitude #-}
   {-# INLINE displacement #-}
   amplitude :: (sample0, sample1, sample2)
-> Amplitude (sample0, sample1, sample2)
amplitude (sample0
sample0, sample1
sample1, sample2
sample2) =
      (forall sample. Inspect sample => sample -> Amplitude sample
amplitude sample0
sample0, forall sample. Inspect sample => sample -> Amplitude sample
amplitude sample1
sample1, forall sample. Inspect sample => sample -> Amplitude sample
amplitude sample2
sample2)
   displacement :: (sample0, sample1, sample2)
-> Displacement (sample0, sample1, sample2)
displacement (sample0
sample0, sample1
sample1, sample2
sample2) =
      (forall sample. Inspect sample => sample -> Displacement sample
displacement sample0
sample0, forall sample. Inspect sample => sample -> Displacement sample
displacement sample1
sample1, forall sample. Inspect sample => sample -> Displacement sample
displacement sample2
sample2)