{-# LANGUAGE FlexibleInstances #-}
module Sound.SC3.Server.Process.CommandLine (
    rtCommandLine
  , nrtCommandLine
) where

import Data.Default (Default, def)
import Data.List (intercalate)
import Data.Maybe (fromMaybe)
import Sound.SC3.Server.Enum
import Sound.SC3.Server.Process.Options

-- ====================================================================
-- scsynth commandline options

-- | Convert a value to an option string.
class Show a => Option a where
    showOption :: a -> String

instance Option String where
    showOption = id

instance Option Int where
    showOption = show

instance Option Bool where
    showOption = showOption . fromEnum

instance Option a => Option (Maybe a) where
    showOption Nothing  = ""
    showOption (Just a) = showOption a

instance Option Verbosity where
    showOption = showOption . fromEnum

instance Option [FilePath] where
    showOption = intercalate ":"

instance Option NetworkPort where
  showOption (UDPPort p) = show p
  showOption (TCPPort p) = show p

instance Option SoundFileFormat where
    showOption = soundFileFormatString

instance Option SampleFormat where
    showOption = sampleFormatString

option :: (Default a, Option b, Eq b) => a -> String -> (a -> b) -> Maybe (String, String)
option options flag accessor =
      if value == accessor def
      then Nothing
      else Just (flag, showOption value)
    where
      value = accessor options

flatten :: [Maybe (a, a)] -> [a]
flatten [] = []
flatten (Nothing:xs) = flatten xs
flatten (Just (a, b):xs) = a : b : flatten xs

mkServerOptions :: ServerOptions -> [String]
mkServerOptions options = flatten [
    option options "-c" numberOfControlBusChannels
  , option options "-a" numberOfAudioBusChannels
  , option options "-i" numberOfInputBusChannels
  , option options "-o" numberOfOutputBusChannels
  , option options "-z" blockSize
  , option options "-b" numberOfSampleBuffers
  , option options "-n" maxNumberOfNodes
  , option options "-d" maxNumberOfSynthDefs
  , option options "-m" realtimeMemorySize
  , option options "-w" numberOfWireBuffers
  , option options "-r" numberOfRandomSeeds
  , option options "-D" loadSynthDefs
  , option options "-v" verbosity
  , option options "-U" ugenPluginPath
  , option options "-P" restrictedPath ]

mkRTOptions :: RTOptions -> [String]
mkRTOptions options = flatten $
    [ case networkPort options of
        -- Don't use 'option' here because one of these _has_ to be present!
        p@(UDPPort _) -> Just ("-u", showOption p)
        p@(TCPPort _) -> Just ("-t", showOption p) ]
    ++
    [ option options "-R" useZeroconf
    , option options "-H" hardwareDeviceName
    , option options "-Z" hardwareBufferSize
    , option options "-S" hardwareSampleRate
    , option options "-l" maxNumberOfLogins
    , option options "-p" sessionPassword
    , option options "-I" inputStreamsEnabled
    , option options "-O" outputStreamsEnabled ]

mkNRTOptions :: NRTOptions -> Maybe FilePath -> [String]
mkNRTOptions options commandFilePath =
    "-N" : [
          fromMaybe "_" commandFilePath
        , fromMaybe "_" (inputFilePath options)
        , outputFilePath options
        , showOption (outputSampleRate options)
        , showOption (outputSoundFileFormat options)
        , showOption (outputSampleFormat options) ]

-- | Construct the scsynth command line from 'ServerOptions' and 'RTOptions'.
rtCommandLine :: ServerOptions -> RTOptions -> [String]
rtCommandLine serverOptions rtOptions =
    (serverProgram serverOptions : mkServerOptions serverOptions)
 ++ mkRTOptions rtOptions

-- | Construct the scsynth command line from 'ServerOptions' and 'NRTOptions'.
nrtCommandLine :: ServerOptions -> NRTOptions -> Maybe FilePath -> [String]
nrtCommandLine serverOptions nrtOptions commandFilePath =
    (serverProgram serverOptions : mkServerOptions serverOptions)
 ++ mkNRTOptions nrtOptions commandFilePath