//--Fragment half of the lighting shader. Lighting is computed by adding distances to the light sources
//  which are uploaded to the shader.
#version 150 compatibility
uniform sampler2D xTexSampler;

//--Viewport variables.
uniform float uViewportOffsetX;
uniform float uViewportOffsetY;
uniform float uViewportSizeX;
uniform float uViewportSizeY;
uniform float uVirtualCanvasX;
uniform float uVirtualCanvasY;

//--Camera position. The camera offsets all light positions.
uniform float uCameraX;
uniform float uCameraY;

//--Ambient light.
uniform float uAmbientR;
uniform float uAmbientG;
uniform float uAmbientB;

//--Light variables.
uniform int uTotalLights;
uniform int uSkipLight[128];
uniform int uLightType[128];
uniform vec4 uLightPosition[128];
uniform vec4 uLightColor[128];
uniform float uLightIntensity[128];

//--Position Overrides
uniform int uUseOverrideX;
uniform int uUseOverrideY;
uniform float uOverrideX;
uniform float uOverrideY;

//--Fullbright. Player party always renders fullbright if the player has the lantern.
//  Colored lighting will still affect them.
uniform int uFullbrightEntity;

//--Outlines. Enemies need to render these but two shaders can't run at the same time. The functionality of
//  ColorOutline.frag is thus rolled into the lighting shader.
uniform int uShowColorOutline;
uniform vec4 uOutlineColor;

//--Flashwhite flag. Used when a party member is transforming.
uniform int uIsFlashwhite;

//--Viewcone flag. Viewcones ignore texels and use the color mixer.
uniform int uIsViewcone;

//--Mixer override. Used if the color needs to be modified by the mixer. Presently just replaces the color.
uniform int uUseMixer;
uniform vec4 uMixer;

//--Light Types
//  0: Radial. Emits light in all directions, uses the X/Y as center.
//  1: Line. Emits light in all directions across a line. Uses X1/Y2 to X2/Y2.

//--Function for computing the distance between a line and a point.
/*
float DistToLine(vec2 pPointA, vec2 pPointB, vec2 pPointTest)
{
    //--Get distances.
    float tXDif = pPointB.x - pPointA.x;
    float tYDif = pPointB.y - pPointA.y;

    //--If the points are the same, use a simple distance calc.
    if ((tXDif == 0.0f) && (tYDif == 0.0f))
    {
        tXDif = pPointTest.x - pPointA.x;
        tYDif = pPointTest.y - pPointA.y;
        return sqrt(tXDif * tXDif + tYDif * tYDif);
    }

    float t = ((pPointTest.x - pPointA.x) * tXDif + (pPointTest.y - pPointA.y) * tYDif) / (tXDif * tXDif + tYDif * tYDif);

    if (t < 0)
    {
        //point is nearest to the first point i.e x1 and y1
        tXDif = pPointTest.x - pPointA.x;
        tYDif = pPointTest.y - pPointA.y;
    }
    else if (t > 1)
    {
        //point is nearest to the end point i.e x2 and y2
        tXDif = pPointTest.x - pPointB.x;
        tYDif = pPointTest.y - pPointB.y;
    }
    else
    {
        //if perpendicular line intersect the line segment.
        tXDif = pPointTest.x - (pPointA.x + t * tXDif);
        tYDif = pPointTest.y - (pPointA.y + t * tYDif);
    }

    //returning shortest distance
    return sqrt(tXDif * tXDif + tYDif * tYDif);
}*/


//--Function.
void main (void)
{
    //--Retrieve the texel.
    vec4 tColor = texture2D(xTexSampler, gl_TexCoord[0].st);
    if(uIsFlashwhite > 0)
    {
        gl_FragColor = vec4(1.0f, 1.0f, 1.0f, uIsFlashwhite / 100.0f);
        return;
    }

    //--If using the mixer, override it here.
    if(uUseMixer == 1)
    {
        tColor = uMixer;
    }

    //--Outline. Used by enemies that are tougher than tier-0 enemies. The outline is still affected by light color.
    if(uShowColorOutline == 1 && tColor.a == 0.0f)
    {
        //--If the color is transparent, check each of the four texels surrounding this texel. If any of them
        //  are not transparent, we're on an edge, so render an outline.
        bool tRenderOutlineHere = false;
        ivec2 cTexSize = textureSize(xTexSampler, 0);
        float cTexelX = 1.0 / float(cTexSize.x);
        float cTexelY = 1.0 / float(cTexSize.y);
        vec2 tQuery = gl_TexCoord[0].st;

        //--Edge checker. If we happen to be exactly on the edge of the sprite, don't render. Sprites
        //  should be padded by 2 to make this work correctly.
        //--This code would break lighting if used on anything but an enemy. Make sure the color outline
        //  flag is off when rendering environmental parts.
        if(tQuery.x == 0.0 || tQuery.x == 1.0 - cTexelX || tQuery.y == 0.0 || tQuery.y == 1.0 - cTexelY)
        {
            tRenderOutlineHere = false;
        }
        else
        {
            //--Left check.
            if(tRenderOutlineHere == false)
            {
                tQuery.x = tQuery.x - cTexelX;
                tColor = texture2D(xTexSampler, tQuery);
                if(tColor.a != 0.0)
                {
                    tRenderOutlineHere = true;
                }
                tQuery.x = tQuery.x + cTexelX;
            }
            //--Up check.
            if(tRenderOutlineHere == false)
            {
                tQuery.y = tQuery.y - cTexelY;
                tColor = texture2D(xTexSampler, tQuery);
                if(tColor.a != 0.0)
                {
                    tRenderOutlineHere = true;
                }
                tQuery.y = tQuery.y + cTexelY;
            }
            //--Right check.
            if(tRenderOutlineHere == false)
            {
                tQuery.x = tQuery.x + cTexelX;
                tColor = texture2D(xTexSampler, tQuery);
                if(tColor.a != 0.0)
                {
                    tRenderOutlineHere = true;
                }
                tQuery.x = tQuery.x - cTexelX;
            }
            //--Down check.
            if(tRenderOutlineHere == false)
            {
                tQuery.y = tQuery.y + cTexelY;
                tColor = texture2D(xTexSampler, tQuery);
                if(tColor.a != 0.0)
                {
                    tRenderOutlineHere = true;
                }
                tQuery.y = tQuery.y - cTexelY;
            }
        }

        //--Now override the color. Normal case...
        if(!tRenderOutlineHere)
        {
        }
        //--Grey case.
        else
        {
            tColor = uOutlineColor;
        }
    }
    //--This is a viewcone.
    else if(uIsViewcone == 1)
    {
        tColor = gl_Color;
    }

    //--Distance penalty. Only used in some circumstances.
    float tDistancePenalty = 0.0f;

    //--Position. Invert the Y position if using screen coordinates.
    vec4 tUsePosition = gl_FragCoord;
    tUsePosition.x = (tUsePosition.x - uViewportOffsetX) * (uVirtualCanvasX / uViewportSizeX);
    tUsePosition.y = (tUsePosition.y + uViewportOffsetY) * (uVirtualCanvasY / uViewportSizeY);
    tUsePosition.y = uVirtualCanvasY - tUsePosition.y;

    //--If these values are 1, use them instead of the original.
    if(uUseOverrideX == 1) tUsePosition.x = uOverrideX;
    if(uUseOverrideY == 1) tUsePosition.y = uOverrideY;

    //--If these values are 2, use them in addition to the original.
    if(uUseOverrideX == 2) tUsePosition.x = tUsePosition.x + uOverrideX;
    if(uUseOverrideY == 2) tUsePosition.y = tUsePosition.y + uOverrideY;

    //--If the value is 3, use this instead of the original, and then subtract by the original
    //  for depth. Used for high walls.
    if(uUseOverrideY == 3)
    {
        //--Override.
        tUsePosition.y = uOverrideY + 0.0f;

        //--Original position.
        float tOriginalPosition = (gl_FragCoord.y + uViewportOffsetY) * (uVirtualCanvasY / uViewportSizeY);
        tOriginalPosition = uVirtualCanvasY - tOriginalPosition;

        //--Compute penalty.
        tDistancePenalty = (tUsePosition.y - tOriginalPosition) * 0.30f;
    }
    //--If the value is 4, this is a very-high wall.
    else if(uUseOverrideY == 4)
    {
        //--Override.
        tUsePosition.y = uOverrideY + 48.0f;

        //--Original position.
        float tOriginalPosition = (gl_FragCoord.y + uViewportOffsetY) * (uVirtualCanvasY / uViewportSizeY);
        tOriginalPosition = uVirtualCanvasY - tOriginalPosition;

        //--Compute penalty.
        tDistancePenalty = (tUsePosition.y - tOriginalPosition) * 0.30f;
    }

    //--Current light intensity.
    vec4 tIntensity;
    tIntensity.r = uAmbientR;
    tIntensity.g = uAmbientG;
    tIntensity.b = uAmbientB;

    //--Intensity by Distance factor.
    float cDistanceFactor = 0.30f;
    int cMaxLights = 28;
    int cUseLights = 0;

    //--Iterate across the lights.
    for(int i = 0; i < uTotalLights; i ++)
    {
        //--Skip lights that are out of range or disabled.
        if(uSkipLight[i] == 1) continue;

        //--Radial Light:
        if(uLightType[i] == 0)
        {
            //--Distance.
            float cXDif = tUsePosition.x - (uLightPosition[i].x - uCameraX);
            float cYDif = tUsePosition.y - (uLightPosition[i].y - uCameraY);
            float cDistance = sqrt((cXDif * cXDif) + (cYDif * cYDif)) + tDistancePenalty;

            //--Attenuation.
            float att = 1.0 / (1.0 + (0.01*cDistance*cDistance) + (0.015*cDistance*cDistance));

            //--Apply to the final color.
            tIntensity.r = tIntensity.r + (uLightIntensity[i] * uLightColor[i].r / cDistance * att * cDistanceFactor);
            tIntensity.g = tIntensity.g + (uLightIntensity[i] * uLightColor[i].g / cDistance * att * cDistanceFactor);
            tIntensity.b = tIntensity.b + (uLightIntensity[i] * uLightColor[i].b / cDistance * att * cDistanceFactor);
        }
        //--Square Radial Light:
        else if(uLightType[i] == 1)
        {
            //--Setup.
            float cDistance;

            //--[North Cases]
            if(tUsePosition.y < (uLightPosition[i].y - uCameraY))
            {
                //--NW case.
                if(tUsePosition.x <= (uLightPosition[i].x - uCameraX))
                {
                    float cXDif = tUsePosition.x - (uLightPosition[i].x - uCameraX);
                    float cYDif = tUsePosition.y - (uLightPosition[i].y - uCameraY);
                    cDistance = sqrt((cXDif * cXDif) + (cYDif * cYDif)) + tDistancePenalty;
                }
                //--NE case.
                else if(tUsePosition.x >= (uLightPosition[i].z - uCameraX))
                {
                    float cXDif = tUsePosition.x - (uLightPosition[i].z - uCameraX);
                    float cYDif = tUsePosition.y - (uLightPosition[i].y - uCameraY);
                    cDistance = sqrt((cXDif * cXDif) + (cYDif * cYDif)) + tDistancePenalty;
                }
                //--North Line case.
                else
                {
                    float cXDif = 0.0f;
                    float cYDif = tUsePosition.y - (uLightPosition[i].y - uCameraY);
                    cDistance = abs(cYDif) + tDistancePenalty;
                }
            }
            //--[South Cases]
            else if(tUsePosition.y > (uLightPosition[i].w - uCameraY))
            {
                //--SW case.
                if(tUsePosition.x <= (uLightPosition[i].x - uCameraX))
                {
                    float cXDif = tUsePosition.x - (uLightPosition[i].x - uCameraX);
                    float cYDif = tUsePosition.y - (uLightPosition[i].w - uCameraY);
                    cDistance = sqrt((cXDif * cXDif) + (cYDif * cYDif)) + tDistancePenalty;
                }
                //--SE case.
                else if(tUsePosition.x >= (uLightPosition[i].z - uCameraX))
                {
                    float cXDif = tUsePosition.x - (uLightPosition[i].z - uCameraX);
                    float cYDif = tUsePosition.y - (uLightPosition[i].w - uCameraY);
                    cDistance = sqrt((cXDif * cXDif) + (cYDif * cYDif)) + tDistancePenalty;
                }
                //--South Line case.
                else
                {
                    float cXDif = 0.0f;
                    float cYDif = tUsePosition.y - (uLightPosition[i].w - uCameraY);
                    cDistance = abs(cYDif) + tDistancePenalty;
                }
            }
            //--[West Line Case]
            else if(tUsePosition.x < (uLightPosition[i].x - uCameraX))
            {
                float cXDif = tUsePosition.x - (uLightPosition[i].x - uCameraX);
                float cYDif = 0.0f;
                cDistance = abs(cXDif) + tDistancePenalty;
            }
            //--[East Line Case]
            else if(tUsePosition.x > (uLightPosition[i].z - uCameraX))
            {
                float cXDif = tUsePosition.x - (uLightPosition[i].z - uCameraX);
                float cYDif = 0.0f;
                cDistance = abs(cXDif) + tDistancePenalty;
            }
            //--[Interior]
            //--If the point is within the light zone, it's max power.
            else
            {
                cDistance = 0.0f;
            }

            //--Add penalty.
            cDistance = cDistance + tDistancePenalty;

            //--Attenuation.
            float att = 1.0 / (1.0 + (0.1*cDistance) + (0.01*cDistance*cDistance));

            //--Apply.
            tIntensity.r = tIntensity.r + (uLightIntensity[i] * uLightColor[i].r * att * cDistanceFactor);
            tIntensity.g = tIntensity.g + (uLightIntensity[i] * uLightColor[i].g * att * cDistanceFactor);
            tIntensity.b = tIntensity.b + (uLightIntensity[i] * uLightColor[i].b * att * cDistanceFactor);
        }
    }

    //--Normalize the intensity.
    float tHighestIntensity = tIntensity.r;
    if(tIntensity.g > tHighestIntensity) tHighestIntensity = tIntensity.g;
    if(tIntensity.b > tHighestIntensity) tHighestIntensity = tIntensity.b;
    if(tHighestIntensity > 1.0f)
    {
        tIntensity.r = tIntensity.r / tHighestIntensity;
        tIntensity.g = tIntensity.g / tHighestIntensity;
        tIntensity.b = tIntensity.b / tHighestIntensity;
    }

    //--In fullbright, the intensity will normalize until at least one is at 1.0f.
    if(uFullbrightEntity == 1 && tHighestIntensity < 1.0f)
    {
        tIntensity.r = tIntensity.r + (1.0f - tHighestIntensity);
        tIntensity.g = tIntensity.g + (1.0f - tHighestIntensity);
        tIntensity.b = tIntensity.b + (1.0f - tHighestIntensity);
    }

    //--Multiply by color.
    tColor.r = tColor.r * tIntensity.r;
    tColor.g = tColor.g * tIntensity.g;
    tColor.b = tColor.b * tIntensity.b;

    //--Finalized color.
    gl_FragColor = tColor;
}
