/// License: MIT
module sj.TitleTextProgram;

import gl4d;

import sj.util.image;

///
class TitleTextProgram {
 public:
  ///
  static struct Vertex {
   public:
    ///
    align(1) vec3 pos;
    ///
    align(1) vec2 uv;
  }

  ///
  enum ShaderHeader = "#version 330 core
#extension GL_ARB_explicit_uniform_location : enable";

  ///
  enum VertexShaderSrc = ShaderHeader ~ q{
    layout(location = 0) uniform mat4 P;
    layout(location = 1) uniform mat4 V;
    layout(location = 2) uniform mat4 M;

    layout(location = 0) in vec3 vert;
    layout(location = 1) in vec2 uv;

    out vec2 uv_;

    void main() {
      uv_ = uv;
      gl_Position = P * V * M * vec4(vert, 1);
    }
  };
  ///
  enum FragmentShaderSrc = ShaderHeader ~ q{
    layout(location = 3) uniform sampler2D tex;
    layout(location = 4) uniform int frame;

    in vec2 uv_;

    out vec4 pixel_;

    float stepstep(float a, float b, float x) {
      return step(a, x) * (1-step(b, x));
    }

    void main() {
      vec2 uv = uv_;

      uv.x += stepstep(0.1, 0.2, uv.y) *
        step(51.0, float(frame%61)) * 0.03;
      uv.x += stepstep(0.3, 0.35, uv.y) *
        step(85.0, float(frame%103)) * -0.05;
      uv.x += stepstep(0.3, 0.4, uv.y) *
        step(294.0, float(frame%303)) * 0.07;
      uv.x += stepstep(0.35, 0.45, uv.y) *
        step(475.0, float(frame%829)) * -0.01;
      uv.x += stepstep(0.5, 0.6, uv.y) *
        step(22.0, float(frame%78)) * 0.002;
      uv.x += stepstep(0.55, 0.65, uv.y) *
        step(32.0, float(frame%37)) * -0.007;
      uv.x += stepstep(0.85, 0.95, uv.y) *
        step(82.0, float(frame%273)) * 0.004;
      uv.x += stepstep(0.7, 0.9, uv.y) *
        step(152.0, float(frame%203)) * -0.005;

      vec4 texel = texture(tex, clamp(uv, 0, 1));
      pixel_.r = texel.a;
      pixel_.g = texel.b;
      pixel_.b = texel.g;
      pixel_.a = texel.r;

      pixel_.r += stepstep(0.2, 0.25, uv.y) *
        step(20.0, float(frame%30)) * 0.2;
      pixel_.r += stepstep(0.5, 0.55, uv.y) *
        step(83.0, float(frame%127)) * 0.2;
      pixel_.r += stepstep(0.6, 0.75, uv.y) *
        step(18, float(frame%21)) * 0.2;
    }
  };

  ///
  enum ImgBuf = cast(ubyte[]) import("images/title.png");

  ///
  this() {
    ProgramLinker linker;
    linker.vertex   = VertexShader.Compile(VertexShaderSrc);
    linker.fragment = FragmentShader.Compile(FragmentShaderSrc);
    program_ = linker.Link();
    program_.Validate();

    tex_     = CreateTextureFromBuffer(ImgBuf);
    sampler_ = Sampler.Create();

    with (SamplerConfigurer()) {
      filterMin = GL_LINEAR;
      filterMag = GL_LINEAR;
      Configure(sampler_);
    }

    vao_      = VertexArray.Create();
    vertices_ = ArrayBuffer.Create();

    vao_.Bind();
    with (VertexArrayAttacher()) {
      index     = 0;
      type      = GL_FLOAT;
      offset    = 0;
      stride    = Vertex.sizeof;
      dimension = 3;
      Attach(vao_, vertices_);

      index     = 1;
      type      = GL_FLOAT;
      offset    = vec3.sizeof;
      stride    = Vertex.sizeof;
      dimension = 2;
      Attach(vao_, vertices_);
    }

    vertices_.Bind();
    with (ArrayBufferAllocator()) {
      const v = [
        -1f,  1f, 0f,  1f, 0f,
        -1f, -1f, 0f,  1f, 1f,
         1f, -1f, 0f,  0f, 1f,
         1f,  1f, 0f,  0f, 0f,
      ];
      data  = v.ptr;
      size  = v.length * v[0].sizeof;
      usage = GL_STATIC_DRAW;
      Allocate(vertices_);
    }
  }

  ///
  void Draw(mat4 proj, mat4 view, mat4 model, int frame) {
    tex_.BindToUnit(GL_TEXTURE0);
    sampler_.Bind(0);

    program_.Use();
    program_.uniform!0 = proj;
    program_.uniform!1 = view;
    program_.uniform!2 = model;
    program_.uniform!3 = 0;
    program_.uniform!4 = frame;

    vao_.Bind();
    gl.DrawArrays(GL_TRIANGLE_FAN, 0, 4);
  }

 private:
  ProgramRef program_;

  Texture2DRef tex_;

  SamplerRef sampler_;

  VertexArrayRef vao_;

  ArrayBufferRef vertices_;
}
