-- High level api for playing sounds (and background music)

module Sound.SFML (
    -- * PolySounds
    PolySound,
    newPolySound,
    freePolySound,
    triggerPolySound,

    -- * LoopedSounds
    LoopedSound,
    newLoopedSound,
    freeLoopedSound,
    startLoopedSound,
    stopLoopedSound,

  ) where


import Data.Maybe
import Data.IORef

import Control.Monad

import Foreign.Ptr

import Sound.SFML.LowLevel

-- * PolySounds

-- | A PolySound allows you to trigger one sound multiple times.
-- The played sounds will then overlap.
-- (Internally, there will be multiple sound instances, that will
-- be triggered one after the other. If there are not enough internal
-- instances, sounds will be cut.)
data PolySound = PolySound FilePath (Ptr SoundBuffer) [Ptr Sound] (IORef Int)

instance Show PolySound where
    show (PolySound file _ _ _) = "PolySound " ++ show file

-- | Loads a sound into memory.
newPolySound ::
    FilePath -- ^ soundfile
    -> Int -- ^ number of internal sound instances.
    -> IO PolySound
newPolySound path numberOfVoices = do
    buffer <- sfSoundBuffer_CreateFromFile path
    sounds <- forM [1 .. numberOfVoices] $ \ _ -> do
        sound <- sfSound_Create
        sfSound_SetBuffer sound buffer
        return sound
    ref <- newIORef 0
    return $ PolySound path buffer sounds ref

-- | Frees the memory allocated by a sound. Don't use the PolySound afterwards.
freePolySound :: PolySound -> IO ()
freePolySound (PolySound _ buffer sounds _) = do
    sfSoundBuffer_Destroy buffer
    mapM_ sfSound_Destroy sounds

-- | Trigger a sound
triggerPolySound :: PolySound -> Maybe Float -> IO ()
triggerPolySound (PolySound _ _ sounds ref) volume = do
    i <- readIORef ref
    let sound = sounds !! i
    status <- getStatus sound
    when (status == Stopped) $ do
        writeIORef ref ((i + 1) `mod` length sounds)
        sfSound_SetVolume sound ((fromMaybe 1 volume) * 100)
        sfSound_Play sound


-- * LoopedSounds

-- | LoopedSounds are sounds that will always loop.
-- They can just be switched on and off.
newtype LoopedSound = LoopedSound (Ptr Sound)
  deriving Show

-- | Loads a sound into memory.
newLoopedSound :: FilePath -> IO LoopedSound
newLoopedSound path = do
    buffer <- sfSoundBuffer_CreateFromFile path
    sound <- sfSound_Create
    sfSound_SetBuffer sound buffer
    sfSound_SetLoop sound True
    return $ LoopedSound sound

-- | Releases the allocated memory of a LoopedSound.
-- Don't use the LoopedSound afterwards.
freeLoopedSound :: LoopedSound -> IO ()
freeLoopedSound (LoopedSound ptr) =
    sfSound_Destroy ptr

-- | Starts a looped sound.
startLoopedSound :: LoopedSound -> IO ()
startLoopedSound (LoopedSound ptr) =
    sfSound_Play ptr

-- | Stops a looped sound.
stopLoopedSound :: LoopedSound -> IO ()
stopLoopedSound (LoopedSound ptr) =
    sfSound_Stop ptr


-- * Music

-- There is always only one music lpaying at a single time.

-- | Loads and plays a music file once.
-- playMusic :: FilePath -> IO ()
-- playMusic = error "playMusic"

-- | Loads and plays a music file in a loop.
-- Stops if the program ends or 'stopMusic' is called.
-- playMusicLooped :: FilePath -> IO ()
-- playMusicLooped = error "playMusicLooped"

-- | Stops any background music that is playing.
-- stopMusic :: IO ()
-- stopMusic = error "stopMusic"