-- | Synthesis parameters
module Sound.Sc3.Server.Param where

import Data.Function {- base -}
import Data.List {- base -}
import Data.Maybe {- base -}

import Data.List.Split {- split -}

import Sound.Sc3.Common.Math {- hsc3 -}

-- | An Sc3 synthesis parameter, ie. (controlName, controlValue).
type Param1 = (String, Double)

-- | Set of Sc3 synthesiser parameters.
type Param = [Param1]

-- | Add new, or overwrite existing, parameter.
param_insert :: Param -> Param1 -> Param
param_insert :: Param -> Param1 -> Param
param_insert Param
p Param1
z = Param1
z forall a. a -> [a] -> [a]
: forall a. (a -> a -> Bool) -> a -> [a] -> [a]
deleteBy (forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall a b. (a, b) -> a
fst) Param1
z Param
p

{- | Merge, require keys be unique.

> param_merge_uniq [("a",1),("b",2)] [("c",3),("d",4)] == [("a",1),("b",2),("c",3),("d",4)]
> param_merge_uniq [("a",1)] [("a",2)] -- error
-}
param_merge_uniq :: Param -> Param -> Param
param_merge_uniq :: Param -> Param -> Param
param_merge_uniq Param
p1 Param
p2 =
  case forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst Param
p1 forall a. Eq a => [a] -> [a] -> [a]
`intersect` forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst Param
p2 of
    [] -> Param
p1 forall a. [a] -> [a] -> [a]
++ Param
p2
    [String]
_ -> forall a. HasCallStack => String -> a
error String
"param_merge_uniq?"

{- | Merge, right biased.

> param_merge_r [("a",1),("b",2)] [("c",3),("a",4)] == [("b",2),("c",3),("a",4)]
-}
param_merge_r :: Param -> Param -> Param
param_merge_r :: Param -> Param -> Param
param_merge_r Param
p1 Param
p2 =
    let p3 :: Param
p3 = let k2 :: [String]
k2 = forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst Param
p2 in forall a. (a -> Bool) -> [a] -> [a]
filter (\(String
x,Double
_) -> String
x forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
k2) Param
p1
    in Param
p3 forall a. [a] -> [a] -> [a]
++ Param
p2

{- | Right-fold (right-biased) of 'param_merge'

> param_merge_r_seq [[("a",1),("b",2)],[("c",3),("a",4)],[("b",5)]] == [("c",3),("a",4),("b",5)]
-}
param_merge_r_seq :: [Param] -> Param
param_merge_r_seq :: [Param] -> Param
param_merge_r_seq = forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 Param -> Param -> Param
param_merge_r

-- | Lookup parameter value, with default.
param_get :: Param -> String -> Double -> Double
param_get :: Param -> String -> Double -> Double
param_get Param
p String
k Double
v = forall a. a -> Maybe a -> a
fromMaybe Double
v (forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
k Param
p)

{- | Given (param-separator,key-value-separator) parse paramter string.

> param_parse (';','=') "a=1;b=2" == [("a",1),("b",2)]
-}
param_parse :: (Char,Char) -> String -> Param
param_parse :: (Char, Char) -> String -> Param
param_parse (Char
c1,Char
c2) String
str =
    let f :: String -> (String, b)
f String
x = case forall a. Eq a => [a] -> [a] -> [[a]]
splitOn [Char
c2] String
x of
                [String
lhs,String
rhs] -> (String
lhs,forall a. Read a => String -> a
read String
rhs)
                [String]
_ -> forall a. HasCallStack => String -> a
error (String
"param_parse: " forall a. [a] -> [a] -> [a]
++ String
x)
    in if forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
str then [] else forall a b. (a -> b) -> [a] -> [b]
map forall {b}. Read b => String -> (String, b)
f (forall a. Eq a => [a] -> [a] -> [[a]]
splitOn [Char
c1] String
str)

{- | Inverse of 'param_parse', /k/ is the precision to print values to.

> param_pp (';','=') 4 [("a",1),("b",2)] == "a=1.0;b=2.0"
-}
param_pp :: (Char,Char) -> Int -> Param -> String
param_pp :: (Char, Char) -> Int -> Param -> String
param_pp (Char
c1,Char
c2) Int
k =
    let f :: Param1 -> String
f (String
lhs,Double
rhs) = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String
lhs,[Char
c2],Int -> Double -> String
double_pp Int
k Double
rhs]
    in forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> [a] -> [a]
intersperse [Char
c1] forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Param1 -> String
f