function Intro(){

 var SCREEN_X = 160;
 var SCREEN_Y = 100;

 function getStyle(index) {
   return [
     c_filters[0]*128 * (1 + Math.sin(c_cycles[0]*offset/16 + c_multipliers[0]*index*Math.PI)),
     c_filters[1]*128 * (1 - Math.cos(c_cycles[1]*offset/16 + c_multipliers[1]*index*Math.PI)),
     c_filters[2]*128 * (1 - Math.sin(c_cycles[2]*offset/16 + c_multipliers[2]*index*Math.PI)),
   ];
 }

 function draw(weights) {
   var ctx = document.getElementById("canvas").getContext("2d");
   var screen = ctx.getImageData(0, 0, SCREEN_X, SCREEN_Y);
   var imgData = screen.data;
   var sin_offset_64th = Math.sin(offset/64),
       cos_offset_64th = Math.cos(offset/64),
       sin_offset_32th = Math.sin(offset/32),
       cos_offset_32th = Math.cos(offset/32),
       cos_offset_16th = Math.cos(offset/16);

   var cx1 = SCREEN_X/2 + SCREEN_Y *(sin_offset_64th),
       cy1 = SCREEN_Y/2 - SCREEN_Y *(cos_offset_64th);

   var cx2 = SCREEN_X/2 - SCREEN_Y *((1 - sin_offset_64th))*cos_offset_32th,
       cy2 = SCREEN_Y/2 + SCREEN_Y *((1 - cos_offset_64th))*sin_offset_32th;

   var sinVal1, sinVal2, cosVal1, cosVal2, val, pixel_start, style;

   for (var i = 0; i < SCREEN_Y; i++) {
     for (var j = 0; j < SCREEN_X; j++) {
       sinVal1 = Math.sin((j/16) + offset/16 * cos_offset_64th );
       sinVal2 = Math.sin( (2*j * sin_offset_32th + 2*i * cos_offset_16th)/16 + offset/16 );
       cosVal1 = Math.cos(0.4*(Math.sqrt(((j-cx1) * (j-cx1)) + ((i-cy1) * (i-cy1))))-offset);
       cosVal2 = Math.cos(0.4*(Math.sqrt(((j-cx2) * (j-cx2)) + ((i-cy2) * (i-cy2))))-offset);
       val = (weights[0]*sinVal1 + weights[1]*sinVal2 + weights[2]*cosVal1 + weights[3]*cosVal2) / 4;

       pixel_start = (i*SCREEN_X + j)*4;
       style = getStyle(val);
       imgData[pixel_start]=style[0];
       imgData[pixel_start+1]=style[1];
       imgData[pixel_start+2]=style[2];
       imgData[pixel_start+3]=255;
     }
   }
   ctx.putImageData(screen, 0, 0);
 }


 var PERIOD = Math.PI*256;
 var PERIOD_DURATION_MS = 26600;

 function render(timestamp) {
   var elapsed_total = Math.max(timestamp - started, 0);
   var elapsed_cycle = Math.max(timestamp - cycle_started, 0);
   draw(weights);
   offset = PERIOD * elapsed_cycle / PERIOD_DURATION_MS; //(offset + 0.51);
   if (Math.floor(elapsed_total / PERIOD_DURATION_MS)% steps.length != step){
     // console.log("reset! "+ (timestamp - cycle_started) + " ms");
     offset = 0;
     periodStart();
   }
   if (!stopped){
    window.requestAnimationFrame(render);
   }
 }

 function start(){
   showText('');
   offset = 0;
   started = performance.now();
   weights = [0,0,0,0];
   c_cycles = [0,0,0];
   c_multipliers = [0,0,0];
   c_filters = [0,0,0];
   step = -1;
   stopped = false;

   setSize();
   periodStart();
   doPlasmaTransition();
   window.requestAnimationFrame(render);
 }

 function stop(){
   stopped = true;
   stopAudio();
   showText('');
 }

 function periodStart(){
   cycle_started = performance.now();
   step = (step + 1) % steps.length;
   // console.log("step: "+step);
   startTextScript();
   if (step == 0){
     var audio_scheduled_at = performance.now();
     setTimeout(function(){
       if (!stopped && audio_scheduled_at >= started){
               startAudio();
       } else {
         console.log("Outdated audio timer");
       }
     }, 8500);
   }
 }

 function _adjust(valueList, goalList, delta){
   for (var i=0; i<=Math.min(valueList.length, goalList.length); i++){
     if (valueList[i] < goalList[i]){
       valueList[i] += delta;
       valueList[i] = Math.min(goalList[i], valueList[i]);
     } else if (valueList[i] > goalList[i]){
       valueList[i] -= delta;
       valueList[i] = Math.max(goalList[i], valueList[i]);
     }
   }
 }

 function doPlasmaTransition(){
   var delta = 0.025;
   _adjust(weights, steps[step][0], delta);
   _adjust(c_cycles, steps[step][1], delta);
   _adjust(c_multipliers, steps[step][2], delta);
   _adjust(c_filters, steps[step][3], delta);
   schedule(doPlasmaTransition, 100);
 }

 var offset = 0;
 var started, cycle_started;
 var stopped = true;

 var steps = [ // plasma composition, cycles, multipliers, filters:
   [[1,0,0,0],   [0,0,0], [1,1,1], [0,0,1]],  // blue vertical bars
   [[0.5,1,0,0], [0,0,0], [1,2,2], [0,1,1]],  // blue-green fabric
   //[[0.4,0.5,0,0.2], [0,0,0], [4,4,4], [1,1,1]], // orange-blue circular waves

   [[0.15,0,0,1], [0,0,0], [2,2,-2], [1,1,1]],  // magenta-green circles
   [[0.8,0.2,0.1,0.1], [3,3,3], [4,3,4], [1,1,1]], // saturated waves
   [[1,1,1,1], [1,0,0], [1,1,1], [1,1,1]],    // pallette cycles
   [[0,0,1,1], [0,0,0], [1,0,-1], [1,1,1]],   // magenta waves
 ];

 var weights = [0,0,0,0]; // steps[0][0];

 var c_cycles = [0,0,0];      // steps[0][1];  // color waves (0=none)
 var c_multipliers = [0,0,0]; // steps[0][2];  // number of stripes
 var c_filters = [0,0,0];     // steps[0][3];  // color opacity (0-1)

 var step = -1;

 function setSize(){
   var size = calculateSize();
   document.getElementById("plasma").style.width=""+size[0]+"px";
   document.getElementById("plasma").style.height=""+size[1]+"px";
   document.getElementById("marquee").style.top=""+Math.floor(size[1]/2)+"px";
   document.getElementById("marquee").style['font-size']=""+Math.floor(size[1]/6)+"px";
 }

 function calculateSize(){
   var vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
   var vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);

   var from_width = [vw, Math.floor(vw * SCREEN_Y / SCREEN_X)];
   var from_height = [Math.floor(vh * SCREEN_X / SCREEN_Y), vh];

   return from_width[0] < from_height[0]? from_width : from_height;
 }


 // Marquee

 function showText(text){
   var el = document.getElementById('text');
   el.textContent=text;
   el.style.animation = 'none';
   el.style['moz-animation'] = 'none';
   el.style['webkit-animation'] = 'none';
   el.style.animation = 'none';
   el.offsetHeight; /* trigger reflow */
   el.style.animation = null;
   el.style['moz-animation'] = null;
   el.style['webkit-animation'] = null;
 }


 function startTextScript(){
   var texts = [
     ['FHC presents...', '::: BYOD :::'],
     ['(c) n-n 2015-2020','Interactive Fiction'],
     ['Game + Walkthrough', 'An IFComp entry'],
     ['Intro:  Brutal', 'Music: E-Mantra'],
     ['Greetings to:', 'All scene groups'],
     ['Contact Us!', '(ESC to exit)'],
   ];
   var current_txt = 0;
   var total_ms = 25000; // just under avg plasma cycle
   var scheduled_at = performance.now();

   if (step < texts.length){
     schedule(null, total_ms, function(){
       if (!stopped && scheduled_at >= started){
         showText(texts[step][current_txt++]);
       } else {
         console.log("Outdated timer (started: "+started+", scheduled_at:"+scheduled_at);
       }
     }, total_ms / (texts[step].length + 1), true);
   }
 }

 // Audio

 var audio = new Audio('res/intro.mp3');

 function startAudio(){
   audio.play();
 }
 function stopAudio(){
   audio.pause();
   audio.currentTime = 0;
 }


 // Timer management

 function schedule(timeoutCallback, timeoutMs, stepCallback, timeoutStepMs){
   var init = performance.now();

   if (timeoutCallback) {
     setTimeout(timeoutCallback, timeoutMs);
   }
   if (!!stepCallback){
     var wrapper = function(){
           stepCallback();
           var now = performance.now();
           if (now < init + timeoutMs){
               var timer = setTimeout(wrapper, timeoutStepMs);
           } else {
               // console.log("Done after "+timeoutMs);
           }
       };
     setTimeout(wrapper, timeoutStepMs);
   }
 }

 function setup(element_id){
   window.addEventListener("resize", setSize);
   document.getElementById(element_id).innerHTML =
     '<div class="plasma" id="plasma">' +
       '<div class="marquee" id="marquee">' +
         '<div class="text" id="text"></div>' +
       '</div>' +
       '<canvas style="width:100%" id="canvas" width="160" height="100"></canvas>' +
     '</div>';
   return this;
 }
 return {
   setup: setup,
   setSize: setSize,
   start: start,
   stop: stop
 }

}
// vim: set ai expandtab tabstop=2 shiftwidth=2