{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-}
module Draw where

import qualified Control.Concurrent.STM as STM
import Control.Monad
import Control.Monad.Trans.Resource
import qualified Data.Vector as V
import qualified Vulkan.Core10 as Vk
import qualified Vulkan.Extensions.VK_KHR_swapchain as Khr
import qualified Vulkan.CStruct.Extends as Vk
import qualified Vulkan.Zero as Vk

-- internal imports

import CommandBuffer
import Types
import Control.Monad.IO.Class (MonadIO(liftIO))

drawFrame
  :: (MonadResource m, MonadFail m)
  => EngineData
  -> STM.TMVar Pipelines
  -> Int
  -> m ()
drawFrame engineData switch frameNumber = do

  unless (all (V.length (engineCommandBuffers engineData) ==)
    [ V.length (engineCommandBuffers engineData)
    , V.length (engineFramebuffers engineData)
    ]) $
      error ("Numbers of elements mismatch: command buffers " <>
        show (V.length $ engineCommandBuffers engineData) <>
        ", frame buffers " <> show (V.length $ engineFramebuffers engineData)
        )

  let fences = V.singleton $ engineInFlightFence engineData

  void $ Vk.waitForFences (engineLogicalDevice engineData) fences True maxBound
  Vk.resetFences (engineLogicalDevice engineData) fences

  (imageResult, index) <-
    Khr.acquireNextImageKHR
      (engineLogicalDevice engineData)
      (engineSwapchain engineData)
      maxBound
      (engineImageAvailableSemaphore engineData)
      Vk.NULL_HANDLE

  unless (imageResult == Vk.SUCCESS) $
    error "drawFrame: Failed acquiring next image from swapchain"

  -- liftIO $ putStrLn "resetting command buffer"
  Vk.resetCommandBuffer
    (engineCommandBuffers engineData V.! fromIntegral index) (Vk.CommandBufferResetFlagBits 0)

  switchState <- liftIO $ STM.atomically $ STM.readTMVar switch

  -- liftIO $ putStrLn "recording command buffer"
  recordCommandBuffer
    (engineCommandBuffers engineData V.! fromIntegral index)
    (engineRenderPass engineData)
    (engineFramebuffers engineData V.! fromIntegral index)
    (engineWindowDimensions engineData)
    (case switchState of
      Rainbow ->
        redEnginePipelines engineData V.! fromIntegral index
      Red ->
        rainbowEnginePipelines engineData V.! fromIntegral index
      Green ->
        meshPipeline engineData V.! fromIntegral index
      )
    (meshPipelineLayout engineData)
    (case switchState of
      Green ->
        Just $ engineMesh engineData
      _ ->
        Nothing
      )
    frameNumber

  let submitInfo = Vk.zero
        { Vk.waitSemaphores =
          V.singleton $ engineImageAvailableSemaphore engineData
        , Vk.waitDstStageMask =
          V.singleton Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
        , Vk.commandBuffers =
          V.singleton (Vk.commandBufferHandle $ engineCommandBuffers engineData V.! fromIntegral index)
        , Vk.signalSemaphores =
          V.singleton $ engineRenderFinishedSemaphore engineData
        }

  -- liftIO $ putStrLn "submitting to queue"
  Vk.queueSubmit
    (engineQueue engineData)
    (V.singleton (Vk.SomeStruct submitInfo))
    (engineInFlightFence engineData)

  let presentInfo = Vk.zero
        { Khr.waitSemaphores = V.singleton $ engineRenderFinishedSemaphore engineData
        , Khr.swapchains = V.singleton $ engineSwapchain engineData
        , Khr.imageIndices = V.singleton index
        }

  -- liftIO $ putStrLn "presenting queue"
  presentResult <- Khr.queuePresentKHR (engineQueue engineData) presentInfo

  unless (presentResult == Vk.SUCCESS) $
    error "drawFrame: presentation failed"
