-- |
-- Module      :  DobutokO.Sound.Effects.Reverb
-- Copyright   :  (c) OleksandrZhabenko 2020
-- License     :  MIT
-- Stability   :  Experimental
-- Maintainer  :  olexandr543@yahoo.com
--
-- Helps to create experimental music. 
-- Can be used for applying the SoX \"reverb\" and \"reverse\" effects. 
-- 

{-# OPTIONS_GHC -threaded #-}
{-# LANGUAGE CPP, FlexibleInstances #-}

module DobutokO.Sound.Effects.Reverb where

#ifdef __GLASGOW_HASKELL__
#if __GLASGOW_HASKELL__>=710
/* code that applies only to GHC 7.10.* and higher versions */
import GHC.Base (mconcat)
#endif
#endif
import Numeric (showFFloat)
import Data.List (intersperse)
import qualified DobutokO.Sound.Frequency as FQ

#ifdef __GLASGOW_HASKELL__
#if __GLASGOW_HASKELL__==708
/* code that applies only to GHC 7.8.* */
mconcat = concat
#endif
#endif

data Four = O4 | T4 | H4 | F4 deriving Eq

data Reverb a b c d =  Rvrb a b [c] d deriving Eq

instance Show (Reverb Four FQ.Di Float Int) where
  show (Rvrb variant wet xs n)
   | compare n 0 == LT = error $ "DobutokO.Sound.Effects.Reverb.show is not defined for the value of the last argument " ++ show n
   | otherwise =
      let (zs, ks) = splitAt 4 xs in
        let ys = map FQ.toRange100 zs in
             if null ks
               then case variant of
                       O4 -> mconcat ["reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys]
                       T4 -> mconcat ["reverse reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," reverse"]
                       H4 -> mconcat ["reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," reverse"]
                       _  -> mconcat ["reverse reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys]
               else let r5 = head ks in if null . tail $ ks then
                      case variant of
                        O4 -> mconcat ["reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys, " ",
                          showFFloat (Just n) r5 ""]
                        T4 -> mconcat ["reverse reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," ",
                          showFFloat (Just n) r5 ""," reverse"]
                        H4 -> mconcat ["reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," ",
                          showFFloat (Just n) r5 ""," reverse"]
                        _  -> mconcat ["reverse reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," ",
                          showFFloat (Just n) r5 ""]
                     else let r60 = last ks in
                            let r6 = (r60 / 120.0 - (fromIntegral . truncate $ r60 / 120.0)) * 120.0 in
                                 case variant of
                                   O4 -> mconcat ["reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys, " ",
                                     showFFloat (Just n) r5 ""," ",showFFloat (Just n) r6 ""]
                                   T4 -> mconcat ["reverse reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," ",
                                     showFFloat (Just n) r5 " ",showFFloat (Just n) r6 " reverse"]
                                   H4 -> mconcat ["reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," ",
                                     showFFloat (Just n) r5 " ",showFFloat (Just n) r6 " reverse"]
                                   _  -> mconcat ["reverse reverb ", if wet == FQ.O then "-w " else "", mconcat . intersperse " " . map (\x -> showFFloat (Just n) x " ") $ ys," ",
                                     showFFloat (Just n) r5 " ",showFFloat (Just n) r6 ""]

reverb1 :: Reverb a b c d -> a
reverb1 (Rvrb x _ _ _) = x

reverb2 :: Reverb a b c d -> b
reverb2 (Rvrb _ y _ _) = y

reverb3 :: Reverb a b c d -> [c]
reverb3 (Rvrb _ _ zs _) = take 6 zs

reverb4 :: Reverb a b c d -> d
reverb4 (Rvrb _ _ _ t) = t

type ReverbE = Reverb Four FQ.Di Float Int

reverb3E :: Int -> ReverbE -> Float
reverb3E n x
 | compare n 0 == GT && compare n 7 == LT = if null . drop (n - 1) . reverb3 $ x then 50.0 * fromIntegral ((((n - 1) `quot` 2) + 1) `rem` 3) else reverb3 x !! (n - 1)
 | otherwise = error "DobutokO.Sound.Effects.Reverb.reverb3E: Not defined parameter. "

reverbSet1 :: a -> Reverb a b c d -> Reverb a b c d
reverbSet1 x (Rvrb _ y zs t) = Rvrb x y zs t

reverbSet2 :: b -> Reverb a b c d -> Reverb a b c d
reverbSet2 y (Rvrb x _ zs t) = Rvrb x y zs t

reverbSet3 :: [c] -> Reverb a b c d -> Reverb a b c d
reverbSet3 zs (Rvrb x y _ t) = Rvrb x y (take 6 zs) t

reverbSet4 :: d -> Reverb a b c d -> Reverb a b c d
reverbSet4 t (Rvrb x y zs _) = Rvrb x y zs t

reverbSet3E :: Int -> Float -> ReverbE -> ReverbE
reverbSet3E n y x
 | compare n 0 == GT && compare n 5 == LT = Rvrb (reverb1 x) (reverb2 x) (mconcat [take (n - 1) . reverb3 $ x, [FQ.toRange100 y], drop n . reverb3 $ x]) (reverb4 x)
 | n == 5 = Rvrb (reverb1 x) (reverb2 x) (mconcat [take 4 . reverb3 $ x, [y], drop 5 . reverb3 $ x]) (reverb4 x)
 | n == 6 = Rvrb (reverb1 x) (reverb2 x) (mconcat [take 5 . reverb3 $ x, [y / 120.0 - (fromIntegral . truncate $ y / 120.0) * 120.0]]) (reverb4 x)
 | otherwise = error "DobutokO.Sound.Effects.Reverb.reverbSet3E: The first argument is out of range [1..6]. "

showQReverb :: ReverbE -> [String]
showQReverb = words . show