#version 150
 
in vec4 Position;

uniform mat4 ProjMat;
uniform vec2 InSize;
uniform vec2 OutSize;

uniform sampler2D DiffuseSampler;

out vec2 texCoord;
out vec2 oneTexel;
out vec3 direction;
out float timeOfDay;
out float near;
out float far;
out mat4 projInv;
out vec4 fogColor;
out vec3 up;
out vec3 sunDir;
out float moonPhase;
out float weather;

#define FPRECISION 4000000.0
#define PROJNEAR 0.05

vec2 getControl(int index, vec2 screenSize) {
  return vec2(floor(screenSize.x / 2.0) + float(index) * 2.0 + 0.5, 0.5) / screenSize;
}

int intmod(int i, int base) {
  return i - (i / base * base);
}

vec3 encodeInt(int i) {
  int s = int(i < 0) * 128;
  i = abs(i);
  int r = intmod(i, 256);
  i = i / 256;
  int g = intmod(i, 256);
  i = i / 256;
  int b = intmod(i, 128);
  return vec3(float(r) / 255.0, float(g) / 255.0, float(b + s) / 255.0);
}

int decodeInt(vec3 ivec) {
  ivec *= 255.0;
  int s = ivec.b >= 128.0 ? -1 : 1;
  return s * (int(ivec.r) + int(ivec.g) * 256 + (int(ivec.b) - 64 + s * 64) * 256 * 256);
}

vec3 encodeFloat(float i) {
  return encodeInt(int(i * FPRECISION));
}

float decodeFloat(vec3 ivec) {
  return decodeInt(ivec) / FPRECISION;
}

void main() {
  vec4 outPos = ProjMat * vec4(Position.xy, 0.0, 1.0);
  gl_Position = vec4(outPos.xy, 0.2, 1.0);

  oneTexel = 1.0 / InSize;

  texCoord = Position.xy / OutSize;

  vec2 start = getControl(0, OutSize);
  vec2 inc = vec2(2.0 / OutSize.x, 0.0);

  mat4 ModelViewMat = mat4(decodeFloat(texture(DiffuseSampler, start + 16.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 17.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 18.0 * inc).xyz), 0.0,
              decodeFloat(texture(DiffuseSampler, start + 19.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 20.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 21.0 * inc).xyz), 0.0,
              decodeFloat(texture(DiffuseSampler, start + 22.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 23.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 24.0 * inc).xyz), 0.0,
              0.0, 0.0, 0.0, 1.0);

  mat4 ProjMat = mat4(tan(decodeFloat(texture(DiffuseSampler, start + 3.0 * inc).xyz)), decodeFloat(texture(DiffuseSampler, start + 6.0 * inc).xyz), 0.0, 0.0,
      decodeFloat(texture(DiffuseSampler, start + 5.0 * inc).xyz), tan(decodeFloat(texture(DiffuseSampler, start + 4.0 * inc).xyz)), decodeFloat(texture(DiffuseSampler, start + 7.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 8.0 * inc).xyz),
      decodeFloat(texture(DiffuseSampler, start + 9.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 10.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 11.0 * inc).xyz),  decodeFloat(texture(DiffuseSampler, start + 12.0 * inc).xyz),
      decodeFloat(texture(DiffuseSampler, start + 13.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 14.0 * inc).xyz), decodeFloat(texture(DiffuseSampler, start + 15.0 * inc).xyz), 0.0);

  sunDir = normalize((inverse(ModelViewMat) * vec4(
    decodeFloat(texture(DiffuseSampler, start).xyz),
    decodeFloat(texture(DiffuseSampler, start + inc).xyz),
    decodeFloat(texture(DiffuseSampler, start + 2.0 * inc).xyz),
    1.0
  )).xyz);

  up = vec3(0, 1, 0);

  fogColor = texture(DiffuseSampler, start + inc * 25);

  timeOfDay = dot(sunDir, vec3(0, 1, 0));

  near = PROJNEAR;
  far = ProjMat[3][2] * near / (ProjMat[3][2] + 2.0 * near);
  float fov = atan(1 / ProjMat[1][1]);

  projInv = inverse(ProjMat * ModelViewMat);

  vec2 squareUV = (texCoord - 0.5) / (OutSize.yy / OutSize.xy);

  direction = (projInv * vec4(outPos.xy * (far - near), far + near, far - near)).xyz;

  vec4 idx26 = texture(DiffuseSampler, start + 26.0 * inc);
  moonPhase = idx26.x;
  weather = idx26.y;
}
