{-# LANGUAGE NamedFieldPuns, BlockArguments #-}

module Platforms.GLFW.Utils (buildWindow, withWindow, createGBufferRef) where

import qualified Graphics.UI.GLFW          as GLFW
import Control.Monad
import qualified Graphics.GL.Compatibility41 as GL
import Data.IORef (newIORef, writeIORef, readIORef, IORef)
import Hickory.Types (Size(..))
import Hickory.Graphics (GBuffer(..), createGBuffer, getTexID)
import Hickory.Utils.Utils (withArrayLen)

buildWindow :: Int -> Int -> String -> IO (Maybe GLFW.Window)
buildWindow width height title = do
    GLFW.setErrorCallback $ Just simpleErrorCallback
    r <- GLFW.init
    GLFW.windowHint (GLFW.WindowHint'OpenGLForwardCompat True)
    GLFW.windowHint (GLFW.WindowHint'OpenGLProfile GLFW.OpenGLProfile'Core)
    GLFW.windowHint (GLFW.WindowHint'ContextVersionMajor 3)
    GLFW.windowHint (GLFW.WindowHint'ContextVersionMinor 2)
    if r then do
        m <- GLFW.createWindow width height title Nothing Nothing
        case m of
          (Just win) -> do
              GLFW.makeContextCurrent m
              GLFW.setErrorCallback $ Just simpleErrorCallback
              return $ Just win
          Nothing -> do
              print ("ERROR: Couldn't create window" :: String)
              return Nothing
        else return Nothing
  where
    simpleErrorCallback e s =
        putStrLn $ unwords [show e, show s]

withWindow :: Int -> Int -> String -> (GLFW.Window -> IO ()) -> IO ()
withWindow width height title f = do
    GLFW.setErrorCallback $ Just simpleErrorCallback
    r <- GLFW.init
    GLFW.windowHint (GLFW.WindowHint'OpenGLForwardCompat True)
    GLFW.windowHint (GLFW.WindowHint'OpenGLProfile GLFW.OpenGLProfile'Core)
    GLFW.windowHint (GLFW.WindowHint'ContextVersionMajor 3)
    GLFW.windowHint (GLFW.WindowHint'ContextVersionMinor 2)
    when r $ do
        m <- GLFW.createWindow width height title Nothing Nothing
        case m of
          (Just win) -> do
              GLFW.makeContextCurrent m
              GLFW.setErrorCallback $ Just simpleErrorCallback

              GLFW.setFramebufferSizeCallback win . Just $ \_ w h -> do
                GL.glViewport 0 0 (fromIntegral w) (fromIntegral h)

              f win

              GLFW.destroyWindow win
              GLFW.terminate

          Nothing -> do
              print ("ERROR: Couldn't create window" :: String)
  where
    simpleErrorCallback e s =
        putStrLn $ unwords [show e, show s]

-- Dynamically recreates GBuffer as the framebuffer size changes
createGBufferRef :: GLFW.Window -> IO (IORef GBuffer)
createGBufferRef win = do
  (fbw, fbh) <- GLFW.getFramebufferSize win
  ref <- createGBuffer (Size fbw fbh) >>= newIORef
  GLFW.setFramebufferSizeCallback win . Just $ \_win w h -> do
    GBuffer { frameBuffer, position, normal, albedo } <- readIORef ref
    withArrayLen [frameBuffer] GL.glDeleteFramebuffers
    withArrayLen (map getTexID [position, normal, albedo]) GL.glDeleteTextures
    createGBuffer (Size w h) >>= writeIORef ref

    GL.glViewport 0 0 (fromIntegral w) (fromIntegral h) -- Just as the typical callback does
  pure ref
