{- |
  All the provided render passes and pipelines packaged and delivered.
-}

module Render.Basic where

import RIO

import Control.Monad.Trans.Resource (ResourceT)
import Data.Tagged (Tagged(..))
import RIO.Vector.Partial as Vector (headM)
import Vulkan.Core10 qualified as Vk

-- keid-core

import Engine.Types qualified as Engine
import Engine.Types (StageRIO)
import Engine.Vulkan.Pipeline qualified as Pipeline
import Engine.Vulkan.Types (DsBindings, HasSwapchain(getMultisample), RenderPass(..))

-- keid-render-basic

import Render.Debug.Pipeline qualified as Debug
import Render.DescSets.Set0 (Scene)
import Render.DescSets.Set0 qualified as Scene
import Render.DescSets.Sun (Sun)
import Render.DescSets.Sun qualified as Sun
import Render.Font.EvanwSdf.Pipeline qualified as EvanwSdf
import Render.ForwardMsaa (ForwardMsaa)
import Render.ForwardMsaa qualified as ForwardMsaa
import Render.Lit.Colored.Pipeline qualified as LitColored
import Render.Lit.Material.Pipeline qualified as LitMaterial
import Render.Lit.Textured.Pipeline qualified as LitTextured
import Render.Samplers qualified as Samplers
import Render.ShadowMap.Pipeline qualified as ShadowPipe
import Render.ShadowMap.RenderPass (ShadowMap)
import Render.ShadowMap.RenderPass qualified as ShadowPass
import Render.Skybox.Pipeline qualified as Skybox
import Render.Unlit.Colored.Pipeline qualified as UnlitColored
import Render.Unlit.Textured.Pipeline qualified as UnlitTextured

type Stage = Engine.Stage RenderPasses Pipelines
type Frame = Engine.Frame RenderPasses Pipelines
type StageFrameRIO r s a = Engine.StageFrameRIO RenderPasses Pipelines r s a

data RenderPasses = RenderPasses
  { rpForwardMsaa :: ForwardMsaa
  , rpShadowPass  :: ShadowMap
  }

instance RenderPass RenderPasses where
  allocateRenderpass_ context = do
    rpForwardMsaa <- ForwardMsaa.allocateMsaa context

    -- TODO: get from config
    let
      size      = 4096 -- XXX: 2048 is kinda minimum, even with PCF enabled
      numLayers = 2    -- XXX: no-cascade sun + moon
    rpShadowPass <- ShadowPass.allocate context size numLayers

    pure RenderPasses{..}

  updateRenderpass context RenderPasses{..} = RenderPasses
    <$> ForwardMsaa.updateMsaa context rpForwardMsaa
    <*> pure rpShadowPass -- XXX: not a screen pass

  refcountRenderpass RenderPasses{..} = do
    refcountRenderpass rpForwardMsaa
    refcountRenderpass rpShadowPass

data Pipelines = Pipelines
  { pEvanwSdf :: EvanwSdf.Pipeline
  , pSkybox   :: Skybox.Pipeline
  , pDebug    :: Debug.Pipeline

  , pLitColored       :: LitColored.Pipeline
  , pLitColoredBlend  :: LitColored.Pipeline
  , pLitMaterial      :: LitMaterial.Pipeline
  , pLitMaterialBlend :: LitMaterial.Pipeline
  , pLitTextured      :: LitTextured.Pipeline
  , pLitTexturedBlend :: LitTextured.Pipeline

  , pUnlitColored        :: UnlitColored.Pipeline
  , pUnlitColoredNoDepth :: UnlitColored.Pipeline
  , pUnlitTextured       :: UnlitTextured.Pipeline
  , pUnlitTexturedBlend  :: UnlitTextured.Pipeline
  , pWireframe           :: UnlitColored.Pipeline
  , pWireframeNoDepth    :: UnlitColored.Pipeline

  , pShadowCast :: ShadowPipe.Pipeline
  }

allocatePipelines_
  :: HasSwapchain swapchain
  => swapchain
  -> RenderPasses
  -> ResourceT (StageRIO st) Pipelines
allocatePipelines_ swapchain renderpasses = do
  (_, samplers) <- Samplers.allocate swapchain
  allocatePipelines
    (Scene.set0 samplers (Left 3) (Left 0) 2)
    swapchain
    renderpasses

allocatePipelines
  :: HasSwapchain swapchain
  => Tagged Scene DsBindings
  -> swapchain
  -> RenderPasses
  -> ResourceT (StageRIO st) Pipelines
allocatePipelines sceneBinds swapchain RenderPasses{..} = do
  let msaa = getMultisample swapchain

  pDebug    <- Debug.allocate msaa sceneBinds rpForwardMsaa
  pEvanwSdf <- EvanwSdf.allocate msaa sceneBinds rpForwardMsaa
  pSkybox   <- Skybox.allocate msaa sceneBinds rpForwardMsaa

  pLitColored       <- LitColored.allocate msaa sceneBinds rpForwardMsaa
  pLitColoredBlend  <- LitColored.allocateBlend msaa sceneBinds rpForwardMsaa
  pLitMaterial      <- LitMaterial.allocate msaa sceneBinds rpForwardMsaa
  pLitMaterialBlend <- LitMaterial.allocateBlend msaa sceneBinds rpForwardMsaa
  pLitTextured      <- LitTextured.allocate msaa sceneBinds rpForwardMsaa
  pLitTexturedBlend <- LitTextured.allocateBlend msaa sceneBinds rpForwardMsaa

  pUnlitColored        <- UnlitColored.allocate True msaa sceneBinds rpForwardMsaa
  pUnlitColoredNoDepth <- UnlitColored.allocate False msaa sceneBinds rpForwardMsaa
  pUnlitTextured       <- UnlitTextured.allocate msaa sceneBinds rpForwardMsaa
  pUnlitTexturedBlend  <- UnlitTextured.allocateBlend msaa sceneBinds rpForwardMsaa
  pWireframe           <- UnlitColored.allocateWireframe True msaa sceneBinds rpForwardMsaa
  pWireframeNoDepth    <- UnlitColored.allocateWireframe False msaa sceneBinds rpForwardMsaa

  let sunBinds = Sun.set0
  pShadowCast <- ShadowPipe.allocate sunBinds rpShadowPass ShadowPipe.defaults

  pure Pipelines{..}

getSceneLayout :: Pipelines -> Tagged '[Scene] Vk.DescriptorSetLayout
getSceneLayout Pipelines{pLitColored} =
  case Vector.headM (unTagged $ Pipeline.pDescLayouts pLitColored) of
    Nothing ->
      error "pLitColored has at least set0 in layout"
    Just set0layout ->
      Tagged set0layout

getSunLayout :: Pipelines -> Tagged '[Sun] Vk.DescriptorSetLayout
getSunLayout Pipelines{pShadowCast} =
  case Vector.headM (unTagged $ Pipeline.pDescLayouts pShadowCast) of
    Nothing ->
      error "pShadowCast has at least set0 in layout"
    Just set0layout ->
      Tagged set0layout
