(function() {

var gl          = null;
var aspect      = 1;
var vbuffers    = null;
var texture     = null;
var program     = null;
var uniformVars = null;
var count       = 0;
var drawTimer   = null;
var reinitTimer = null;

function initialize() {
  var contentEl = $('#contents');
  var width     = contentEl.innerWidth();
  var height    = contentEl.innerHeight();
  var size      = Math.min(width, height);
  contentEl.html('<canvas id="screen" width="' + size + '" height="' + size + '"></canvas>');

  var canvas = $("#screen").get(0);
  $.each(["webgl", "experimental-webgl"], function(i, name) {
    try { gl = canvas.getContext(name); } catch(e) {}
    return !gl
  });
  if(!gl) {
    alert("WebGL がサポートされていません。");
    return;
  }
  aspect = 1;

  initVertices();
  initTexture();
  initShaders();

  if(drawTimer) clearInterval(drawTimer);
  drawTimer = setInterval(redrawScene, 1000/30);
}

$(initialize);
$(window).resize(function(){
  if(reinitTimer) clearTimeout(reinitTimer);
  reinitTimer = setTimeout(initialize, 100);
});

function initVertices() {
  var positions = [-1,+1, +1,+1, -1,-1, +1,-1];
  var uvs       = [0,0, 1,0, 0,1, 1,1];

  vbuffers = $.map([positions, uvs], function(data, i) {
    var vbuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(data), gl.STATIC_DRAW);
    return vbuffer;
  });
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
}

function initTexture() {
  texture = gl.createTexture();

  var image = new Image();
  image.onload = function() {
    gl.enable(gl.TEXTURE_2D);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, image);
    gl.generateMipmap(gl.TEXTURE_2D);
    gl.bindTexture(gl.TEXTURE_2D, 0);
  }
  image.src = "images/texture1.jpg";
}

function initShaders() {
  var vshader = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vshader, $('#vshader').text());
  gl.compileShader(vshader);
  if(!gl.getShaderParameter(vshader, gl.COMPILE_STATUS))
    alert(gl.getShaderInfoLog(vshader));

  var fshader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(fshader, $('#fshader').text());
  gl.compileShader(fshader);
  if(!gl.getShaderParameter(fshader, gl.COMPILE_STATUS))
    alert(gl.getShaderInfoLog(fshader));

  program = gl.createProgram();
  gl.attachShader(program, vshader);
  gl.attachShader(program, fshader);

  $.each(["position", "uv"], function(i, name) {
    gl.bindAttribLocation(program, i, name);
  });

  gl.linkProgram(program);
  if(!gl.getProgramParameter(program, gl.LINK_STATUS))
    alert(gl.getProgramInfoLog(program));

  uniformVars = $.map(["mvpMatrix", "normalMatrix", "lightVec"], function(name) {
    return gl.getUniformLocation(program, name);
  });
}

function setUniform(i, value) {
  if(value instanceof CanvasMatrix4)
    gl.uniformMatrix4fv(uniformVars[i], false, value.getAsWebGLFloatArray());
  else
    gl.uniform4fv(uniformVars[i], new WebGLFloatArray(value));
};

function redrawScene() {
  count += 1;

  gl.clearColor(0, 0, 0, 0);
  gl.clearDepth(1000);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  gl.enable(gl.DEPTH_TEST);
  gl.useProgram(program);

  $.each([2, 2], function(i, stride) {
    gl.enableVertexAttribArray(i);
    gl.bindBuffer(gl.ARRAY_BUFFER, vbuffers[i]);
    gl.vertexAttribPointer(i, stride, gl.FLOAT, false, 0, 0);
  });

  gl.enable(gl.TEXTURE_2D);
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

  var lightVec  = [0.577, 0.577, 0.577, 0.0];
  setUniform(2, lightVec);

  var vpMatrix = new CanvasMatrix4();
  vpMatrix.rotate(30, 1, 0, 0);
  vpMatrix.translate(0, 0, -7);
  vpMatrix.perspective(30, aspect, 0.1, 1000);

  for(var i = 0 ; i < 5 ; ++i) {
	var modelMatrix = new CanvasMatrix4();
	if(i == 4) modelMatrix.rotate(-90, 1, 0, 0);
	modelMatrix.rotate(count + 90*i, 0, 1, 0);
	var mvpMatrix = new CanvasMatrix4(modelMatrix);
	mvpMatrix.multRight(vpMatrix);

	setUniform(0, mvpMatrix);
	setUniform(1, modelMatrix);

	gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  }

  gl.flush();
}

})();
