package wtf.opal.utility.render;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL33;

@Environment(EnvType.CLIENT)
public final class GLUtility {
   private static final int[] lastActiveTexture;
   private static final int[] lastProgram;
   private static final int[] lastTexture;
   private static final int[] lastSampler;
   private static final int[] lastArrayBuffer;
   private static final int[] lastVertexArrayObject;
   private static final int[] lastPolygonMode;
   private static final int[] lastViewport;
   private static final int[] lastScissorBox;
   private static final int[] lastBlendSrcRgb;
   private static final int[] lastBlendDstRgb;
   private static final int[] lastBlendSrcAlpha;
   private static final int[] lastBlendDstAlpha;
   private static final int[] lastBlendEquationRgb;
   private static final int[] lastBlendEquationAlpha;
   private static boolean lastEnableBlend;
   private static boolean lastEnableCullFace;
   private static boolean lastEnableDepthTest;
   private static boolean lastEnableStencilTest;
   private static boolean lastEnableScissorTest;
   private static boolean lastEnablePrimitiveRestart;
   private static boolean lastDepthMask;
   private static int glVersion;

   private GLUtility() {
   }

   public static void setup() {
      int[] var0 = new int[1];
      int[] var1 = new int[1];
      GL30.glGetIntegerv(33307, var0);
      GL30.glGetIntegerv(33308, var1);
      glVersion = var0[0] * 100 + var1[0] * 10;
   }

   public static void push() {
      if (glVersion == -1) {
         throw new IllegalStateException("GlStateUtility.setup(glVersion) must be called before push/pop!");
      } else {
         GL30.glGetIntegerv(34016, lastActiveTexture);
         GL30.glActiveTexture(33984);
         GL30.glGetIntegerv(35725, lastProgram);
         GL30.glGetIntegerv(32873, lastTexture);
         if (glVersion >= 330 || GL.getCapabilities().GL_ARB_sampler_objects) {
            GL30.glGetIntegerv(35097, lastSampler);
         }

         GL30.glGetIntegerv(34964, lastArrayBuffer);
         GL30.glGetIntegerv(34229, lastVertexArrayObject);
         if (glVersion >= 200) {
            GL30.glGetIntegerv(2880, lastPolygonMode);
         }

         GL30.glGetIntegerv(2978, lastViewport);
         GL30.glGetIntegerv(3088, lastScissorBox);
         GL30.glGetIntegerv(32969, lastBlendSrcRgb);
         GL30.glGetIntegerv(32968, lastBlendDstRgb);
         GL30.glGetIntegerv(32971, lastBlendSrcAlpha);
         GL30.glGetIntegerv(32970, lastBlendDstAlpha);
         GL30.glGetIntegerv(32777, lastBlendEquationRgb);
         GL30.glGetIntegerv(34877, lastBlendEquationAlpha);
         lastEnableBlend = GL30.glIsEnabled(3042);
         lastEnableCullFace = GL30.glIsEnabled(2884);
         lastEnableDepthTest = GL30.glIsEnabled(2929);
         lastEnableStencilTest = GL30.glIsEnabled(2960);
         lastEnableScissorTest = GL30.glIsEnabled(3089);
         if (glVersion >= 310) {
            lastEnablePrimitiveRestart = GL30.glIsEnabled(36765);
         }

         lastDepthMask = GL30.glGetBoolean(2930);
      }
   }

   public static void pop() {
      if (glVersion == -1) {
         throw new IllegalStateException("GlStateUtility.setup(glVersion) must be called before push/pop!");
      } else {
         GL30.glUseProgram(lastProgram[0]);
         GL30.glBindTexture(3553, lastTexture[0]);
         if (glVersion >= 330 || GL.getCapabilities().GL_ARB_sampler_objects) {
            GL33.glBindSampler(0, lastSampler[0]);
         }

         GL30.glActiveTexture(lastActiveTexture[0]);
         GL30.glBindVertexArray(lastVertexArrayObject[0]);
         GL30.glBindBuffer(34962, lastArrayBuffer[0]);
         GL30.glBlendEquationSeparate(lastBlendEquationRgb[0], lastBlendEquationAlpha[0]);
         GL30.glBlendFuncSeparate(lastBlendSrcRgb[0], lastBlendDstRgb[0], lastBlendSrcAlpha[0], lastBlendDstAlpha[0]);
         setGlState(3042, lastEnableBlend);
         setGlState(2884, lastEnableCullFace);
         setGlState(2929, lastEnableDepthTest);
         setGlState(2960, lastEnableStencilTest);
         setGlState(3089, lastEnableScissorTest);
         if (glVersion >= 310) {
            setGlState(36765, lastEnablePrimitiveRestart);
         }

         if (glVersion >= 200) {
            GL30.glPolygonMode(1032, lastPolygonMode[0]);
         }

         GL30.glViewport(lastViewport[0], lastViewport[1], lastViewport[2], lastViewport[3]);
         GL30.glScissor(lastScissorBox[0], lastScissorBox[1], lastScissorBox[2], lastScissorBox[3]);
         GL30.glDepthMask(lastDepthMask);
      }
   }

   private static void setGlState(int capability, boolean enabled) {
      if (enabled) {
         GL30.glEnable(capability);
      } else {
         GL30.glDisable(capability);
      }
   }

   static {

      lastActiveTexture = new int[1];
      lastProgram = new int[1];
      lastTexture = new int[1];
      lastSampler = new int[1];
      lastArrayBuffer = new int[1];
      lastVertexArrayObject = new int[1];
      lastPolygonMode = new int[2];
      lastViewport = new int[4];
      lastScissorBox = new int[4];
      lastBlendSrcRgb = new int[1];
      lastBlendDstRgb = new int[1];
      lastBlendSrcAlpha = new int[1];
      lastBlendDstAlpha = new int[1];
      lastBlendEquationRgb = new int[1];
      lastBlendEquationAlpha = new int[1];
      glVersion = -1;
   }
}
