const CHANNEL_COUNT = 4;

const HEADER = `#version 300 es
precision highp float;
precision highp int;
precision highp sampler2D;

uniform vec3 iResolution;
uniform float iTime;
uniform float iTimeDelta;
uniform float iFrameRate;
uniform int iFrame;
uniform float iChannelTime[4];
uniform vec3 iChannelResolution[4];
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;

#define texture2D texture
#define textureCube texture

out vec4 shadertoyFragColor;
`;

const FOOTER = `
void main() {
  vec4 color = vec4(0.0);
  mainImage(color, gl_FragCoord.xy);
  shadertoyFragColor = color;
}
`;

function buildFragmentSource({ code, common = "" }) {
  const commonCode = stripVersion(common || "");
  const shaderCode = stripVersion(code || "");
  const source = `${HEADER}
${commonCode}

${shaderCode}
${FOOTER}`;

  const lineOffset = countLines(HEADER) + countLines(commonCode) + 2;
  return { source, lineOffset };
}

function collectUniforms(gl, program) {
  const uniforms = {
    iResolution: gl.getUniformLocation(program, "iResolution"),
    iTime: gl.getUniformLocation(program, "iTime"),
    iTimeDelta: gl.getUniformLocation(program, "iTimeDelta"),
    iFrameRate: gl.getUniformLocation(program, "iFrameRate"),
    iFrame: gl.getUniformLocation(program, "iFrame"),
    iChannelTime: gl.getUniformLocation(program, "iChannelTime"),
    iChannelResolution: gl.getUniformLocation(program, "iChannelResolution"),
    iMouse: gl.getUniformLocation(program, "iMouse"),
    iDate: gl.getUniformLocation(program, "iDate"),
    iSampleRate: gl.getUniformLocation(program, "iSampleRate"),
    channels: [],
  };

  for (let index = 0; index < CHANNEL_COUNT; index += 1) {
    uniforms.channels[index] = gl.getUniformLocation(program, `iChannel${index}`);
  }

  return uniforms;
}

function setShadertoyUniforms(gl, uniforms, state, passSize, channelResolution) {
  gl.uniform3f(uniforms.iResolution, passSize.width, passSize.height, passSize.pixelRatio || 1);
  gl.uniform1f(uniforms.iTime, state.time);
  gl.uniform1f(uniforms.iTimeDelta, state.delta);
  gl.uniform1f(uniforms.iFrameRate, state.frameRate);
  gl.uniform1i(uniforms.iFrame, state.frame);
  gl.uniform1fv(uniforms.iChannelTime, state.channelTime);
  gl.uniform3fv(uniforms.iChannelResolution, channelResolution);
  gl.uniform4fv(uniforms.iMouse, state.mouse);
  gl.uniform4fv(uniforms.iDate, state.date);
  gl.uniform1f(uniforms.iSampleRate, 44100);

  for (let index = 0; index < CHANNEL_COUNT; index += 1) {
    gl.uniform1i(uniforms.channels[index], index);
  }
}

function stripVersion(source) {
  return source.replace(/^\s*#version\s+300\s+es\s*$/m, "");
}

function countLines(source) {
  if (!source) return 0;
  return source.split("\n").length;
}
