package net.vulkanmod.vulkan;

import com.mojang.blaze3d.vertex.VertexFormat;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.util.Arrays;
import net.vulkanmod.vulkan.memory.MemoryManager;
import net.vulkanmod.vulkan.memory.MemoryTypes;
import net.vulkanmod.vulkan.memory.buffer.Buffer;
import net.vulkanmod.vulkan.memory.buffer.IndexBuffer;
import net.vulkanmod.vulkan.memory.buffer.UniformBuffer;
import net.vulkanmod.vulkan.memory.buffer.VertexBuffer;
import net.vulkanmod.vulkan.memory.buffer.index.AutoIndexBuffer;
import net.vulkanmod.vulkan.util.VUtil;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.vulkan.VK10;
import org.lwjgl.vulkan.VkCommandBuffer;

public class Drawer {
   private static final int INITIAL_VB_SIZE = 4000000;
   private static final int INITIAL_IB_SIZE = 1000000;
   private static final int INITIAL_UB_SIZE = 200000;
   private static final LongBuffer buffers = MemoryUtil.memAllocLong(1);
   private static final LongBuffer offsets = MemoryUtil.memAllocLong(1);
   private static final long pBuffers;
   private static final long pOffsets;
   private int framesNum;
   private VertexBuffer[] vertexBuffers;
   private IndexBuffer[] indexBuffers;
   private final AutoIndexBuffer quadsIndexBuffer;
   private final AutoIndexBuffer quadsIntIndexBuffer;
   private final AutoIndexBuffer linesIndexBuffer;
   private final AutoIndexBuffer debugLineStripIndexBuffer;
   private final AutoIndexBuffer triangleFanIndexBuffer;
   private final AutoIndexBuffer triangleStripIndexBuffer;
   private UniformBuffer[] uniformBuffers;
   private int currentFrame;

   public Drawer() {
      this.quadsIndexBuffer = new AutoIndexBuffer(65536, AutoIndexBuffer.DrawType.QUADS);
      this.quadsIntIndexBuffer = new AutoIndexBuffer(100000, AutoIndexBuffer.DrawType.QUADS);
      this.linesIndexBuffer = new AutoIndexBuffer(10000, AutoIndexBuffer.DrawType.LINES);
      this.debugLineStripIndexBuffer = new AutoIndexBuffer(10000, AutoIndexBuffer.DrawType.DEBUG_LINE_STRIP);
      this.triangleFanIndexBuffer = new AutoIndexBuffer(1000, AutoIndexBuffer.DrawType.TRIANGLE_FAN);
      this.triangleStripIndexBuffer = new AutoIndexBuffer(10000, AutoIndexBuffer.DrawType.TRIANGLE_STRIP);
   }

   public void setCurrentFrame(int currentFrame) {
      this.currentFrame = currentFrame;
   }

   public void createResources(int framesNum) {
      this.framesNum = framesNum;
      if (this.vertexBuffers != null) {
         Arrays.stream(this.vertexBuffers).iterator().forEachRemaining(Buffer::scheduleFree);
      }

      this.vertexBuffers = new VertexBuffer[framesNum];
      Arrays.setAll(this.vertexBuffers, (i) -> new VertexBuffer(4000000, MemoryTypes.HOST_MEM));
      if (this.indexBuffers != null) {
         Arrays.stream(this.indexBuffers).iterator().forEachRemaining(Buffer::scheduleFree);
      }

      this.indexBuffers = new IndexBuffer[framesNum];
      Arrays.setAll(this.indexBuffers, (i) -> new IndexBuffer(1000000, MemoryTypes.HOST_MEM));
      if (this.uniformBuffers != null) {
         Arrays.stream(this.uniformBuffers).iterator().forEachRemaining(Buffer::scheduleFree);
      }

      this.uniformBuffers = new UniformBuffer[framesNum];
      Arrays.setAll(this.uniformBuffers, (i) -> new UniformBuffer(200000, MemoryTypes.HOST_MEM));
   }

   public void resetBuffers(int currentFrame) {
      this.vertexBuffers[currentFrame].reset();
      this.indexBuffers[currentFrame].reset();
      this.uniformBuffers[currentFrame].reset();
   }

   public void draw(ByteBuffer vertexData, VertexFormat.class_5596 mode, VertexFormat vertexFormat, int vertexCount) {
      this.draw(vertexData, (ByteBuffer)null, mode, vertexFormat, vertexCount);
   }

   public void draw(ByteBuffer vertexData, ByteBuffer indexData, VertexFormat.class_5596 mode, VertexFormat vertexFormat, int vertexCount) {
      VertexBuffer vertexBuffer = this.vertexBuffers[this.currentFrame];
      int size = vertexFormat.getVertexSize() * vertexCount;
      vertexBuffer.copyBuffer(vertexData, size);
      if (indexData != null) {
         IndexBuffer indexBuffer = this.indexBuffers[this.currentFrame];
         indexBuffer.copyBuffer(indexData, indexData.remaining());
         int indexCount = vertexCount * 3 / 2;
         this.drawIndexed(vertexBuffer, indexBuffer, indexCount);
      } else {
         AutoIndexBuffer autoIndexBuffer = this.getAutoIndexBuffer(mode, vertexCount);
         if (autoIndexBuffer != null) {
            int indexCount = autoIndexBuffer.getIndexCount(vertexCount);
            autoIndexBuffer.checkCapacity(indexCount);
            this.drawIndexed(vertexBuffer, autoIndexBuffer.getIndexBuffer(), indexCount);
         } else {
            this.draw(vertexBuffer, vertexCount);
         }
      }

   }

   public void drawIndexed(Buffer vertexBuffer, IndexBuffer indexBuffer, int indexCount) {
      this.drawIndexed(vertexBuffer, indexBuffer, indexCount, indexBuffer.indexType.value);
   }

   public void drawIndexed(Buffer vertexBuffer, Buffer indexBuffer, int indexCount, int indexType) {
      VkCommandBuffer commandBuffer = Renderer.getCommandBuffer();
      VUtil.UNSAFE.putLong(pBuffers, vertexBuffer.getId());
      VUtil.UNSAFE.putLong(pOffsets, vertexBuffer.getOffset());
      VK10.nvkCmdBindVertexBuffers(commandBuffer, 0, 1, pBuffers, pOffsets);
      this.bindIndexBuffer(commandBuffer, indexBuffer, indexType);
      VK10.vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0);
   }

   public void draw(VertexBuffer vertexBuffer, int vertexCount) {
      VkCommandBuffer commandBuffer = Renderer.getCommandBuffer();
      VUtil.UNSAFE.putLong(pBuffers, vertexBuffer.getId());
      VUtil.UNSAFE.putLong(pOffsets, vertexBuffer.getOffset());
      VK10.nvkCmdBindVertexBuffers(commandBuffer, 0, 1, pBuffers, pOffsets);
      VK10.vkCmdDraw(commandBuffer, vertexCount, 1, 0, 0);
   }

   public void bindIndexBuffer(VkCommandBuffer commandBuffer, Buffer indexBuffer, int indexType) {
      VK10.vkCmdBindIndexBuffer(commandBuffer, indexBuffer.getId(), indexBuffer.getOffset(), indexType);
   }

   public void cleanUpResources() {
      for(int i = 0; i < this.framesNum; ++i) {
         Buffer buffer = this.vertexBuffers[i];
         MemoryManager.freeBuffer(buffer.getId(), buffer.getAllocation());
         Buffer var3 = this.indexBuffers[i];
         MemoryManager.freeBuffer(((Buffer)var3).getId(), ((Buffer)var3).getAllocation());
         Buffer var4 = this.uniformBuffers[i];
         MemoryManager.freeBuffer(((Buffer)var4).getId(), ((Buffer)var4).getAllocation());
      }

      this.quadsIndexBuffer.freeBuffer();
      this.quadsIntIndexBuffer.freeBuffer();
      this.linesIndexBuffer.freeBuffer();
      this.triangleFanIndexBuffer.freeBuffer();
      this.triangleStripIndexBuffer.freeBuffer();
      this.debugLineStripIndexBuffer.freeBuffer();
   }

   public AutoIndexBuffer getQuadsIndexBuffer() {
      return this.quadsIndexBuffer;
   }

   public AutoIndexBuffer getLinesIndexBuffer() {
      return this.linesIndexBuffer;
   }

   public AutoIndexBuffer getTriangleFanIndexBuffer() {
      return this.triangleFanIndexBuffer;
   }

   public AutoIndexBuffer getTriangleStripIndexBuffer() {
      return this.triangleStripIndexBuffer;
   }

   public AutoIndexBuffer getDebugLineStripIndexBuffer() {
      return this.debugLineStripIndexBuffer;
   }

   public UniformBuffer getUniformBuffer() {
      return this.uniformBuffers[this.currentFrame];
   }

   public AutoIndexBuffer getAutoIndexBuffer(VertexFormat.class_5596 mode, int vertexCount) {
      AutoIndexBuffer var10000;
      switch (mode) {
         case field_27382:
            int indexCount = vertexCount * 3 / 2;
            var10000 = indexCount > 65536 ? this.quadsIntIndexBuffer : this.quadsIndexBuffer;
            break;
         case field_27377:
            var10000 = this.linesIndexBuffer;
            break;
         case field_27381:
            var10000 = this.triangleFanIndexBuffer;
            break;
         case field_27380:
         case field_27378:
            var10000 = this.triangleStripIndexBuffer;
            break;
         case field_29345:
            var10000 = this.debugLineStripIndexBuffer;
            break;
         case field_27379:
         case field_29344:
            var10000 = null;
            break;
         default:
            throw new MatchException((String)null, (Throwable)null);
      }

      return var10000;
   }

   static {
      pBuffers = MemoryUtil.memAddress0(buffers);
      pOffsets = MemoryUtil.memAddress0(offsets);
   }
}
