#[ #{.experimental: "codeReordering".}
{.deadCodeElim: on.}

import textUtils
, glm
, text_types
, bitops
, ../scenes/[ scene_object 
            ]

, ../vk/[ vulkan_utils
        , vkTypes
        , buffer
        , vulkan_record
        , graphics_pipeline
        ]

, ../vk/vulkan
, ../ktx/ktxutils
, ../camera
, ../drawable/texture
, ../utils/lets

import ../vk/vkm/vkmat as vkm

# TODO: caching
#[ proc genVertBuffers*( rec: var Vulkan_Record
                    , text: var SDFText
                    , str: string
                    ) =
 var
  indexOffset: uint32
  indices: array[6, uint32] =  [0.uint32,1,2, 2,3,0 ]
  w: float32 = text.texture.width.float32
  posX = 0.0
  posY = 0.0
  data: pointer
 
 for c in str:
  var ci: Bmchar = text.font.chars[int(c)]
  if ci.width == 0: ci.width = 36

  var 
   charw: float32 = ci.width.float32 / 36.0f
   dimx: float32 = 1.0f * charw
   charh: float32 = ci.height.float32 / 36.0f
   dimy: float32 = 1.0f * charh
  
   us: float32 = ci.x.float32 / w
   ue: float32 = (ci.x + ci.width).float32 / w
   ts: float32 = ci.y.float32 / w
   te: float32 = (ci.y + ci.height).float32 / w
  
   xo: float32 = ci.xoffset.float32 / 36.0f
   yo: float32 = ci.yoffset.float32 / 36.0f
  
  posy = yo

  text.vertices.add TextVert( pos: vec3f(posx + dimx + xo, posy + dimy, 0.0f) 
                       , uv: vec2(ue, te)
                       )

  text.vertices.add TextVert( pos: vec3f(posx + xo, posy + dimy, 0.0f)
                       , uv: vec2(us, te) 
                       )

  text.vertices.add TextVert( pos: vec3f(posx + xo, posy, 0.0f)
                       , uv: vec2(us, ts)
                       )
  
  text.vertices.add TextVert( pos: vec3f(posx + dimx + xo, posy, 0.0f)
                       , uv: vec2(ue, ts)
                       )
  
  for i in indices:
   text.indices.add indexOffset + i

  indexOffset += 4
  var adv: float32 = ci.xadvance.float32 / 36.0
  posx += adv
              
 text.indexCount = text.indices.len.uint32
 
 # centering?
 for v in text.vertices.mitems:
  v.pos.x -= (posx / 2.0).float32
  v.pos.y -=  0.5.float32
 
 var 
  indSize = VkDeviceSize text.indices.len * uint32.sizeof
  vertSize = VkDeviceSize text.vertices.len.uint32 * TextVert.sizeof.uint32
  copyCmd: VkCommandBuffer = rec.vk_device.getCommandBuffers(rec.command_pool, true)
  copyRegion: VkBufferCopy

 text.stVertexBuffer = a_vulkan_buffer( rec.vk_device
                                      , rec.gpu.memory_properties
                                       ,  VkBufferUsageFlags VK_BUFFER_USAGE_TRANSFER_SRC_BIT
                                        , VkMemoryPropertyFlags bitor( VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT.ord
                                                                     , VK_MEMORY_PROPERTY_HOST_COHERENT_BIT.ord
                                                                     )
                                        , vertSize
                                        
                                        ) 
   
 text.vertexBuffer = a_vulkan_buffer( rec.vk_device
 , rec.gpu.memory_properties
                                    ,  VkBufferUsageFlags bitor( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT.ord
                                                                , VK_BUFFER_USAGE_TRANSFER_DST_BIT.ord
                                                                )
                                      , VkMemoryPropertyFlags VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
                                      , vertSize
                                      
                                      ) 
    
 rec.vk_device.map_memory( text.stVertexBuffer.memory
              , data
              )
   
 copyMem(data, addr text.vertices[0], Natural vertSize)
 vkUnmapMemory( rec.vk_device, text.stVertexBuffer.memory)
 discard vkBindBufferMemory( rec.vk_device, text.stVertexBuffer.vkbuffer, text.stVertexBuffer.memory, VkDeviceSize 0)
 discard vkBindBufferMemory( rec.vk_device, text.vertexBuffer.vkbuffer, text.vertexBuffer.memory, VkDeviceSize 0)                                         
  
 text.stIndexBuffer =  a_vulkan_buffer( rec.vk_device
                                      , rec.gpu.memory_properties
                                      ,  VkBufferUsageFlags VK_BUFFER_USAGE_TRANSFER_SRC_BIT
                                            , VkMemoryPropertyFlags bitor( VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT.ord
                                                                         , VK_MEMORY_PROPERTY_HOST_COHERENT_BIT.ord
                                                                         )
                                            , indSize
                                            ) 
   
 text.indexBuffer =  a_vulkan_buffer( rec.vk_device
 , rec.gpu.memory_properties
                                    ,  VkBufferUsageFlags bitor(VK_BUFFER_USAGE_INDEX_BUFFER_BIT.ord, VK_BUFFER_USAGE_TRANSFER_DST_BIT.ord)
                                            , VkMemoryPropertyFlags VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
                                            , indSize
                                            
                                            ) 
 
 rec.vk_device.map_memory( text.stIndexBuffer.memory
              , data
              )
 
 copyMem(data, addr text.indices[0], Natural indSize)
 vkUnmapMemory( rec.vk_device, text.stIndexBuffer.memory)
 discard vkBindBufferMemory( rec.vk_device, text.stIndexBuffer.vkbuffer, text.stIndexBuffer.memory, VkDeviceSize 0)
 discard vkBindBufferMemory( rec.vk_device, text.indexBuffer.vkbuffer, text.indexBuffer.memory, VkDeviceSize 0)
  
 copyRegion.size = vertSize
 vkCmdCopyBuffer(copyCmd
                , text.stVertexBuffer.vkbuffer
                , text.vertexBuffer.vkbuffer
                , 1.uint32
                , addr copyRegion
                ) 

 copyRegion.size = indSize
 vkCmdCopyBuffer( copyCmd
                , text.stIndexBuffer.vkbuffer
                , text.indexBuffer.vkbuffer
                , 1.uint32
                , addr copyRegion
                )  
 
 #  # Flushing the command buffer will also submit it to the queue and 
 #  # uses a fence to ensure that all commands have been executed before returning
 rec.vk_device.flushCommandBuffer rec.queue,  rec.command_pool, copyCmd
 
 # Destroy staging buffers
 # Note: Staging buffer must not be deleted before the copies have been submitted and executed
 vkDestroyBuffer( rec.vk_device, text.stVertexBuffer.vkbuffer, nil)
 vkFreeMemory( rec.vk_device,    text.stVertexBuffer.memory, nil)
 vkDestroyBuffer( rec.vk_device, text.stIndexBuffer.vkbuffer, nil)
 vkFreeMemory(  rec.vk_device,    text.stIndexBuffer.memory, nil)
 ]#
proc updateUniformBuffers*(text: SDFText, rec: Vulkan_Record ) = 
 
 copyMem(text.uniBufferV.data, addr text.ubos.vs, text.ubos.vs.sizeof)

proc prepare_uniform_buffers*( rec: Vulkan_Record
                           , text: var SDFText
                           ) =

 text.uniBufferV = a_vulkan_buffer( rec.vk_device
 , rec.gpu.memory_properties
                                  ,  VkBufferUsageFlags VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
                                           , VkMemoryPropertyFlags ( VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT.ord or 
                                                                     VK_MEMORY_PROPERTY_HOST_COHERENT_BIT.ord
                                                                   )
                                           , VkDeviceSize text.ubos.vs.sizeof
                                           
                                           )
 text.uniBufferF = a_vulkan_buffer( rec.vk_device
 , rec.gpu.memory_properties
                                   ,  VkBufferUsageFlags VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
                                           , VkMemoryPropertyFlags ( VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT.ord or
                                                                     VK_MEMORY_PROPERTY_HOST_COHERENT_BIT.ord
                                                                   )
                                           , VkDeviceSize text.ubos.fs.sizeof
                                           
                                           )
 
 rec.vk_device.bindIt text.uniBufferV
 rec.vk_device.bindIt text.uniBufferF
 
 text.uniBufferV.fillDescr 
 text.uniBufferF.fillDescr 

proc setupDescrSetLayout*( rec: Vulkan_Record
                              , text: var SDFText
                              ) = 
 
 var
  #Binding 0: Vertex shader uniform buffer
  #Binding 1: Fragment shader image sampler
  #Binding 2: Fragment shader uniform buffer
  descriptorSetLayoutBindings = 
   [ aVkDescriptorSetLayoutBinding( descType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
                                  , stageFlags = VkShaderStageFlags VK_SHADER_STAGE_VERTEX_BIT
                                  , binding = 0
                                  )
  
   , aVkDescriptorSetLayoutBinding( descType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
                                  , stageFlags = VkShaderStageFlags VK_SHADER_STAGE_FRAGMENT_BIT
                                  , binding = 1
                                  )
  
   , aVkDescriptorSetLayoutBinding( descType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
                                  , stageFlags = VkShaderStageFlags VK_SHADER_STAGE_FRAGMENT_BIT
                                  , binding = 2
                                  )
  ]
  descriptorLayout: VkDescriptorSetLayoutCreateInfo
  pPipelineLayoutCreateInfo: VkPipelineLayoutCreateInfo

 descriptorLayout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO
 descriptorLayout.bindingCount = descriptorSetLayoutBindings.len.uint32
 descriptorLayout.pBindings = addr descriptorSetLayoutBindings[0]
 
 discard vkCreateDescriptorSetLayout(rec.vk_device
                                    , addr descriptorLayout
                                    , nil
                                    , addr text.descrSetLayout
                                    )

 # Create the pipeline layout that is used to
 # generate the rendering pipelines that are based on this descriptor set layout
 # In a more complex scenario you would have different pipeline layouts
 # for different descriptor set layouts that could be reused
 pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
 #pPipelineLayoutCreateInfo.pNext = nil
 pPipelineLayoutCreateInfo.setLayoutCount = 1
 pPipelineLayoutCreateInfo.pSetLayouts = addr text.descrSetLayout

 discard vkCreatePipelineLayout(rec.vk_device, addr pPipelineLayoutCreateInfo, nil, addr text.graphicsPipeline.pipelineLayout)

proc prepPipeline*( rec: Vulkan_Record
                  , rp: VkRenderPass
                  , gp: Graphics_Pipeline
                  ) =
 
 var
  # Input attribute bindings describe shader attribute locations and memory layouts
  # Location 0 : Position
  # Location 1 : Texture coordinates
  vertexInputAttributs: array[2,VkVertexInputAttributeDescription]
 
 var
  pipelineInfo = VkGraphicsPipelineCreateInfo(
     sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
     , layout: gp.pipelineLayout
     , render_pass: rp
      #subpass: 0,
      #basePipelineHandle: VkPipeline(VK_NULL_HANDLE), # optional
      #basePipelineIndex: -1, # optional
    )
  
  #Input assembly state describes how primitives are assembled
  #This pipeline will assemble vertex data as a triangle lists (though we only use one triangle)
  inputAssembly = VkPipelineInputAssemblyStateCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO
     , topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
     , primitiveRestartEnable: vkfalse
     , flags: VkPipelineInputAssemblyStateCreateFlags 0
    )
  
  rasterizer = VkPipelineRasterizationStateCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO
     , depthClampEnable: vkfalse
     , rasterizerDiscardEnable: vkfalse
     , polygonMode: VK_POLYGON_MODE_FILL
     , lineWidth: 1f
     , cullMode: VkCullModeFlags VK_CULL_MODE_NONE
     , frontFace: VK_FRONT_FACE_COUNTER_CLOCKWISE
     , depthBiasEnable: vkfalse
     , flags: VkPipelineRasterizationStateCreateFlags 0
    #  , depthBiasConstantFactor: 0f # optional
    #  , depthBiasClamp: 0f # optional
    #  , depthBiasSlopeFactor: 0f # optional
    )

  colorBlendAttachment = VkPipelineColorBlendAttachmentState(
   #colorWriteMask: VkColorComponentFlags 0xf
    blendEnable: vktrue
   , srcColorBlendFactor: VK_BLEND_FACTOR_ONE
   , dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
   , colorBlendOp: VK_BLEND_OP_ADD
   , srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE
   , dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO
   , alphaBlendOp: VK_BLEND_OP_ADD
   , colorWriteMask: VkColorComponentFlags bitor( VK_COLOR_COMPONENT_R_BIT.ord
                                                , VK_COLOR_COMPONENT_G_BIT.ord
                                                ,  VK_COLOR_COMPONENT_B_BIT.ord
                                                , VK_COLOR_COMPONENT_A_BIT.ord
                                                )
   )
  
  colorBlending = VkPipelineColorBlendStateCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO
    # , logicOpEnable: vkfalse
    # , logicOp: VK_LOGIC_OP_COPY # optional
     , attachmentCount: 1
     , pAttachments: colorBlendAttachment.addr
   #  , blendConstants: [0f, 0f, 0f, 0f] # optional
    ) 
  
  # Enable dynamic states
  # Most states are baked into the pipeline, but there are still a few dynamic states that can be changed within a command buffer
  # To be able to change these we need do specify which dynamic states VkPipelineViewportStateCreateInfoVkPipelineViewportStateCreateInfo be changed using this pipeline. Their actual states are set later on in the command buffer.
  # For this example we will set the viewport and scissor using dynamic states
  dynamicStateEnables: array[2,VkDynamicState] = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, #[VK_DYNAMIC_STATE_LINE_WIDTH]#]
  dynamicState = VkPipelineDynamicStateCreateInfo(
   sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO
   , pDynamicStates: addr dynamicStateEnables[0]
   , dynamicStateCount: dynamicStateEnables.len.uint32
   )
  
  # Depth and stencil state containing depth and stencil compare and test operations
  # We only use depth tests and want depth tests and writes to be enabled and compare with less or equal
  depthStencilState = VkPipelineDepthStencilStateCreateInfo(
    sType: VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
    , depthTestEnable: vkfalse
    , depthWriteEnable: vktrue
    , depthCompareOp: VK_COMPARE_OP_LESS_OR_EQUAL
    # , depthBoundsTestEnable: VkBool32 VK_FALSE
    , back: VkStencilOpState( #failOp: VK_STENCIL_OP_KEEP
                            #, passOp: VK_STENCIL_OP_KEEP
                            compareOp: VK_COMPARE_OP_ALWAYS
                            )
    # , front: VkStencilOpState( failOp: VK_STENCIL_OP_KEEP
    #                         , passOp: VK_STENCIL_OP_KEEP
    #                         , compareOp: VK_COMPARE_OP_ALWAYS
    #                         )
    # , stencilTestEnable: VkBool32 VK_FALSE
  )
  
  # Multi sampling state
  # This example does not make use of multi sampling (for anti-aliasing), the state must still be set and passed to the pipeline
  multisampleState = VkPipelineMultisampleStateCreateInfo(
    sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO
    , rasterizationSamples: VK_SAMPLE_COUNT_1_BIT
    , pSampleMask: nil
  )

 # Vertex input state used for pipeline creation
 var 
  viewport = VkViewport( 
    x: 0f, y: 0f
    , width: rec.swapchain.current_extent.width.float32
    ,  height: rec.swapchain.current_extent.height.float32
    ,  minDepth: 0f
    ,  maxDepth: 1f
    )
  
  scissor = VkRect2D( offset: VkOffset2D(x: 0, y: 0)
                    , extent: rec.swapchain.current_extent,
                    )
  
  viewportState = VkPipelineViewportStateCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO
     , viewportCount: 1
    , pViewports: viewport.addr
     , scissorCount: 1
     , pScissors: scissor.addr
     , flags: VkPipelineViewportStateCreateFlags 0
    )
  
  # Shaders
 vertexInputAttributs[0].binding = 0
 vertexInputAttributs[0].location = 0
 vertexInputAttributs[0].format = VK_FORMAT_R32G32B32_SFLOAT
 vertexInputAttributs[0].offset = 0
 
 vertexInputAttributs[1].binding = 0
 vertexInputAttributs[1].location = 1
 vertexInputAttributs[1].format = VK_FORMAT_R32G32_SFLOAT
 vertexInputAttributs[1].offset = float32.sizeof * 3
 
 var
  bindingDescs = [ VkVertexInputBindingDescription( binding: 0
                                                  , stride: TextVert.sizeof.uint32
                                                  , inputRate: VK_VERTEX_INPUT_RATE_VERTEX
                                                  )
                 ]

  vertexInputState = VkPipelineVertexInputStateCreateInfo(
  sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
  , vertexBindingDescriptionCount: bindingDescs.len.uint32 
  , pVertexBindingDescriptions: addr bindingDescs[0]
  , vertexAttributeDescriptionCount: vertexInputAttributs.len.uint32
  , pVertexAttributeDescriptions: addr vertexInputAttributs[0]
  ) 
  shaderStages: array[2,VkPipelineShaderStageCreateInfo]
 
 const 
  vertShaderCode = staticRead("../../data/shaders/sdfV.spv")
  fragShaderCode = staticRead("../../data/shaders/sdfF.spv")

 # Vertex shader
 shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO
 # Set pipeline stage for this shader
 shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT
 # Load binary SPIR-V shader
 shaderStages[0].module = rec.vk_device.createShaderModule vertShaderCode
 # Main entry point for the shader
 shaderStages[0].pName = "main"

 # Fragment shader
 shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO
 # Set pipeline stage for this shader
 shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT
 # Load binary SPIR-V shader
 shaderStages[1].module = rec.vk_device.createShaderModule fragShaderCode
 # Main entry point for the shader
 shaderStages[1].pName = "main"
 
 # Set pipeline shader stage info
 pipelineInfo.stageCount = shaderStages.len.uint32
 pipelineInfo.pStages = addr shaderStages[0]
  # Assign the pipeline states to the pipeline creation info structure
 pipelineInfo.pVertexInputState = addr vertexInputState
 pipelineInfo.pInputAssemblyState = addr inputAssembly
 pipelineInfo.pRasterizationState = addr rasterizer
 pipelineInfo.pColorBlendState = addr colorBlending
 pipelineInfo.pMultisampleState = addr multisampleState
 pipelineInfo.pViewportState = addr viewportState
 pipelineInfo.pDepthStencilState = addr depthStencilState
 pipelineInfo.render_pass = rp
 pipelineInfo.pDynamicState = addr dynamicState

 # Create rendering pipeline using the specified states
 discard vkCreateGraphicsPipelines(rec.vk_device
                                  , rec.pipelineCache
                                  , 1
                                  , addr pipelineInfo
                                  , nil
                                  , addr gp.pipeline
                                  )
 
 # Shader modules are no longer needed once the graphics pipeline has been created
 vkDestroyShaderModule(rec.vk_device, shaderStages[0].module, nil)
 vkDestroyShaderModule(rec.vk_device, shaderStages[1].module, nil)

proc setupDescrSets*(rec: Vulkan_Record, text: SDFText) =
 
 var
  dSet: VkDescriptorSet
  allocInfo = VkDescriptorSetAllocateInfo(
       sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO
       , descriptorPool: text.descrPool
       , descriptorSetCount: 1
       , pSetLayouts: addr text.descrSetLayout
       ) 

 discard vkAllocateDescriptorSets( rec.vk_device
                                 , addr allocInfo
                                 , addr dSet
                                 ) 

 var
  cms = aVkDescriptorImageInfo( sampler = text.texture.sampler
                              , imageView = text.texture.view
                              , imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
                              ) 
  sets = [
  #Binding 0: Vertex shader uniform buffer
  #Binding 1: Fragment shader image sampler
  #Binding 2: Fragment shader uniform buffer
   VkWriteDescriptorSet( sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET
                       , dstSet: dSet
                       , descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
                       , dstBinding: 0
                       , pBufferInfo: cast[ ptr ptr VkDescriptorBufferInfo](addr text.uniBufferV.descriptor)
                       , descriptorCount: 1
                       )
                       
  , VkWriteDescriptorSet( sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET
                        , dstSet: dSet
                        , descriptorType: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
                        , dstBinding: 1
                        , pImageInfo: addr cms
                        , descriptorCount: 1
                        )
  , VkWriteDescriptorSet( sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET 
                        , dstSet: dSet
                        , descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
                        , dstBinding: 2
                        , pBufferInfo: cast[ ptr ptr VkDescriptorBufferInfo](addr text.uniBufferF.descriptor)
                        , descriptorCount: 1
                        )                  
  ]

 vkUpdateDescriptorSets(rec.vk_device, sets.len.uint32, addr sets[0], 0, nil)
 text.descrSet = dSet

proc buildCommandBuffers*( rec: Vulkan_Record
                         , text: SDFText
                         , rp: VkRenderpass
                         , i: int
                         ) =
 
  var offsets = VkDeviceSize 0
  vkCmdBindPipeline( rec.draw_command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, text.graphicsPipeline.pipeline)
  vkCmdBindDescriptorSets( rec.draw_command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, text.graphicsPipeline.pipelineLayout, 0, 1, addr text.descrSet, 0, nil)
  vkCmdBindVertexBuffers( rec.draw_command_buffers[i], 0, 1, addr text.vertexBuffer.vkbuffer, addr offsets)
  vkCmdBindIndexBuffer( rec.draw_command_buffers[i], text.indexBuffer.vkbuffer, VkDeviceSize 0, VK_INDEX_TYPE_UINT32)
  vkCmdDrawIndexed( rec.draw_command_buffers[i], text.indexCount , 1, 0, 0, 0) # , 1 ?
  
  text.camera.set_aspect_ratio_with( 60
                                   , (float rec.swapchain.current_extent.width) / 
                                     (float rec.swapchain.current_extent.height)
                                   )
  
  rec.readyFrame = true

proc setupDescrPool(rec: var Vulkan_Record
                        , text: var SDFText
                        ) = 
 var
  sizes = [ VkDescriptorPoolSize( typee: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
                                , descriptorCount: 4
                                )
          
          , VkDescriptorPoolSize( typee: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
                                , descriptorCount: 2
                                )
          ]
  
  descpoolinfo = VkDescriptorPoolCreateInfo( sType: VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO
                                           , poolSizeCount: sizes.len.uint32
                                           , pPoolSizes: addr sizes[0]
                                           , maxSets: 2
                                           )
 discard vkCreateDescriptorPool(rec.vk_device, addr descpoolinfo, nil, addr text.descrPool)

proc anSDFUBO*( outLineColor: Vec4[float32] = vec4f(1.float32, 0, 0, 0.0)
              , outLineWidth: float32 = 98.543
              , isOutlined: bool = true
              ): UBOS = 
 
 result = UBOS( vs: text_types.UBOVS()
              , fs: text_types.UBOFS() 
              )

 result.fs.outlineColor = outlineColor
 result.fs.outlineWidth = outlineWidth
 result.fs.outlined = 0.1


#[ proc updateCamera*( text: var SDFText 
                  ) =

 #  if text.camera_type == Ortho: 
 
  #text.ubos.vs.view = text.camera.matrices.view
  #text.camera.updateViewMatrix()
   
 #  if text.camera_type == Perspective:
 #  text.ubos.vs.proj = text.camera.matrices.persp
 #  text.ubos.vs.view = text.camera.matrices.view
 #  text.ubos.vs.model = mat4(1.0.float32).scale 0.16
 #  text.camera.updateViewMatrix()

 text.device[].map_memory( text.uniBufferV.memory
               , text.uniBufferV.data
               )

 text.device[].map_memory( text.uniBufferF.memory
               , text.uniBufferF.data
               )
 
 copymem(text.uniBufferV.data, addr text.ubos.vs, text.ubos.vs.sizeof)
 copymem(text.uniBufferF.data, addr text.ubos.fs, text.ubos.fs.sizeof)
 vkUnmapMemory(text.device[], text.uniBufferV.memory)
 vkUnmapMemory(text.device[], text.uniBufferF.memory)
 text.changed = false ]#

proc rotate*( t: var SDFText
            , a, x,y,z: float32
            ) = 
 
 case t.camera_type 
 of Ortho: 
  t.ubos.vs.model = t.ubos.vs.model.rotate(radians a,x,y,z)
  #t.updateCamera 
 of Perspective: discard

proc move*( t: var SDFText
          , x,y: float32
          ) = 
 
 case t.camera_type 
 of Ortho: 
  t.ubos.vs.model = t.ubos.vs.model.translate(x,y,0)
  #t.updateCamera 
 of Perspective: discard

 # TODO: outlined needs to be a bool in the fragment shader
 #       but for some reason is currently a float32, that checks for >0.0 to outline

proc updateFS*( t: var SDFText
              , outLineColor: Vec4[float32] = vec4f(1.float32, 0, 0, 0.1)
              , outLineWidth: float32 = 90
              , outlined: float32 = 0.0
              ) = 

 t.ubos.fs.outLineColor = outlineColor
 t.ubos.fs.outLineWidth = outLineWidth
 t.ubos.fs.outlined = outlined
 #t.updateCamera

proc strIs*( t: var SDFText
            , s: string
            #, rec: var Vulkan_Record
            ) = 
 t.str = s
 t.strChanged = true
 t.theVkRec[].genVertBuffers( t
                   , s
                   )
 t.strChanged = false


# Vulkan is y neg
# TODO: get rid of the hack.
# currently the SDF text gets chopped off at the tops which messed up capital letters
# after modifying the model(?)
proc move_to*( t: var SDFText
            , x,y: float32
            ) = 
 
 case t.camera_type 
 of Ortho: 
  t.ubos.vs.model = mat4f(1)
                    .translate(0,0,0)
                    .translate(x,-y,0)
                    # SDF text currently upside down
                    # TODO: Actually fix please, thank you. 
                    .scale(t.size * 4.0)
                    .rotate(radians -540.float32, 1,0,0)

  #var ts = t.str
  
  #t.updateCamera
 of Perspective: discard

proc anSDFText*( rec: var Vulkan_Record
             , scene: var Scene
             , name: string
             , str: string
             , size: float32
             , camTy: Camera_Kind = Ortho
             ): SDFText = 
 
 result = SDFText( font: aPBMFont()
                 , ubos: anSDFUBO()
                 , texture: Texture2D()
                 , indexBuffer: Buffer()
                 , vertexBuffer: Buffer() 
                 , graphicsPipeline: GraphicsPipeline()
                 , camera: aCamera( float32 ( rec.current_viewport.width /
                                          rec.current_viewport.height
                                         )
                                , camTy
                               )
                 , name: name
                 , device: addr rec.vk_device
                 , id: scene.current_entity_id
                 , size: size
                 , theVkRec: addr rec
                 , str: str
                 )  

 rec.loadFromFile(result.texture)
 rec.genVertBuffers( result
                   , str 
                   )

 rec.prepare_uniform_buffers result
 rec.setupDescrSetLayout result
 rec.prepPipeline scene.render_pass, result.graphicsPipeline
 rec.setupDescrPool result
 rec.setupDescrSets result
 
 result.ubos.vs.proj = vkm.ortho( 0.float32
                                , rec.swapchain.current_extent.width.float32
                                , 0.float32
                                , rec.swapchain.current_extent.height.float32
                                , 0.0.float32
                                , -1000.0.float32
                                )
 result.ubos.vs.model = mat4f(1)
                        .scale(size * 4)
                        # SDF text currently upside down
                        # TODO: Actually fix please, thank you. 
                        .rotate(radians -540.float32, 1,0,0)
 
 result.ubos.vs.view = mat4f(1)
 #result.updateCamera  ]#