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

module Raylib.Util (WindowResources, cameraDirectionRay, whileWindowOpen, whileWindowOpen_, whileWindowOpen0, setMaterialShader, inGHCi, Freeable (..)) where

import Control.Monad (void)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Raylib.Core (windowShouldClose)
import Raylib.ForeignUtil (Freeable (..))
import Raylib.Internal (WindowResources)
import Raylib.Types
  ( Camera3D (camera3D'position, camera3D'target),
    Material (material'shader),
    Model (model'materials),
    Ray (Ray),
    Shader,
  )
import Raylib.Util.Math (Vector (vectorNormalize, (|-|)))

-- | 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