module Render.Unlit.Colored.Pipeline
  ( Pipeline
  , allocate
  , allocateWireframe
  ) where

import RIO

import Control.Monad.Trans.Resource (ResourceT)
import Data.Tagged (Tagged(..))
import Vulkan.Core10 qualified as Vk
import Vulkan.Zero (zero)

import Engine.Vulkan.Pipeline qualified as Pipeline
import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)
import Render.Code (compileVert, compileFrag, glsl)
import Render.DescSets.Set0 (Scene, vertexPos, instanceTransform)
import Render.DescSets.Set0.Code (set0binding0)
import Render.Unlit.Colored.Model qualified as Model

type Config = Pipeline.Config '[Scene] Model.VertexAttrs Model.InstanceAttrs
type Pipeline = Pipeline.Pipeline '[Scene] Model.VertexAttrs Model.InstanceAttrs

allocate
  :: ( HasVulkan env
     , HasRenderPass renderpass
     )
  => Bool
  -> Vk.SampleCountFlagBits
  -> Tagged Scene DsBindings
  -> renderpass
  -> ResourceT (RIO env) Pipeline
allocate useDepth multisample tset0 rp = do
  (_, p) <- Pipeline.allocate
    Nothing
    multisample
    (config useDepth tset0)
    rp
  pure p

allocateWireframe
  :: ( HasVulkan env
     , HasRenderPass renderpass
     )
  => Bool
  -> Vk.SampleCountFlagBits
  -> Tagged Scene DsBindings
  -> renderpass
  -> ResourceT (RIO env) Pipeline
allocateWireframe useDepth multisample tset0 rp = do
  (_, p) <- Pipeline.allocate
    Nothing
    multisample
    (configWireframe useDepth tset0)
    rp
  pure p

config :: Bool -> Tagged Scene DsBindings -> Config
config useDepth (Tagged set0) = zero
  { Pipeline.cDescLayouts  = Tagged @'[Scene] [set0]
  , Pipeline.cVertexCode   = Just vertCode
  , Pipeline.cVertexInput  = vertexInput
  , Pipeline.cFragmentCode = Just fragCode
  , Pipeline.cBlend        = True
  , Pipeline.cDepthTest    = useDepth
  , Pipeline.cDepthWrite   = useDepth
  }
  where
    vertexInput = Pipeline.vertexInput
      [ vertexPos
      , (Vk.VERTEX_INPUT_RATE_VERTEX, Model.vkVertexAttrs)
      , instanceTransform
      ]

configWireframe :: Bool -> Tagged Scene DsBindings -> Config
configWireframe useDepth tset0 = (config useDepth tset0)
  { Pipeline.cTopology = Vk.PRIMITIVE_TOPOLOGY_LINE_LIST
  }

vertCode :: ByteString
vertCode =
  $(compileVert [glsl|
    #version 450
    #extension GL_ARB_separate_shader_objects : enable

    ${set0binding0}

    layout(location = 0) in vec3 vPosition;
    layout(location = 1) in vec4 vColor;

    layout(location = 2) in mat4 iModel;

    layout(location = 0) out vec4 fColor;

    void main() {
      gl_Position
        = scene.projection
        * scene.view
        * iModel
        * vec4(vPosition, 1.0);

      fColor = vColor;
      fColor.rgb * fColor.a;
    }
  |])

fragCode :: ByteString
fragCode =
  $(compileFrag [glsl|
    #version 450
    #extension GL_ARB_separate_shader_objects : enable

    layout(location = 0) in vec4 fragColor;

    layout(location = 0) out vec4 outColor;

    void main() {
      outColor = fragColor;
    }
  |])
