#ifndef TONEMAP
#define TONEMAP

#include "/lib/util.glsl"

vec3 uncharted2Tonemap(vec3 x) {
  float A = 0.25;
  float B = 0.1;
  float C = 0.06;
  float D = 0.2;
  float E = 0.02;
  float F = 1.6;
  return (x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F) - E / F;
}

vec3 uncharted2(vec3 v) {
  float exposure_bias = 1.0f;
  vec3 curr = uncharted2Tonemap(v * exposure_bias);

  vec3 W = vec3(11.2f);
  vec3 white_scale = vec3(1.0f) / uncharted2Tonemap(W);
  return pow(curr * white_scale, vec3(1.0 / 2.2));
}

vec3 aces_tonemap(vec3 color) {
  mat3 m1 = mat3(
    0.59719, 0.076  , 0.0284 ,
    0.35458, 0.90834, 0.13383,
    0.04823, 0.01566, 0.83777
  );
  mat3 m2 = mat3(
     1.60475, -0.10208, -0.00327,
    -0.53108,  1.10813, -0.07276,
    -0.07367, -0.00605,  1.07602
  );
  vec3 v = m1 * color;
  vec3 a = v * (v + 0.0245786) - 0.000090537;
  vec3 b = v * (0.983729 * v + 0.432951) + 0.238081;
  return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2));
}

vec3 reinhard_jodie(vec3 v) {
  float l = luminance(v);
  vec3 tv = v / (1.0f + v);
  return pow(mix(v / (1.0f + l), tv, tv), vec3(1.0 / 2.2));
}

//adapted from https://github.com/dmnsgn/glsl-tone-map/blob/main/agx.glsl
const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3(
   1.6605, -0.1246, -0.0182,
  -0.5876,  1.1329, -0.1006,
  -0.0728, -0.0083,  1.1187
);

const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3(
  0.6274, 0.0691, 0.0164,
  0.3293, 0.9195, 0.088 ,
  0.0433, 0.0113, 0.8956
);

// Converted to column major from blender: https://github.com/blender/blender/blob/fc08f7491e7eba994d86b610e5ec757f9c62ac81/release/datafiles/colormanagement/config.ocio#L358
const mat3 AgXInsetMatrix = mat3(
  0.856627153315983 , 0.137318972929847 , 0.11189821299995  ,
  0.0951212405381588, 0.761241990602591 , 0.0767994186031903,
  0.0482516061458583, 0.101439036467562 , 0.811302368396859
);

// Converted to column major and inverted from https://github.com/EaryChow/AgX_LUT_Gen/blob/ab7415eca3cbeb14fd55deb1de6d7b2d699a1bb9/AgXBaseRec2020.py#L25
// https://github.com/google/filament/blob/bac8e58ee7009db4d348875d274daf4dd78a3bd1/filament/src/ToneMapper.cpp#L273-L278
const mat3 AgXOutsetMatrix = mat3(
   1.1271005818144368  , -0.1413297634984383  , -0.14132976349843826 ,
  -0.11060664309660323 ,  1.157823702216272   , -0.11060664309660294 ,
  -0.016493938717834573, -0.016493938717834257,  1.2519364065950405
);

const float AgxMinEv = AGX_MIN_EV;
const float AgxMaxEv = AGX_MAX_EV;

// 0: Default, 1: Golden, 2: Punchy
#ifndef AGX_LOOK
#define AGX_LOOK 2
#endif

vec3 agxAscCdl(vec3 color, vec3 slope, vec3 offset, vec3 power, float sat) {
  const vec3 lw = vec3(0.2126, 0.7152, 0.0722);
  float luma = dot(color, lw);
  vec3 c = pow(color * slope + offset, power);
  return luma + sat * (c - luma);
}

// Sample usage
vec3 agx(vec3 color) {
  color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; // From three.js

  // 1. agx()
  // Input transform (inset)
  color = AgXInsetMatrix * color;

  color = max(color, 1e-10); // From Filament: avoid 0 or negative numbers for log2

  // Log2 space encoding
  color = clamp(log2(color), AgxMinEv, AgxMaxEv);
  color = (color - AgxMinEv) / (AgxMaxEv - AgxMinEv);

  color = clamp(color, 0.0, 1.0); // From Filament

  // Apply sigmoid function approximation
  // Mean error^2: 3.6705141e-06
  vec3 x2 = color * color;
  vec3 x4 = x2 * x2;
  color =
    +15.5 * x4 * x2 -
    40.04 * x4 * color +
    31.96 * x4 -
    6.868 * x2 * color +
    0.4298 * x2 +
    0.1191 * color -
    0.00232;

  // 2. agxLook()
  #if AGX_LOOK == 1
  // Golden
  color = agxAscCdl(color, vec3(1.0, 0.9, 0.5), vec3(0.0), vec3(0.8), 1.3);
  #elif AGX_LOOK == 2
  // Punchy
  color = agxAscCdl(
    color,
    vec3(1.0),
    AGX_OFFSET_COLOR,
    AGX_POWER,
    AGX_SATURATION
  );
  #endif

  // 3. agxEotf()
  // Inverse input transform (outset)
  color = AgXOutsetMatrix * color;

  // sRGB IEC 61966-2-1 2.2 Exponent Reference EOTF Display
  // NOTE: We're linearizing the output here. Comment/adjust when
  // *not* using a sRGB render target
  color = pow(max(vec3(0.0, 0.0, 0.0), color), vec3(2.2)); // From filament: max()

  color = LINEAR_REC2020_TO_LINEAR_SRGB * color; // From three.js
  // Gamut mapping. Simple clamp for now.
  color = clamp(color, 0.0, 1.0);

  return color;
}

vec3 TonemapGeneric(vec3 rgb) {
  
  vec4 GenericTonemapperContrastAndScaleAndOffsetAndCrosstalk = vec4(
    1.0,
    0.997265,
    1.1152,
    25
  );
  vec4 GenericTonemapperCrosstalkParams = vec4(1, 0, 0, 0);

  float peak = max(rgb.r, max(rgb.g, rgb.b));
  vec3 ratio = rgb / peak;
  peak = pow(peak, GenericTonemapperContrastAndScaleAndOffsetAndCrosstalk.x);
  peak =
    peak /
    (GenericTonemapperContrastAndScaleAndOffsetAndCrosstalk.y * peak +
      GenericTonemapperContrastAndScaleAndOffsetAndCrosstalk.z);

  return pow(
    mix(
      pow(ratio, vec3(1.0 / GenericTonemapperCrosstalkParams.x)),
      vec3(1.0),
      vec3(pow(peak, GenericTonemapperContrastAndScaleAndOffsetAndCrosstalk.w))
    ),
    vec3(GenericTonemapperCrosstalkParams.x)
  ) *
  peak;
}

vec3 hejlBurgessTonemap(vec3 v) {
  v /= 2.0;
  vec3 x = max(v - 0.004, 0);
  return x * (6.2 * x + 0.5) / (x * (6.2 * x + 1.7) + 0.06);
}

// https://github.com/dmnsgn/glsl-tone-map/blob/main/lottes.glsl
vec3 lottesTonemap(vec3 x) {
  x *= 0.4;

  const vec3 a = vec3(1.6);
  const vec3 d = vec3(0.977);
  const vec3 hdrMax = vec3(8.0);
  const vec3 midIn = vec3(0.18);
  const vec3 midOut = vec3(0.267);

  const vec3 b =
    (-pow(midIn, a) + pow(hdrMax, a) * midOut) /
    ((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
  const vec3 c =
    (pow(hdrMax, a * d) * pow(midIn, a) -
      pow(hdrMax, a) * pow(midIn, a * d) * midOut) /
    ((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);

  return pow(pow(x, a) / (pow(x, a * d) * b + c), vec3(1.0 / 2.2));
}

#endif //TONEMAPPING_GLSL
