import System.Exit
import Data.Maybe
import qualified Media.Streaming.GStreamer as Gst
import qualified System.Glib as G
import qualified System.Glib.MainLoop as G
import qualified System.Glib.Properties as G
import qualified System.Glib.GError as G
import qualified System.Glib.Signals as G
import Text.Printf
import Control.Monad
import System.IO
import System.Environment

mkElement action =
    do element <- action
       case element of
         Just element' ->
             return element'
         Nothing ->
             do hPutStrLn stderr "could not create all GStreamer elements\n"
                exitFailure

main =
    do args <- getArgs
       when (length args /= 1) $
            do hPutStrLn stderr "Usage: vorbis-play <Ogg/Vorbis filename>\n"
               exitFailure

       Gst.init

       mainLoop <- G.mainLoopNew Nothing True

       pipeline <- Gst.pipelineNew "audio-player"
       source <- mkElement $ Gst.elementFactoryMake "filesrc" $ Just "file-source"
       parser <- mkElement $ Gst.elementFactoryMake "oggdemux" $ Just "ogg-parser"
       decoder <- mkElement $ Gst.elementFactoryMake "vorbisdec" $ Just "vorbis-decoder"
       conv <- mkElement $ Gst.elementFactoryMake "audioconvert" $ Just "convert"
       sink <- mkElement $ Gst.elementFactoryMake "alsasink" $ Just "alsa-output"

       let elements = [source, parser, decoder, conv, sink]

       G.objectSetPropertyString "location" source (head args)

       bus <- Gst.pipelineGetBus (Gst.castToPipeline pipeline)
       Gst.busAddWatch bus G.priorityDefault $ \bus message ->
           do case Gst.messageType message of
                Gst.MessageEOS ->
                    do putStrLn "end of stream"
                       G.mainLoopQuit mainLoop
                Gst.MessageError ->
                    let G.GError _ _ msg = fst $ fromJust $ Gst.messageParseError message
                        messageStr = "Error: " ++ msg
                    in do hPutStrLn stderr messageStr
                          G.mainLoopQuit mainLoop
                _ -> return ()
              return True

       mapM_ (Gst.binAdd $ Gst.castToBin pipeline) elements

       Gst.elementLink source parser
       Gst.elementLink decoder conv
       Gst.elementLink conv sink

       G.on parser Gst.elementPadAdded $ \pad ->
           do sinkPad <- Gst.elementGetStaticPad decoder "sink"
              Gst.padLink pad $ fromJust sinkPad
              return ()

       flip G.timeoutAdd 100 $ do
         position <- Gst.elementQueryPosition pipeline Gst.FormatTime
         duration <- Gst.elementQueryDuration pipeline Gst.FormatTime
         case position of
           Just (_, position') ->
               case duration of
                 Just (_, duration') -> do
                   printf "%10d / %10d\r"
                     (fromIntegral (position' `div` Gst.second)::Integer)
                     (fromIntegral (duration' `div` Gst.second)::Integer)
                 Nothing -> do
                   putStr "no information\r"
           Nothing -> do
             putStr "no information\r"
         hFlush stdout
         return True

       Gst.elementSetState pipeline Gst.StatePlaying

       G.mainLoopRun mainLoop

       Gst.elementSetState pipeline Gst.StateNull

       return ()
