-- | The unit-generator graph structure implemented by the SuperCollider synthesis server.
module Sound.Sc3.Server.Synthdef where

import qualified Data.ByteString.Lazy as L {- bytestring -}

import qualified Sound.Sc3.Ugen.Graph as Graph {- hsc3 -}
import qualified Sound.Sc3.Ugen.Help.Graph as Help.Graph {- hsc3 -}
import qualified Sound.Sc3.Ugen.Ugen as Ugen {- hsc3 -}

import qualified Sound.Sc3.Server.Graphdef as Graphdef {- hsc3 -}
import qualified Sound.Sc3.Server.Graphdef.Binary as Graphdef.Binary {- hsc3 -}
import qualified Sound.Sc3.Server.Graphdef.Graph as Graphdef.Graph {- hsc3 -}
import qualified Sound.Sc3.Server.Param as Param {- hsc3 -}

-- | A named unit generator graph.
data Synthdef = Synthdef
  { synthdefName :: String
  , synthdefUgen :: Ugen.Ugen
  }
  deriving (Eq, Show)

-- | Alias for 'Synthdef'.
synthdef :: String -> Ugen.Ugen -> Synthdef
synthdef = Synthdef

{- | The Sc3 /default/ instrument 'Synthdef', see 'default_ugen_graph'.

> import Sound.Osc {\- hosc -\}
> import Sound.Sc3 {\- hsc3 -\}
> withSc3 (sendMessage (d_recv defaultSynthdef))
> audition defaultSynthdef
-}
defaultSynthdef :: Synthdef
defaultSynthdef = synthdef "default" Help.Graph.default_ugen_graph

{- | The Sc3 /default/ sample (buffer) playback instrument 'Synthdef',
see 'default_sampler_ugen_graph'.

> withSc3 (sendMessage (d_recv (defaultSampler False)))
> audition (defaultSampler False)
-}
defaultSampler :: Bool -> Synthdef
defaultSampler use_gate =
  let nm = "default-sampler-" ++ if use_gate then "gate" else "fixed"
  in synthdef nm (Help.Graph.default_sampler_ugen_graph use_gate)

-- | 'ugen_to_graph' of 'synthdefUgen'.
synthdefGraph :: Synthdef -> Graph.U_Graph
synthdefGraph = Graph.ugen_to_graph . synthdefUgen

{- | Parameter names at 'Synthdef'.

>>> synthdefParam defaultSynthdef
[("amp",0.1),("pan",0.0),("gate",1.0),("freq",440.0),("out",0.0)]
-}
synthdefParam :: Synthdef -> Param.Param
synthdefParam =
  map (\n -> (Graph.u_node_k_name n, Graph.u_node_k_default n))
    . Graph.ug_controls
    . synthdefGraph

-- | 'graph_to_graphdef' at 'Synthdef'.
synthdef_to_graphdef :: Synthdef -> Graphdef.Graphdef
synthdef_to_graphdef (Synthdef nm u) = Graphdef.Graph.graph_to_graphdef nm (Graph.ugen_to_graph u)

-- | 'graph_to_graphdef' at 'Synthdef'.
ugen_to_graphdef :: Ugen.Ugen -> Graphdef.Graphdef
ugen_to_graphdef = synthdef_to_graphdef . Synthdef "anonymous"

-- | Encode 'Synthdef' as a binary data stream.
synthdefData :: Synthdef -> L.ByteString
synthdefData = Graphdef.Binary.encode_graphdef . synthdef_to_graphdef

-- | Write 'Synthdef' to indicated file.
synthdefWrite :: FilePath -> Synthdef -> IO ()
synthdefWrite fn = Graphdef.Binary.graphdefWrite fn . synthdef_to_graphdef

{- | Write 'Synthdef' to indicated directory.  The filename is the
'synthdefName' with the appropriate extension (@scsyndef@).
-}
synthdefWrite_dir :: FilePath -> Synthdef -> IO ()
synthdefWrite_dir dir = Graphdef.Binary.graphdefWrite_dir dir . synthdef_to_graphdef

-- | 'graph_stat_ln' of 'synth'.
synthstat_ln :: Ugen.Ugen -> [String]
synthstat_ln = Graph.ug_stat_ln . Graph.ugen_to_graph

-- | 'unlines' of 'synthstat_ln'.
synthstat :: Ugen.Ugen -> String
synthstat = unlines . synthstat_ln

{- | 'putStrLn' of 'synthstat'.

> synthstat_wr Sound.Sc3.Ugen.Help.Graph.default_ugen_graph
-}
synthstat_wr :: Ugen.Ugen -> IO ()
synthstat_wr = putStrLn . synthstat

{- | Variant without Ugen sequence.

> putStrLn $ synthstat_concise (default_sampler_ugen_graph True)
-}
synthstat_concise :: Ugen.Ugen -> String
synthstat_concise = unlines . reverse . drop 1 . reverse . synthstat_ln

-- | 'graphdef_dump_ugens' of 'ugen_to_graphdef'
ugen_dump_ugens :: Ugen.Ugen -> IO ()
ugen_dump_ugens = Graphdef.graphdef_dump_ugens . ugen_to_graphdef
