-----------------------------------------------------------------------------
-- |
-- Module      :  Graphics.UI.SDL.Mixer.Music
-- Copyright   :  (c) David Himmelstrup 2005
-- License     :  BSD-like
--
-- Maintainer  :  lemmih@gmail.com
-- Stability   :  provisional
-- Portability :  portable
--
-----------------------------------------------------------------------------
module Graphics.UI.SDL.Mixer.Music
    ( freeMusic
    , tryLoadMUS
    , loadMUS
    , tryPlayMusic
    , playMusic
    , tryFadeInMusic
    , fadeInMusic
    , tryFadeInMusicPos
    , fadeInMusicPos
    , setMusicVolume
    , getMusicVolume
    , modifyMusicVolume
    , pauseMusic
    , resumeMusic
    , rewindMusic
    , trySetMusicPosition
    , setMusicPosition
    , trySetMusicCmd
    , setMusicCmd
    , disableMusicCmd
    , haltMusic
    , tryFadeOutMusic
    , fadeOutMusic
    , getMusicType
    , playingMusic
    , pausedMusic
    , fadingMusic
    ) where


import Foreign(Ptr, FunPtr, nullPtr, toBool, withForeignPtr, newForeignPtr)
import Foreign.C(withCString, CString)

import Graphics.UI.SDL.Mixer.Types(Fading, MusicType, Music, MusicStruct)
import Graphics.UI.SDL.General(unwrapMaybe, unwrapBool)

-- void Mix_FreeMusic(Mix_Music *music)
foreign import ccall unsafe "&Mix_FreeMusic" mixFreeMusicFinal :: FunPtr (Ptr MusicStruct -> IO ())

mkFinalizedMusic :: Ptr MusicStruct -> IO Music
mkFinalizedMusic = newForeignPtr mixFreeMusicFinal

foreign import ccall unsafe "Mix_FreeMusic" mixFreeMusic :: Ptr MusicStruct -> IO ()
freeMusic :: Music -> IO ()
freeMusic music = withForeignPtr music mixFreeMusic

-- Mix_Music *Mix_LoadMUS(const char *file)
foreign import ccall unsafe "Mix_LoadMUS" mixLoadMUS :: CString -> IO (Ptr MusicStruct)
tryLoadMUS :: FilePath -> IO (Maybe Music)
tryLoadMUS path
    = withCString path $ \cPath ->
      do music <- mixLoadMUS cPath
         if music == nullPtr
            then return Nothing
            else fmap Just (mkFinalizedMusic music)

loadMUS :: FilePath -> IO Music
loadMUS = unwrapMaybe "Mix_LoadMUS" . tryLoadMUS

-- int Mix_PlayMusic(Mix_Music *music, int loops)
foreign import ccall unsafe "Mix_PlayMusic" mixPlayMusic :: Ptr MusicStruct -> Int -> IO Int
tryPlayMusic :: Music -> Int -> IO Bool
tryPlayMusic music loops
    = withForeignPtr music $ \musicPtr ->
      fmap (==0) (mixPlayMusic musicPtr loops)

playMusic :: Music -> Int -> IO ()
playMusic music loops = unwrapBool "Mix_PlayMusic" (tryPlayMusic music loops)

tryFadeInMusic :: Music -> Int -> Int -> IO Bool
tryFadeInMusic music loops ms
    = tryFadeInMusicPos music loops ms 0

fadeInMusic :: Music -> Int -> Int -> IO ()
fadeInMusic music loops ms
    = fadeInMusicPos music loops ms 0

-- int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position)
foreign import ccall unsafe "Mix_FadeInMusicPos" mixFadeInMusicPos
    :: Ptr MusicStruct -> Int -> Int -> Double -> IO Int
tryFadeInMusicPos :: Music -> Int -> Int -> Double -> IO Bool
tryFadeInMusicPos music loops ms pos
    = withForeignPtr music $ \musicPtr ->
      fmap (==0) (mixFadeInMusicPos musicPtr loops ms pos)

fadeInMusicPos :: Music -> Int -> Int -> Double -> IO ()
fadeInMusicPos music loops ms pos
    = unwrapBool "Mix_FadeInMusic" (tryFadeInMusicPos music loops ms pos)

-- int Mix_VolumeMusic(int volume)
foreign import ccall unsafe "Mix_VolumeMusic" mixVolumeMusic :: Int -> IO Int

setMusicVolume :: Int -> IO ()
setMusicVolume volume = mixVolumeMusic volume >> return ()

getMusicVolume :: IO Int
getMusicVolume = mixVolumeMusic (-1)

modifyMusicVolume :: (Int -> Int) -> IO ()
modifyMusicVolume fn = getMusicVolume >>= setMusicVolume . fn

-- void Mix_PauseMusic()
foreign import ccall unsafe "Mix_PauseMusic" pauseMusic :: IO ()

-- void Mix_ResumeMusic()
foreign import ccall unsafe "Mix_ResumeMusic" resumeMusic :: IO ()

-- void Mix_RewindMusic()
foreign import ccall unsafe "Mix_RewindMusic" rewindMusic :: IO ()

-- int Mix_SetMusicPosition(double position)
foreign import ccall unsafe "Mix_SetMusicPosition" mixSetMusicPosition :: Double -> IO Int

trySetMusicPosition :: Double -> IO Bool
trySetMusicPosition = fmap (==0) . mixSetMusicPosition

setMusicPosition :: Double -> IO ()
setMusicPosition = unwrapBool "Mix_SetMusicPosition" . trySetMusicPosition

-- int Mix_SetMusicCMD(const char *command)
foreign import ccall unsafe "Mix_SetMusicCMD" mixSetMusicCmd :: CString -> IO Int

trySetMusicCmd :: String -> IO Bool
trySetMusicCmd cmd
    = withCString cmd $ \cString ->
      fmap (==0) (mixSetMusicCmd cString)

setMusicCmd :: String -> IO ()
setMusicCmd cmd = unwrapBool "Mix_SetMusicCMD" (trySetMusicCmd cmd)

disableMusicCmd :: IO ()
disableMusicCmd = mixSetMusicCmd nullPtr >> return ()

-- int Mix_HaltMusic()
foreign import ccall unsafe "Mix_HaltMusic" mixHaltMusic :: IO Int
haltMusic :: IO ()
haltMusic = mixHaltMusic >> return ()

-- int Mix_FadeOutMusic(int ms)
foreign import ccall unsafe "Mix_FadeOutMusic" mixFadeOutMusic :: Int -> IO Int
tryFadeOutMusic :: Int -> IO Bool
tryFadeOutMusic ms = fmap (==1) (mixFadeOutMusic ms)

fadeOutMusic :: Int -> IO ()
fadeOutMusic ms = unwrapBool "Mix_FadeOutMusic" (tryFadeOutMusic ms)

-- Mix_MusicType Mix_GetMusicType(const Mix_Music *music)
foreign import ccall unsafe "Mix_GetMusicType" mixGetMusicType :: Ptr MusicStruct -> IO Int

getMusicType :: Maybe Music -> IO MusicType
getMusicType mbMusic
    = withMusic mbMusic $ \musicPtr ->
      fmap toEnum (mixGetMusicType musicPtr)
    where withMusic Nothing action = action nullPtr
          withMusic (Just music) action = withForeignPtr music action

-- int Mix_PlayingMusic()
foreign import ccall unsafe "Mix_PlayingMusic" mixPlayingMusic :: IO Int

playingMusic :: IO Bool
playingMusic = fmap toBool mixPlayingMusic

-- int Mix_PausedMusic()
foreign import ccall unsafe "Mix_PausedMusic" mixPausedMusic :: IO Int

pausedMusic :: IO Bool
pausedMusic = fmap toBool mixPausedMusic

-- Mix_Fading Mix_FadingMusic()
foreign import ccall unsafe "Mix_FadingMusic" mixFadingMusic :: IO Int

fadingMusic :: IO Fading
fadingMusic = fmap toEnum mixFadingMusic