{-# OPTIONS -Wall #-}
{-# LANGUAGE CPP #-}

module Raylib.Util
  ( 
    -- * Bracket functions

    withWindow,
    drawing,
    mode2D,
    mode3D,
    textureMode,
    shaderMode,
    blendMode,
    scissorMode,
    vrStereoMode,

    -- * Game loop functions

    whileWindowOpen,
    whileWindowOpen_,
    whileWindowOpen0,

    -- * Miscellaneous

    cameraDirectionRay,
    setMaterialShader,
    inGHCi,
    WindowResources,
    Freeable (..),
  )
where

import Control.Monad (void)
import Control.Monad.Catch (MonadMask, bracket, bracket_)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Raylib.Core (beginBlendMode, beginDrawing, beginMode2D, beginMode3D, beginScissorMode, beginShaderMode, beginTextureMode, beginVrStereoMode, closeWindow, endBlendMode, endDrawing, endMode2D, endMode3D, endScissorMode, endShaderMode, endTextureMode, endVrStereoMode, initWindow, setTargetFPS, windowShouldClose)
import Raylib.ForeignUtil (Freeable (..))
import Raylib.Internal (WindowResources)
import Raylib.Types
  ( BlendMode,
    Camera2D,
    Camera3D (camera3D'position, camera3D'target),
    Material (material'shader),
    Model (model'materials),
    Ray (Ray),
    RenderTexture,
    Shader,
    VrStereoConfig,
  )
import Raylib.Util.Math (Vector (vectorNormalize, (|-|)))

withWindow :: (MonadIO m, MonadMask m) => Int -> Int -> String -> Int -> (WindowResources -> m b) -> m b
withWindow :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
Int -> Int -> String -> Int -> (WindowResources -> m b) -> m b
withWindow Int
w Int
h String
title Int
fps = forall (m :: * -> *) a c b.
MonadMask m =>
m a -> (a -> m c) -> (a -> m b) -> m b
bracket (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ Int -> Int -> String -> IO WindowResources
initWindow Int
w Int
h String
title forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Int -> IO ()
setTargetFPS Int
fps) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. WindowResources -> IO ()
closeWindow)

drawing :: (MonadIO m, MonadMask m) => m b -> m b
drawing :: forall (m :: * -> *) b. (MonadIO m, MonadMask m) => m b -> m b
drawing = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
beginDrawing) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endDrawing)

mode2D :: (MonadIO m, MonadMask m) => Camera2D -> m b -> m b
mode2D :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
Camera2D -> m b -> m b
mode2D Camera2D
camera = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Camera2D -> IO ()
beginMode2D Camera2D
camera)) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endMode2D)

mode3D :: (MonadIO m, MonadMask m) => Camera3D -> m b -> m b
mode3D :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
Camera3D -> m b -> m b
mode3D Camera3D
camera = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Camera3D -> IO ()
beginMode3D Camera3D
camera)) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endMode3D)

textureMode :: (MonadIO m, MonadMask m) => RenderTexture -> m b -> m b
textureMode :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
RenderTexture -> m b -> m b
textureMode RenderTexture
rt = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (RenderTexture -> IO ()
beginTextureMode RenderTexture
rt)) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endTextureMode)

shaderMode :: (MonadIO m, MonadMask m) => Shader -> m b -> m b
shaderMode :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
Shader -> m b -> m b
shaderMode Shader
shader = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Shader -> IO ()
beginShaderMode Shader
shader)) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endShaderMode)

blendMode :: (MonadIO m, MonadMask m) => BlendMode -> m b -> m b
blendMode :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
BlendMode -> m b -> m b
blendMode BlendMode
bm = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (BlendMode -> IO ()
beginBlendMode BlendMode
bm)) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endBlendMode)

scissorMode :: (MonadIO m, MonadMask m) => Int -> Int -> Int -> Int -> m b -> m b
scissorMode :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
Int -> Int -> Int -> Int -> m b -> m b
scissorMode Int
x Int
y Int
width Int
height = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Int -> Int -> Int -> Int -> IO ()
beginScissorMode Int
x Int
y Int
width Int
height)) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endScissorMode)

vrStereoMode :: (MonadIO m, MonadMask m) => VrStereoConfig -> m b -> m b
vrStereoMode :: forall (m :: * -> *) b.
(MonadIO m, MonadMask m) =>
VrStereoConfig -> m b -> m b
vrStereoMode VrStereoConfig
config = forall (m :: * -> *) a c b. MonadMask m => m a -> m c -> m b -> m b
bracket_ (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (VrStereoConfig -> IO ()
beginVrStereoMode VrStereoConfig
config)) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
endVrStereoMode)

-- | Gets the direction of a camera as a ray.

cameraDirectionRay :: Camera3D -> Ray
cameraDirectionRay :: Camera3D -> Ray
cameraDirectionRay Camera3D
camera = Vector3 -> Vector3 -> Ray
Ray (Camera3D -> Vector3
camera3D'position Camera3D
camera) (forall a. Vector a => a -> a
vectorNormalize forall a b. (a -> b) -> a -> b
$ Camera3D -> Vector3
camera3D'target Camera3D
camera forall a. Vector a => a -> a -> a
|-| Camera3D -> Vector3
camera3D'position Camera3D
camera)

-- | Calls the game loop every frame as long as the window is open.

--  For larger projects, instead of using this function, consider

--  making a custom game loop for flexibility.

whileWindowOpen ::
  (MonadIO m) =>
  -- | The game loop. Its only argument should be the current application state, and it should return a new state.

  (a -> m a) ->
  -- | The initial application state.

  a ->
  -- | The application state after the last frame.

  m a
whileWindowOpen :: forall (m :: * -> *) a. MonadIO m => (a -> m a) -> a -> m a
whileWindowOpen a -> m a
f a
state = do
  a
newState <- a -> m a
f a
state
  Bool
shouldClose <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Bool
windowShouldClose
  if Bool
shouldClose
    then forall (m :: * -> *) a. Monad m => a -> m a
return a
newState
    else forall (m :: * -> *) a. MonadIO m => (a -> m a) -> a -> m a
whileWindowOpen a -> m a
f a
newState

-- | Same as `whileWindowOpen`, but discards the final state.

whileWindowOpen_ ::
  (MonadIO m) =>
  (a -> m a) ->
  a ->
  m ()
whileWindowOpen_ :: forall (m :: * -> *) a. MonadIO m => (a -> m a) -> a -> m ()
whileWindowOpen_ a -> m a
f a
state = forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall (m :: * -> *) a. MonadIO m => (a -> m a) -> a -> m a
whileWindowOpen a -> m a
f a
state)

-- | Same as `whileWindowOpen`, but without application state.

whileWindowOpen0 ::
  (MonadIO m) =>
  m () ->
  m ()
whileWindowOpen0 :: forall (m :: * -> *). MonadIO m => m () -> m ()
whileWindowOpen0 m ()
f = forall (m :: * -> *) a. MonadIO m => (a -> m a) -> a -> m a
whileWindowOpen (forall a b. a -> b -> a
const m ()
f) ()

-- | Sets the shader of a material at a specific index (WARNING: This will fail

-- if the index provided is out of bounds).

setMaterialShader ::
  -- | The model to operate on

  Model ->
  -- | The index of the material

  Int ->
  -- | The shader to use

  Shader ->
  -- | The modified model

  Model
setMaterialShader :: Model -> Int -> Shader -> Model
setMaterialShader Model
model Int
matIdx Shader
shader = Model
model {model'materials :: [Material]
model'materials = forall {a}. [a] -> Int -> a -> [a]
setIdx [Material]
mats Int
matIdx Material
newMat}
  where
    mats :: [Material]
mats = Model -> [Material]
model'materials Model
model
    newMat :: Material
newMat = ([Material]
mats forall a. [a] -> Int -> a
!! Int
matIdx) {material'shader :: Shader
material'shader = Shader
shader}
    setIdx :: [a] -> Int -> a -> [a]
setIdx [a]
l Int
i a
v = forall a. Int -> [a] -> [a]
take Int
i [a]
l forall a. [a] -> [a] -> [a]
++ [a
v] forall a. [a] -> [a] -> [a]
++ forall a. Int -> [a] -> [a]
drop (Int
i forall a. Num a => a -> a -> a
+ Int
1) [a]
l

-- | True if the program is running in GHCi

inGHCi :: Bool

#ifdef GHCI
inGHCi = True
#else
inGHCi :: Bool
inGHCi = Bool
False
#endif