//
// Copyright 2022 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// OverlayDraw.frag: Shade overlay widgets.  The quad generated by OverlayDraw.vert is shaded
// according to whether it corresponds to a text or graph widget.
//
// - For text widgets, the font texture is sampled based on the text and blended with a gray
//   background to increase readability.
// - For graph widgets, a black border is output along with bars derived from graph data.

#version 450 core

#extension GL_GOOGLE_include_directive : require

#include "OverlayDraw.inc"

layout(location = 0) flat in uint widgetIndex;
layout(location = 0) out vec4 color;

uint getChar(const uvec2 coordInWidget, const uint fontGlyphWidth)
{
    const uint charIndex = coordInWidget.x / fontGlyphWidth;
    const uint packIndex = charIndex / 4;
    const uvec4 arrayItem = textWidgetsData[widgetIndex].text[packIndex / 4];
    const uint col = packIndex % 4;
    // Work around Android bug with dynamic indexing of the vector component.
    uint packedChars;
    if (col == 0)
    {
        packedChars = arrayItem[0];
    }
    else if (col == 1)
    {
        packedChars = arrayItem[1];
    }
    else if (col == 2)
    {
        packedChars = arrayItem[2];
    }
    else
    {
        packedChars = arrayItem[3];
    }

    const uint shift = (charIndex % 4) * 8;

#if IsBigEndian
    return (packedChars >> (24 - shift)) & 0xFF;
#else
    return (packedChars >> shift) & 0xFF;
#endif
}

float sampleFont(const uint textChar,
                 const uvec2 coordInWidget,
                 const uvec2 fontGlyphSize,
                 const uint fontMip)
{
    const uvec2 coordInGlyph = coordInWidget % fontGlyphSize;
    return texelFetch(font, ivec3(coordInGlyph, textChar), int(fontMip)).x;
}

vec4 renderText(uvec2 coordInWidget)
{
    const uvec4 fontSizePacked = textWidgetsData[widgetIndex].fontSize;
    const uvec2 fontGlyphSize = fontSizePacked.xy;
    const uint fontMip = fontSizePacked.z;

    // Find the character corresponding to this pixel.
    const uint textChar = getChar(coordInWidget, fontGlyphSize.x);

    // The FONT_CHARACTERS value is a value filled where there is no character, so we don't add a
    // background to it.
    if (textChar >= FONT_CHARACTERS)
    {
        return vec4(0);
    }

    // Sample the font based on this character.
    const float sampleValue = sampleFont(textChar, coordInWidget, fontGlyphSize, fontMip);

    vec4 result = vec4(0, 0, 0, 0.4);
    result = mix(result, textWidgetsData[widgetIndex].color, sampleValue);

    return result;
}

uint getValue(const uvec2 coordInWidget, const uint valueWidth)
{
    const uint valueIndex = coordInWidget.x / valueWidth.x;
    const uvec4 arrayItem = graphWidgetsData[widgetIndex].values[valueIndex / 4];
    const uint col = valueIndex % 4;
    // Work around Android bug with dynamic indexing of the vector component.
    if (col == 0)
    {
        return arrayItem[0];
    }
    else if (col == 1)
    {
        return arrayItem[1];
    }
    else if (col == 2)
    {
        return arrayItem[2];
    }
    else
    {
        return arrayItem[3];
    }
}

vec4 renderGraph(uvec2 coordInWidget)
{
    const uvec4 widgetCoords = graphWidgetsData[widgetIndex].coordinates;

    // Use a black border around the graph to mark the area.
    if (coordInWidget.x == 0 || coordInWidget.y == 0 ||
        coordInWidget.x + 1 == (widgetCoords.z - widgetCoords.x) ||
        coordInWidget.y + 1 == (widgetCoords.w - widgetCoords.y))
    {
        return vec4(0, 0, 0, 1);
    }

    const uint valueWidth = graphWidgetsData[widgetIndex].valueWidth.x;

    // Find the value corresponding to this pixel.
    const uint value = getValue(coordInWidget, valueWidth);

    const uint widgetHeight = widgetCoords.w - widgetCoords.y;

    // If the graph value overflows the designated area, have the last four rows show a checkerboard
    // pattern to signify that there is an overflow.
    bool indicateOverflow = value > widgetHeight && coordInWidget.y + 4 >= widgetHeight
            && ((coordInWidget.x ^ coordInWidget.y) & 1) == 0;

    if ((widgetHeight - coordInWidget.y) >= value || indicateOverflow)
    {
        return vec4(0);
    }

    return graphWidgetsData[widgetIndex].color;
}

void main()
{
    const uvec4 widgetCoords = params.isText
        ? textWidgetsData[widgetIndex].coordinates
        : graphWidgetsData[widgetIndex].coordinates;
    uvec2 fragCoords = uvec2(floor(gl_FragCoord.xy));

    if (params.rotateXY)
    {
        // Undo the vertex shader's 90 degree rotation.
        fragCoords.x = params.viewportSize.x - 1 - fragCoords.x;
        fragCoords = fragCoords.yx;
    }

    // Calculate the offset inside the widget.
    const uvec2 coordInWidget = fragCoords - widgetCoords.xy;

    // Assert that coordinates are within bounds.
    if (any(lessThan(coordInWidget, uvec2(0))) ||
        any(greaterThanEqual(coordInWidget, widgetCoords.zw - widgetCoords.xy)))
    {
        color = vec4(1, 0, 1, 1);
        return;
    }

    if (params.isText)
    {
        color = renderText(coordInWidget);
    }
    else
    {
        color = renderGraph(coordInWidget);
    }
}
