module Render.Lit.Material.Collect
  ( LoadedModel
  , SceneModel(..)
  , sceneMaterials
  , modelMaterials
  , nodeMaterials
  ) where

import RIO

import RIO.Map qualified as Map
import RIO.Vector.Storable qualified as Storable

import Render.Lit.Material (shiftTextures)
import Render.Lit.Material.Model qualified as LitMaterial
import Resource.Buffer qualified as Buffer
import Resource.Mesh.Types qualified as Mesh
import Resource.Mesh.Lit qualified as Lit
import Render.Lit.Material (Material)

type LoadedModel =
  ( Mesh.Meta
  , Storable.Vector Lit.MaterialNode
  , LitMaterial.Model 'Buffer.Staged
  )

data SceneModel models textures = SceneModel
  { smLabel            :: Text
  , smGetModel         :: models -> LoadedModel
  , smGetTextureOffset :: textures -> Int32
  }

sceneMaterials
  :: Foldable t
  => models
  -> textures
  -> t (SceneModel models textures)
  -> Storable.Vector Material
sceneMaterials loadedModels combinedTextures =
  Storable.fromList . Map.elems . foldl' beep mempty
  where
    beep acc SceneModel{..} =
      let
        (_meta, materialNodes, _model) = smGetModel loadedModels
        textureOffset = smGetTextureOffset combinedTextures
      in
        modelMaterials smLabel textureOffset acc (Storable.toList materialNodes)

modelMaterials
  :: Foldable t
  => Text
  -> Int32
  -> Map Int Material
  -> t Lit.MaterialNode
  -> Map Int Material
modelMaterials label textureOffset =
  foldr (nodeMaterials label textureOffset)

nodeMaterials
  :: Text
  -> Int32
  -> Lit.MaterialNode
  -> Map Int Material
  -> Map Int Material
nodeMaterials label textureStart Lit.MaterialNode{mnMaterialIx, mnMaterial} collection =
  case Map.lookup mnMaterialIx collection of
    Nothing ->
      Map.insert mnMaterialIx newMaterial collection
    Just oldMaterial ->
      if newMaterial /= oldMaterial then
        error $ unlines
          [ "Ouf... The material indices are clashing for " <> show label
          , "At: " <> show mnMaterialIx
          , "Old: " <> show oldMaterial
          , "Now: " <> show mnMaterial
          ]
      else
        collection
  where
    newMaterial =
      shiftTextures textureStart mnMaterial
