import { SmcErr } from './errors.js';

export { SmcProgramBuilder };

class SmcProgramBuilder {
  #gl;
  #program;

  #isBuilt = false;
  #hasVertexShader = false;
  #hasFragmentShader = false;

  #defaultVertexShader = `
    attribute vec4 aVertex;

    void main() {
      gl_Position = aVertex;
    }
  `;

  // TODO: reset the sample fragment shader back to the rainbow
  #sampleFragmentShader = `
    precision mediump float;

    // uniform float uTime;
    uniform vec2 uResolution;

    void main() {
      vec2 uv = gl_FragCoord.xy / uResolution.xy;
      // vec3 col = 0.5 + 0.5 * cos(uTime + uv.xyx + vec3(0, 2, 4));
      // gl_FragColor = vec4(col, 1.0);
      // gl_FragColor = vec4(216., 43., 72., 255.) / 255.;


      // float maxfc = max(gl_FragCoord.x, gl_FragCoord.y);
      // gl_FragColor = vec4(gl_FragCoord.xy, maxfc, maxfc) / maxfc;

      float maxuv = max(uv.x, uv.y);
      gl_FragColor = vec4(216. * maxuv, 43., 72., 255.) / 255.;

      
      // vec3 col = 0.5 + 0.5*cos(uv.xyx+vec3(0,2,4));
      // gl_FragColor = vec4(col, 1.);
    }
  `;


  constructor(gl, raiseError) {
    this.#gl = gl;
    this.#program = this.#gl.createProgram();
    this.raiseError = raiseError;
  }

  addVertexShader(source) {
    this.#gl.attachShader(
      this.#program,
      this.#newShader(
        this.#gl.VERTEX_SHADER,
        source
      )
    )
    this.#hasVertexShader = true;
    return this;
  }

  addFragmentShader(source) {
    this.#gl.attachShader(
      this.#program,
      this.#newShader(
        this.#gl.FRAGMENT_SHADER,
        source
      )
    )
    console.log(source)
    this.#hasFragmentShader = true;
    return this;
  }

  // fetchVertexShader(uri) {
  //   (async () => this.#fetchShader(uri, (source) => this.addVertexShader(source)))();
  //   return this;
  // }

  // async fetchFragmentShader(uri) {
  //   var delegate = (source) => this.addFragmentShader(source);
  //   var source = await this.#fetchShader(uri);
  //   return this;
  // }

  build() {
    // avoid user accidental calls to build()
    if (!this.#isBuilt) {
      if (!this.#hasVertexShader)
        this.addVertexShader(this.#defaultVertexShader)
      if (!this.#hasFragmentShader)
        this.addFragmentShader(this.#sampleFragmentShader);

      this.#gl.linkProgram(this.#program);
      this.#gl.useProgram(this.#program);
    }
    return this.#program;
  }

  // Creates a shader of the given type, uploads the source and compiles
  #newShader(type, source) {
    const shader = this.#gl.createShader(type);
    this.#gl.shaderSource(shader, source);
    this.#gl.compileShader(shader);

    if (!this.#gl.getShaderParameter(shader, this.#gl.COMPILE_STATUS)) {
      this.#gl.deleteShader(shader);
      const infoLog = this.#gl.getShaderInfoLog(shader);
      this.raiseError(
        SmcErr.SHADER_COMPILATION,
        new Error(`An error occurred while compiling the shader: ${infoLog}`)
      );
    }

    return shader;
  }

  // async #fetchShader(uri, delegate) {
  //   const res = await fetch(uri);
  //   if (res.ok)
  //     return await res.text();
  //   this.raiseError(
  //     SmcErr.FETCH_SHADER,
  //     `Failed to load shader source ${url}: ${res.status} ${res.json()}`);
  //   return ""
  // }

}

