/* Copyright © 2009-2010, Julien Quint <consulting@romulusetrem.us>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   • Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   • Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   • Neither the name of consulting.romulusetrem.us nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE. */


// 2010-01-26: Firefox support; load button.
var MEDIA_ERROR = ["No or unknown error", "MEDIA_ERR_ABORTED",
  "MEDIA_ERR_NETWORK", "MEDIA_ERR_DECODE", "MEDIA_ERR_SRC_NOT_SUPPORTED"];

var fps = 15;      // frames per second for video sampling
var rpf = π / 45;  // rotation per frame

// Set everything up; wait for the video to be loaded to finish
// initializing the canvases and start playing the video
function go()
{
  var rotate = $("#rotate");  // rotate checkbox
  rotate.checked = true;      // auto rotate by default
  var div = $(".m");
  var x_div = 0;
  var y_div = 0;
  for (var e = div; e.constructor != HTMLHtmlElement;
      e = e.parentNode, x_div += e.offsetLeft, y_div += e.offsetTop);
  var θ = 0;  // angle for manual rotation (inside the kaleidoscope)
  var r = 0;  // increment for automatic rotation

  // Handle mouse draggin to rotate when automatic rotation is turned off
  div.addEventListener("mousedown", function(e) {
      if (rotate.checked) rotate.checked = false;
      div.add_class("rotating");
      e.preventDefault();
    }, false);
  div.addEventListener("mousemove", function(e) {
      if (div.has_class("rotating")) {
        θ = Math.atan2(e.pageY - y_div, e.pageX - x_div) * 180.0 / π;
      }
      e.preventDefault();
    }, false);
  div.addEventListener("mouseup", function(e) {
      div.remove_class("rotating");
      e.preventDefault();
    }, false);

  // Init the parameters for the canvas transformations (flip them
  // alternatively and rotate each by 60 degrees more than the previous
  // one.)
  var canvases = $$("div canvas");
  var _θ = 0;
  var flip = false;
  canvases.for_each(function(c) {
      c._θ = _θ;
      _θ += 60;
      c._flip = flip;
      flip = !flip;
    });
  var contexts = canvases.map(function(c) { return c.getContext("2d"); });
  var video = $("video");
  var vsrc = $("#video_url");   // source for video

  $$("source").for_each(function(x) {
      if (x.src != video.currentSrc) {
        $(".{0}".fmt(x.className)).add_class("hidden");
      }
    });
  if (video.currentSrc) {
    $(".none").add_class("hidden");
    vsrc.value = video.currentSrc;
  }

  var video_sampling = null;
  $("#video_load").addEventListener("click", function(e) {
      if (video_sampling) clearInterval(video_sampling);
      $(".w").show(true);
      video.pause();
      video.src = vsrc.value;
      video.load();
    }, false)

  var mute = $("#mute");
  mute.addEventListener("change", function() {
      video.volume = mute.checked ? 0 : 1;
    }, false);

  // Now wait for video to be ready...
  video.addEventListener("canplaythrough", function() {
      var h = Math.min(video.videoWidth, video.videoHeight);
      var x = 2 * h / Math.sqrt(3);
      var xv = (video.videoWidth - h) / 2;
      var yv = (video.videoHeight - h) / 2;
      div.style.width = (2 * x) + "px"
      div.style.height = (2 * h) + "px";
      x_div += x;
      y_div += h;
      // Resize the canvases
      canvases.for_each(function(c) {
          c.style.left = (x / 2) + "px";
          c.width = x;
          c.height = h;
        });
      // Make the clipping mask (a π/3 radian wedge of a circle)
      // Unfortunately the clipping is a bit messy, in Safari at least
      // h_ is the radius of the circle, y_ is used to compute the end
      // points  of the wedge (the x one is easy since cos(π/3) = 0.5)
      var h_ = h / (Math.sqrt(3) * 2 / 3);
      var y_ = h - h_ * Math.sin(2 * π / 3);
      contexts.forEach(function(c) {
          c.beginPath();
          c.moveTo(x / 2, h);
          c.lineTo(x / 2 - h_ / 2, y_);
          c.arcTo(x / 2, 0, x / 2 + h_ / 2, y_, h_);
          c.lineTo(x / 2, h);
          c.clip();
        });
      // Draw a frame: in each canvas, draw the visible part of the video
      // after a rotation of the current angle around the center of the
      // video window. Canvases themselves are rotated as well.
      function draw_frame()
      {
        contexts.forEach(function(c) {
            c.save();
            c.clearRect(0, 0, x, h);
            c.translate(x / 2, h / 2);
            c.rotate(rotate.checked ? r * rpf : θ);
            c.translate(-x / 2, -h / 2);
            c.drawImage(video, xv, yv, x, h, 0, 0, x, h);
            c.restore();
          });
        canvases.for_each(function(c) {
            c.style.WebkitTransform =
            c.style.MozTransform = "{0}rotate({1}deg)"
              .fmt(c._flip ? "scaleX(-1) " : "",
                c._θ + 8 * (rotate.checked ? r * rpf : θ) *
                (c._flip ? 1 : -1));
          });
        ++r;
      }
      video_sampling = setInterval(draw_frame, 1000.0 / fps);
      video.volume = mute.checked ? 0 : 1;
      video.play();
      // Video is loaded so hide the loading image
      $(".w").show(false);
    }, false);

  video.addEventListener("error", function() {
      $(".w").add_class("hidden");
      alert("Error loading video: {0}".fmt(MEDIA_ERROR[video.error.code]));
    }, false)
}
