package client.renderer;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;

public class GlState
{
    private static GlState.AlphaState alphaState = new GlState.AlphaState();
    private static GlState.BooleanState lightingState = new GlState.BooleanState(GL11.GL_LIGHTING);
    private static GlState.BooleanState[] lightState = new GlState.BooleanState[2];
    private static GlState.ColorMaterialState colorMaterialState = new GlState.ColorMaterialState();
    private static GlState.BlendState blendState = new GlState.BlendState();
    private static GlState.DepthState depthState = new GlState.DepthState();
    private static GlState.FogState fogState = new GlState.FogState();
    private static GlState.CullState cullState = new GlState.CullState();
    private static GlState.PolygonOffsetState polygonOffsetState = new GlState.PolygonOffsetState();
    private static GlState.Color clearState = new GlState.Color();
    private static GlState.BooleanState normalizeState = new GlState.BooleanState(GL11.GL_NORMALIZE);
    private static int activeTextureUnit = 0;
    private static GlState.TextureState[] textureState = new GlState.TextureState[2];
    private static int activeShadeModel = GL11.GL_SMOOTH;
    private static GlState.BooleanState rescaleNormalState = new GlState.BooleanState(GL13.GL_RESCALE_NORMAL);
    private static GlState.ColorMask colorMaskState = new GlState.ColorMask();
    private static GlState.Color colorState = new GlState.Color();

    public static void disableAlpha()
    {
        alphaState.alphaTest.setDisabled();
    }

    public static void enableAlpha()
    {
        alphaState.alphaTest.setEnabled();
    }

    public static void alphaFunc(int func, float ref)
    {
        if (func != alphaState.func || ref != alphaState.ref)
        {
            alphaState.func = func;
            alphaState.ref = ref;
            GL11.glAlphaFunc(func, ref);
        }
    }

    public static void enableLighting()
    {
        lightingState.setEnabled();
    }

    public static void disableLighting()
    {
        lightingState.setDisabled();
    }

    public static void enableLight(int light)
    {
        lightState[light].setEnabled();
    }

    public static void disableLight(int light)
    {
        lightState[light].setDisabled();
    }

    public static void enableColorMaterial()
    {
        colorMaterialState.colorMaterial.setEnabled();
    }

    public static void disableColorMaterial()
    {
        colorMaterialState.colorMaterial.setDisabled();
    }

    public static void colorMaterial(int face, int mode)
    {
        if (face != colorMaterialState.face || mode != colorMaterialState.mode)
        {
            colorMaterialState.face = face;
            colorMaterialState.mode = mode;
            GL11.glColorMaterial(face, mode);
        }
    }

    public static void disableDepth()
    {
        depthState.depthTest.setDisabled();
    }

    public static void enableDepth()
    {
        depthState.depthTest.setEnabled();
    }

    public static void depthFunc(int depthFunc)
    {
        if (depthFunc != depthState.depthFunc)
        {
            depthState.depthFunc = depthFunc;
            GL11.glDepthFunc(depthFunc);
        }
    }

    public static void depthMask(boolean flagIn)
    {
        if (flagIn != depthState.maskEnabled)
        {
            depthState.maskEnabled = flagIn;
            GL11.glDepthMask(flagIn);
        }
    }

    public static void disableBlend()
    {
        blendState.blend.setDisabled();
    }

    public static void enableBlend()
    {
        blendState.blend.setEnabled();
    }

    public static void blendFunc(int srcFactor, int dstFactor)
    {
        if (srcFactor != blendState.srcFactor || dstFactor != blendState.dstFactor)
        {
            blendState.srcFactor = srcFactor;
            blendState.dstFactor = dstFactor;
            GL11.glBlendFunc(srcFactor, dstFactor);
        }
    }

    public static void tryBlendFuncSeparate(int srcFactor, int dstFactor, int srcFactorAlpha, int dstFactorAlpha)
    {
        if (srcFactor != blendState.srcFactor || dstFactor != blendState.dstFactor || srcFactorAlpha != blendState.srcFactorAlpha || dstFactorAlpha != blendState.dstFactorAlpha)
        {
            blendState.srcFactor = srcFactor;
            blendState.dstFactor = dstFactor;
            blendState.srcFactorAlpha = srcFactorAlpha;
            blendState.dstFactorAlpha = dstFactorAlpha;
            GL14.glBlendFuncSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha);
        }
    }

    public static void enableFog()
    {
    	if(fogState.enabled)
    		fogState.fog.setEnabled();
    }

    public static void disableFog()
    {
//    	if(fogState.enabled)
    	fogState.fog.setDisabled();
    }

    public static void setFogEnabled(boolean param)
    {
        if (param != fogState.enabled)
        {
            fogState.enabled = param;
        }
        if(param)
        	enableFog();
        else
        	disableFog();
    }

    public static void setFog(int param)
    {
        if (param != fogState.mode)
        {
            fogState.mode = param;
            GL11.glFogi(GL11.GL_FOG_MODE, param);
        }
    }

    public static void setFogDensity(float param)
    {
        if (param != fogState.density)
        {
            fogState.density = param;
            GL11.glFogf(GL11.GL_FOG_DENSITY, param);
        }
    }

    public static void setFogStart(float param)
    {
        if (param != fogState.start)
        {
            fogState.start = param;
            GL11.glFogf(GL11.GL_FOG_START, param);
        }
    }

    public static void setFogEnd(float param)
    {
        if (param != fogState.end)
        {
            fogState.end = param;
            GL11.glFogf(GL11.GL_FOG_END, param);
        }
    }

    public static void enableCull()
    {
        cullState.cullFace.setEnabled();
    }

    public static void disableCull()
    {
        cullState.cullFace.setDisabled();
    }

    public static boolean isCullEnabled()
    {
        return cullState.cullFace.currentState;
    }

    public static void cullFace(int mode)
    {
        if (mode != cullState.mode)
        {
            cullState.mode = mode;
            GL11.glCullFace(mode);
        }
    }

    public static void enablePolygonOffset()
    {
        polygonOffsetState.polygonOffsetFill.setEnabled();
    }

    public static void disablePolygonOffset()
    {
        polygonOffsetState.polygonOffsetFill.setDisabled();
    }

    public static void doPolygonOffset(float factor, float units)
    {
        if (factor != polygonOffsetState.factor || units != polygonOffsetState.units)
        {
            polygonOffsetState.factor = factor;
            polygonOffsetState.units = units;
            GL11.glPolygonOffset(factor, units);
        }
    }
    
    public static void setActiveTexture(int texture)
    {
        if (activeTextureUnit != texture - GL13.GL_TEXTURE0)
        {
            activeTextureUnit = texture - GL13.GL_TEXTURE0;
            GL13.glActiveTexture(texture);
        }
    }

    public static void enableTexture2D()
    {
        textureState[activeTextureUnit].texture2DState.setEnabled();
    }

    public static void disableTexture2D()
    {
        textureState[activeTextureUnit].texture2DState.setDisabled();
    }

    public static void deleteTexture(int texture)
    {
        GL11.glDeleteTextures(texture);

        for (GlState.TextureState glstatemanager$texturestate : textureState)
        {
            if (glstatemanager$texturestate.textureName == texture)
            {
                glstatemanager$texturestate.textureName = -1;
            }
        }
    }

    public static void bindTexture(int texture)
    {
        if (texture != textureState[activeTextureUnit].textureName)
        {
            textureState[activeTextureUnit].textureName = texture;
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
        }
    }
    
    public static void enableNormalize()
    {
        normalizeState.setEnabled();
    }

    public static void disableNormalize()
    {
        normalizeState.setDisabled();
    }

    public static void shadeModel(int mode)
    {
        if (mode != activeShadeModel)
        {
            activeShadeModel = mode;
            GL11.glShadeModel(mode);
        }
    }

    public static void enableRescaleNormal()
    {
        rescaleNormalState.setEnabled();
    }

    public static void disableRescaleNormal()
    {
        rescaleNormalState.setDisabled();
    }

    public static void colorMask(boolean red, boolean green, boolean blue, boolean alpha)
    {
        if (red != colorMaskState.red || green != colorMaskState.green || blue != colorMaskState.blue || alpha != colorMaskState.alpha)
        {
            colorMaskState.red = red;
            colorMaskState.green = green;
            colorMaskState.blue = blue;
            colorMaskState.alpha = alpha;
            GL11.glColorMask(red, green, blue, alpha);
        }
    }
    
    public static void clearColor(float red, float green, float blue, float alpha)
    {
        if (red != clearState.red || green != clearState.green || blue != clearState.blue || alpha != clearState.alpha)
        {
            clearState.red = red;
            clearState.green = green;
            clearState.blue = blue;
            clearState.alpha = alpha;
            GL11.glClearColor(red, green, blue, alpha);
        }
    }

    public static void color(float colorRed, float colorGreen, float colorBlue, float colorAlpha)
    {
        if (colorRed != colorState.red || colorGreen != colorState.green || colorBlue != colorState.blue || colorAlpha != colorState.alpha)
        {
            colorState.red = colorRed;
            colorState.green = colorGreen;
            colorState.blue = colorBlue;
            colorState.alpha = colorAlpha;
            GL11.glColor4f(colorRed, colorGreen, colorBlue, colorAlpha);
        }
    }
    
    public static void color(int c) {
    	float a = (float)(c >> 24 & 255) / 255.0F;
    	float r = (float)(c >> 16 & 255) / 255.0F;
    	float g = (float)(c >> 8 & 255) / 255.0F;
    	float b = (float)(c & 255) / 255.0F;
        color(r, g, b, a);
    }

    public static void resetColor()
    {
        colorState.red = colorState.green = colorState.blue = colorState.alpha = -1.0F;
    }
    
    static
    {
        for (int i = 0; i < lightState.length; ++i)
        {
            lightState[i] = new GlState.BooleanState(GL11.GL_LIGHT0 + i);
        }

        for (int j = 0; j < textureState.length; ++j)
        {
            textureState[j] = new GlState.TextureState();
        }
    }

    static class AlphaState
    {
        public GlState.BooleanState alphaTest;
        public int func;
        public float ref;

        private AlphaState()
        {
            this.alphaTest = new GlState.BooleanState(GL11.GL_ALPHA_TEST);
            this.func = GL11.GL_ALWAYS;
            this.ref = -1.0F;
        }
    }

    static class BlendState
    {
        public GlState.BooleanState blend;
        public int srcFactor;
        public int dstFactor;
        public int srcFactorAlpha;
        public int dstFactorAlpha;

        private BlendState()
        {
            this.blend = new GlState.BooleanState(GL11.GL_BLEND);
            this.srcFactor = GL11.GL_ONE;
            this.dstFactor = GL11.GL_ZERO;
            this.srcFactorAlpha = GL11.GL_ONE;
            this.dstFactorAlpha = GL11.GL_ZERO;
        }
    }

    static class BooleanState
    {
        private final int capability;
        private boolean currentState = false;

        public BooleanState(int capabilityIn)
        {
            this.capability = capabilityIn;
        }

        public void setDisabled()
        {
            this.setState(false);
        }

        public void setEnabled()
        {
            this.setState(true);
        }

        public void setState(boolean state)
        {
            if (state != this.currentState)
            {
                this.currentState = state;

                if (state)
                {
                    GL11.glEnable(this.capability);
                }
                else
                {
                    GL11.glDisable(this.capability);
                }
            }
        }
    }
    
    static class Color
    {
        public float red = 1.0F;
        public float green = 1.0F;
        public float blue = 1.0F;
        public float alpha = 1.0F;

        public Color()
        {
        }
    }
    
    static class ColorMask
    {
        public boolean red;
        public boolean green;
        public boolean blue;
        public boolean alpha;

        private ColorMask()
        {
            this.red = true;
            this.green = true;
            this.blue = true;
            this.alpha = true;
        }
    }

    static class ColorMaterialState
    {
        public GlState.BooleanState colorMaterial;
        public int face;
        public int mode;

        private ColorMaterialState()
        {
            this.colorMaterial = new GlState.BooleanState(GL11.GL_COLOR_MATERIAL);
            this.face = GL11.GL_FRONT_AND_BACK;
            this.mode = GL11.GL_AMBIENT_AND_DIFFUSE;
        }
    }

    static class CullState
    {
        public GlState.BooleanState cullFace;
        public int mode;

        private CullState()
        {
            this.cullFace = new GlState.BooleanState(GL11.GL_CULL_FACE);
            this.mode = GL11.GL_BACK;
        }
    }

    static class DepthState
    {
        public GlState.BooleanState depthTest;
        public boolean maskEnabled;
        public int depthFunc;

        private DepthState()
        {
            this.depthTest = new GlState.BooleanState(GL11.GL_DEPTH_TEST);
            this.maskEnabled = true;
            this.depthFunc = GL11.GL_LESS;
        }
    }

    static class FogState
    {
        public GlState.BooleanState fog;
        public boolean enabled;
        public int mode;
        public float density;
        public float start;
        public float end;

        private FogState()
        {
            this.fog = new GlState.BooleanState(GL11.GL_FOG);
            this.enabled = true;
            this.mode = GL11.GL_EXP;
            this.density = 1.0F;
            this.start = 0.0F;
            this.end = 1.0F;
        }
    }

    static class PolygonOffsetState
    {
        public GlState.BooleanState polygonOffsetFill;
        public GlState.BooleanState polygonOffsetLine;
        public float factor;
        public float units;

        private PolygonOffsetState()
        {
            this.polygonOffsetFill = new GlState.BooleanState(GL11.GL_POLYGON_OFFSET_FILL);
            this.polygonOffsetLine = new GlState.BooleanState(GL11.GL_POLYGON_OFFSET_LINE);
            this.factor = 0.0F;
            this.units = 0.0F;
        }
    }
    
    static class TextureState
    {
        public GlState.BooleanState texture2DState;
        public int textureName;

        private TextureState()
        {
            this.texture2DState = new GlState.BooleanState(GL11.GL_TEXTURE_2D);
            this.textureName = 0;
        }
    }
}
