uniform bool isCubicInterpolation, isWiggle, isSeismicBackground;

uniform sampler2D PropertyTex0, PropertyTex1, PropertyTex2, PropertyTex3;
uniform sampler2D Lookup1Bit;
uniform vec2 cTransferFunctionOffsetScale[4];
uniform ivec2 TexSize[4];

in vec3 vPosition;
in vec3 vObjPos;
in vec3 vNormal;
in vec2 vTexCoord[4];

uniform vec2 cValueScaleOffset[4];
uniform vec2 cWiggleScale;

uniform vec4 BackgroundColor, LineColor, FillColor, NegFillColor;

ivec2 MaxOverlap = ivec2(-2, 2);

vec4 SampleWiggleCubic2D(sampler2D s, float rW, vec2 p)
{
  float rWRCP = 1.0 / rW;
  float tmp = floor(p.x * rW - 0.5) + 0.5;
  float t = fract(p.x * rW - 0.5);
  p.x = tmp * rWRCP;

  vec4 acSamples[4];
  acSamples[0] = texture2D(s, p - vec2(rWRCP, 0.0));
  acSamples[1] = texture2D(s, p);
  acSamples[2] = texture2D(s, p + vec2(rWRCP, 0.0));
  acSamples[3] = texture2D(s, p + vec2(2.0 * rWRCP, 0.0));

  vec4 v[2];
  v[0] = acSamples[1];
  v[1] = acSamples[2];
  vec4 m[2];
  m[0] = (acSamples[2] - acSamples[0]) / 2.0;
  m[1] = (acSamples[3] - acSamples[1]) / 2.0;

  float t2 = t * t;
  float t3 = t2 * t;
  float w[4];
  w[0] =  2.0 * t3 - 3.0 * t2 + 1.0;
  w[1] =  1.0 * t3 - 2.0 * t2 + t;
  w[2] = -2.0 * t3 + 3.0 * t2;
  w[3] =  1.0 * t3 - 1.0 * t2;

  return v[0] * w[0] + m[0] * w[1] + v[1] * w[2] + m[1] * w[3];
}

float GetWiggleValue(vec2 TexCoord)
{
  float rValue;
  if (isCubicInterpolation)
    rValue = SampleWiggleCubic2D(PropertyTex0, float(TexSize[0].x), TexCoord).r;
  else
    rValue = texture2D(PropertyTex0, TexCoord).r;

  rValue = rValue * cWiggleScale.x + cWiggleScale.y;
  return min(max(rValue, float(MaxOverlap.x)), float(MaxOverlap.y));
}

vec4 PixelOver(vec4 a, vec4 b)
{
  // pre-multiply
  a.rgb *= a.a;
  b.rgb *= b.a;

  vec4 c = a + b * (1.0 - a.a);

  // unpre-multiply
  if (abs(c.a) > 0.001)
    c.rgb /= c.a;
  else
    c = vec4(0.0);

  return c;
}

vec4 Wiggle(vec2 uv)
{
  vec2 duvdx = dFdx(uv) * 0.5, duvdy = dFdy(uv) * 0.5;

  float rTemp = uv.y * float(TexSize[0].y);
  int iStartTrace = int(floor(rTemp)) + MaxOverlap.x;
  int iStopTrace  = int(ceil (rTemp)) + MaxOverlap.y;

  vec4 fill = vec4(0.0);
  vec4 line = vec4(0.0);
  for (int i = iStartTrace; i < iStopTrace; ++i)
  {
    float trace = (float(i) + 0.5) / float(TexSize[0].y);
    float frac = (uv.y - trace) * float(TexSize[0].y);
    float val = GetWiggleValue(vec2(uv.x, trace));

    vec4 fracs = vec4(frac);
    fracs.x -= duvdx.y * float(TexSize[0].y);
    fracs.y += duvdx.y * float(TexSize[0].y);
    fracs.z -= duvdy.y * float(TexSize[0].y);
    fracs.w += duvdy.y * float(TexSize[0].y);
    vec4 vals = vec4(
      GetWiggleValue(vec2(uv.x - duvdx.x, trace)), GetWiggleValue(vec2(uv.x + duvdx.x, trace)),
      GetWiggleValue(vec2(uv.x - duvdy.x, trace)), GetWiggleValue(vec2(uv.x + duvdy.x, trace)));

    if (duvdx.y < 0.0)
    {
      frac = -frac;
      fracs = -fracs;
    }

    bvec4 edge = lessThan(fracs, vals);
    if (any(edge) != all(edge)) line = PixelOver(line, LineColor);

    if (frac > 0.0 && val > frac) fill = PixelOver(fill, FillColor);
    else if (frac < 0.0 && val < frac) fill = PixelOver(fill, NegFillColor);
  }

  float fade = 1.0 / max(1.0, fwidth(uv.y * float(TexSize[0].y)) * 4.0);
  line.a *= fade;

  return PixelOver(line, fill);
}

vec4 GetShaderColor(); // generated

vec4 GetColor()
{
  DepthPeel();
  vec4 cColor = isSeismicBackground ? GetShaderColor() : BackgroundColor;
  if (isWiggle)
  {
    cColor = PixelOver(Wiggle(vTexCoord[0]), cColor);
  }
  if (cColor.a < 1e-3) discard;
  return cColor;
}
