﻿module Util where

import Control.Monad
import qualified Graphics.UI.GLFW as GLFW
import System.Exit
import System.IO
import Control.Concurrent (threadDelay)

-- Resize window
import qualified Graphics.Rendering.OpenGL as GL
import Graphics.Rendering.OpenGL (($=))

import Debug.Trace


-- type ErrorCallback = Error -> String -> IO ()
errorCallback :: GLFW.ErrorCallback
errorCallback _ = hPutStrLn stderr

keyCallback :: GLFW.KeyCallback
keyCallback window key _ action _ = when (key == GLFW.Key'Escape && action == GLFW.KeyState'Pressed) $
    GLFW.setWindowShouldClose window True

-- | Resize the viewport and set the projection matrix
resize _ w h = do
    -- These are all analogous to the standard OpenGL functions
    GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral w) (fromIntegral h))
    GL.matrixMode $= GL.Projection
    GL.loadIdentity
    GL.perspective 45 (fromIntegral w / fromIntegral h) 1 100
    GL.matrixMode $= GL.Modelview 0

initialize :: String -> IO GLFW.Window
initialize title = do
    GLFW.setErrorCallback (Just errorCallback)
    successfulInit <- GLFW.init
    -- if init failed, we exit the program
    if not successfulInit then exitFailure else do

        GLFW.windowHint $ GLFW.WindowHint'OpenGLDebugContext True
        GLFW.windowHint $ GLFW.WindowHint'RedBits 8
        GLFW.windowHint $ GLFW.WindowHint'GreenBits 8
        GLFW.windowHint $ GLFW.WindowHint'BlueBits 8
        GLFW.windowHint $ GLFW.WindowHint'DepthBits 1

        mw <- GLFW.createWindow 640 480 title Nothing Nothing
        case mw of
            Nothing -> GLFW.terminate >> exitFailure
            Just window -> do
                GLFW.makeContextCurrent mw
                GLFW.setKeyCallback window (Just keyCallback)
                GLFW.setWindowSizeCallback window (Just resize)
                return window

cleanup :: GLFW.Window -> IO ()
cleanup win = do
    GLFW.destroyWindow win
    GLFW.terminate
    exitSuccess

mainLoop :: (Double -> IO ()) -> GLFW.Window -> Double -> IO ()
mainLoop draw w prev = do
    close <- GLFW.windowShouldClose w
    unless close $ do
                    Just now <- GLFW.getTime

                    draw now
                    GLFW.swapBuffers w

                    -- Sleep for the rest of the frame
                    frameLeft <- fmap (spf + prev -) $ return now
                    when (frameLeft > 0) $
                        trace (show frameLeft) (threadDelay (truncate $ 1000000 * frameLeft))

                    GLFW.pollEvents
                    mainLoop draw w now

    where
        -- maximum frame rate
        fps = 60
        spf = recip fps
