#version 120

uniform vec2 framebufferSize;
uniform vec4 axesColor;
uniform vec4 lowColor;
uniform vec4 midColor;
uniform vec4 highColor;

uniform int waveformLength;
uniform int textureSize;
uniform int textureStride;

uniform float allGain;
uniform float lowGain;
uniform float midGain;
uniform float highGain;
uniform float firstVisualIndex;
uniform float lastVisualIndex;

uniform sampler2D waveformDataTexture;

vec4 getWaveformData(float index) {
    vec2 uv_data;
    uv_data.y = floor(index / float(textureStride));
    uv_data.x = floor(index - uv_data.y * float(textureStride));
    // Divide again to convert to normalized UV coordinates.
    return texture2D(waveformDataTexture, uv_data / float(textureStride));
}

void main(void) {
    vec2 uv = gl_TexCoord[0].st;
    vec4 pixel = gl_FragCoord;

    float new_currentIndex = floor(firstVisualIndex + uv.x *
                                   (lastVisualIndex - firstVisualIndex)) * 2;

    // Texture coordinates put (0,0) at the bottom left, so show the right
    // channel if we are in the bottom half.
    if (uv.y < 0.5) {
        new_currentIndex += 1;
    }

    vec4 outputColor = vec4(0.0, 0.0, 0.0, 0.0);
    bool showing = false;
    bool showingUnscaled = false;
    vec4 showingColor = vec4(0.0, 0.0, 0.0, 0.0);
    vec4 showingUnscaledColor = vec4(0.0, 0.0, 0.0, 0.0);

    // We don't exit early if the waveform data is not valid because we may want
    // to show other things (e.g. the axes lines) even when we are on a pixel
    // that does not have valid waveform data.
    if (new_currentIndex >= 0 && new_currentIndex <= waveformLength - 1) {
      vec4 new_currentDataUnscaled = getWaveformData(new_currentIndex) * allGain;

      vec4 new_currentData = new_currentDataUnscaled;
      new_currentData.x *= lowGain;
      new_currentData.y *= midGain;
      new_currentData.z *= highGain;

      //(vrince) debug see pre-computed signal
      //gl_FragColor = new_currentData;
      //return;

      // Represents the [-1, 1] distance of this pixel. Subtracting this from
      // the signal data in new_currentData, we can tell if a signal band should
      // show in this pixel if the component is > 0.
      float ourDistance = abs((uv.y - 0.5) * 2.0);

      // Since the magnitude of the (low, mid, high) vector is used as the
      // waveform height, re-scale the maximum height to 1.
      const float scaleFactor = 1.0 / sqrt(3.0);

      float signalDistance = sqrt(new_currentData.x * new_currentData.x +
                                  new_currentData.y * new_currentData.y +
                                  new_currentData.z * new_currentData.z) *
                             scaleFactor;
      showing = (signalDistance - ourDistance) >= 0.0;

      // Linearly combine the low, mid, and high colors according to the low,
      // mid, and high components.
      showingColor = lowColor * new_currentData.x +
                     midColor * new_currentData.y +
                     highColor * new_currentData.z;

      // Re-scale the color by the maximum component.
      float showingMax = max(showingColor.x, max(showingColor.y, showingColor.z));
      showingColor = showingColor / showingMax;
      showingColor.w = 1.0;

      // Now do it all over again for the unscaled version of the waveform,
      // which we will draw at very low opacity.
      float signalDistanceUnscaled = sqrt(new_currentDataUnscaled.x * new_currentDataUnscaled.x +
                                          new_currentDataUnscaled.y * new_currentDataUnscaled.y +
                                          new_currentDataUnscaled.z * new_currentDataUnscaled.z) * scaleFactor;
      showingUnscaled = (signalDistanceUnscaled - ourDistance) >= 0.0;

      // Linearly combine the low, mid, and high colors according to the
      // original low, mid, and high components.
      showingUnscaledColor = lowColor * new_currentDataUnscaled.x +
                             midColor * new_currentDataUnscaled.y +
                             highColor * new_currentDataUnscaled.z;

      // Re-scale the color by the maximum component.
      float showingUnscaledMax = max(showingUnscaledColor.x, max(showingUnscaledColor.y, showingUnscaledColor.z));
      showingUnscaledColor = showingUnscaledColor / showingUnscaledMax;
      showingUnscaledColor.w = 1.0;
    }

    // Draw the axes color as the lowest item on the screen.
    // TODO(owilliams): The "4" in this line makes sure the axis gets
    // rendered even when the waveform is fairly short.  Really this
    // value should be based on the size of the widget.
    if (abs(framebufferSize.y / 2 - pixel.y) <= 4) {
      outputColor.xyz = mix(outputColor.xyz, axesColor.xyz, axesColor.w);
      outputColor.w = 1.0;
    }

    if (showingUnscaled) {
      float alpha = 0.4;
      outputColor.xyz = mix(outputColor.xyz, showingUnscaledColor.xyz, alpha);
      outputColor.w = 1.0;
    }

    if (showing) {
      float alpha = 0.8;
      outputColor.xyz = mix(outputColor.xyz, showingColor.xyz, alpha);
      outputColor.w = 1.0;
    }
    gl_FragColor = outputColor;

}
