-- | 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 p z = z : deleteBy ((==) `on` fst) z p

{- | Merge, require keys be unique.

>>> param_merge_uniq [("a",1),("b",2)] [("c",3),("d",4)]
[("a",1.0),("b",2.0),("c",3.0),("d",4.0)]

> param_merge_uniq [("a",1)] [("a",2)] -- error
-}
param_merge_uniq :: Param -> Param -> Param
param_merge_uniq p1 p2 =
  case map fst p1 `intersect` map fst p2 of
    [] -> p1 ++ p2
    _ -> error "param_merge_uniq?"

{- | Merge, right biased.

>>> param_merge_r [("a",1),("b",2)] [("c",3),("a",4)]
[("b",2.0),("c",3.0),("a",4.0)]
-}
param_merge_r :: Param -> Param -> Param
param_merge_r p1 p2 =
  let p3 = let k2 = map fst p2 in filter (\(x, _) -> x `notElem` k2) p1
  in p3 ++ p2

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

>>> param_merge_r_seq [[("a",1),("b",2)],[("c",3),("a",4)],[("b",5)]]
[("c",3.0),("a",4.0),("b",5.0)]
-}
param_merge_r_seq :: [Param] -> Param
param_merge_r_seq = foldr1 param_merge_r

-- | Lookup parameter value, with default.
param_get :: Param -> String -> Double -> Double
param_get p k v = fromMaybe v (lookup k p)

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

>>> param_parse (';','=') "a=1;b=2"
[("a",1.0),("b",2.0)]
-}
param_parse :: (Char, Char) -> String -> Param
param_parse (c1, c2) str =
  let f x = case splitOn [c2] x of
        [lhs, rhs] -> (lhs, read rhs)
        _ -> error ("param_parse: " ++ x)
  in if null str then [] else map f (splitOn [c1] 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 (c1, c2) k =
  let f (lhs, rhs) = concat [lhs, [c2], double_pp k rhs]
  in concat . intersperse [c1] . map f
