// alert('anim.js reporting ..');

/* decimal 2 hexa conversion */
function decToHex(dec) {
  var hexStr = "0123456789ABCDEF";
  var low = dec % 16;
  var high = (dec - low)/16;
  hex = "" + hexStr.charAt(high) + hexStr.charAt(low);
  return hex;
}

/* vrati string realne cislo: x s poctom desatinnych miest: decs */
function getFloat(x, decs) {
  return (Math.round(x*Math.pow(10, decs)) / Math.pow(10,decs));
}

var T=1000;   // global sampling period: T [s]
/*********************************************************************************************/
/***********************   class Animation - animacia HTML elementu   ************************/
function Animation(element, handler, css_var, dest, xType, steps_freq) {

  /* konstanty */
  this.css_vars = new Array('pozadie','farba','border','pismena','sirka','vyska','left','top');
  
  /* vlastnosti */
  this.element = null;      // * HTML element
  this.handler = '';        // handler tejto animacie
  this.css_var = 1;         // * animovana CSS vlastnost, default: pozadie (1)
  this.var_count = 1;       // pocet animovanych premenych
  this.label = '';          // nazov css_var, len pre informaciu
  this.anim_type = 1;       // * typ animacie, default: linear-fixed-steps (1)
  this.x = new Array();     // hodnoty casovo zavislych premennych (CSS)
  this.step=0;              // aktualny krok (pre anim_type=1)
  this.delta = 0.1;         // prirastok kroku
  this.t = 0;               // realny cas beziacej Animacie
  this.freq = 2*Math.PI;    // frekvencia [rad/s]
  this.fade_t = 0.3;          // cas v [s] pre tlmenie exp(-fade_t)
  this.b = 0.7;               // konstanta tlmenia
  this.action = false;      // akcia bezi?
  
  /* metody */
  this.Init = Init;
  this.setInitial = setInitial;
  this.Set = Set;
  this.Start = Start;
  this.Stop = Stop;
  this.doStep = doStep;
  this.Refresh = Refresh;
  this.doAction = doAction;
  this.alertMe = alertMe;
  
  /* inicializacia */
  this.Init(element, handler, css_var, dest, xType, steps_freq);
  
  /* Init
     args: element, css_var, dest [, xType[,steps_freq]]
	   element -> HTML element
	   css_var -> animovana CSS vlatnost, pole (css_vars)
	   dest -> konecny stav
	   xType -> 1: linear 'steps' krokov, 2: sin(w*t) = sin(freq*ticks)
	   steps_freq -> pocet krokov, resp frekvencia [rad/s]
  */
  function Init(element, handler, css_var, dest, xType, steps_freq) { //alert('Animation.Init >>');
    this.Set('element', element);
	this.Set('handler', handler);
	this.Set('css_var', css_var);
	this.Set('anim_type', xType);
	if (this.css_var == 1 || this.css_var == 2 || this.css_var == 3) this.var_count=3; else this.var_count=1;
	for (var i=0; i<this.var_count; i++) {
	  this.x[i] = new Object();
	  if (!dest) this.x[i].to = 0;
	  else {
	    switch(this.css_var) {
		  case 1: case 2: case 3: this.x[i].to = parseInt(dest.substr(2*i,2),16); break;
		  default: this.x[i].to = dest; break;
		}
		if ( isNaN(this.x[i].to) ) this.x[i].to=0;
	  }
	}
	this.label = this.css_vars[this.css_var-1];
	switch(this.anim_type) {   // nastavenie kroku resp. frekvencie
	  case 1: if (steps_freq) this.delta = 1/steps_freq; break;
	  case 2: case 3: case 4: if (steps_freq) this.freq = steps_freq; break;
	}
	if (this.anim_type!=1) this.setInitial();
  }

  /* nastav pociatocny stav podla aktualnej hodnoty animovanej CSS vlastnosti elementu */
  function setInitial() { // alert('Animation.SetInitial >>');
	switch (this.css_var) {
	  case 1: var color = this.element.style.backgroundColor; break; 
	  case 2: var color = this.element.style.color; break;
	  case 3: var color = this.element.style.borderColor; break; 
	}
	if ( color && color.indexOf('rgb')!=-1 ) {
	  var mo=true;
	  color = color.substr(color.indexOf('(')+1);
	}
	else var mo=false;
	for (var i=0; i<this.var_count; i++) {
	  switch (this.css_var) {
	    case 1: case 2: case 3:
		  if (mo) {   // Mozilla
		    this.x[i].from = parseInt(color, 10);
			color = color.substr(color.indexOf(',')+1);
			//alert('color: ' + color + 'this.x['+i+'].from = ' + this.x[i].from);
		  }
		  else {   // IE
		    this.x[i].from = parseInt(color.substr(2*i+1,2), 16);
		  }
		  break;
		case 4: this.x[i].from = parseInt(this.element.style.letterSpacing, 10); break;
		case 5: this.x[i].from = parseInt(this.element.style.width, 10); break;
		case 6: this.x[i].from = parseInt(this.element.style.height, 10); break;
		case 7: this.x[i].from = parseInt(this.element.style.left, 10); break;
		case 8: this.x[i].from = parseInt(this.element.style.top, 10); break;
	  }
	  if ( isNaN(this.x[i].from) ) this.x[i].from=0;
	  this.x[i].span = this.x[i].to - this.x[i].from;
	}
  }
  
  /* nastavenie vlastnosti (property) na hodnotu (value) */
  function Set(property, value) {
    if (property && value) {
	  if (property=='element') this.element = value;
	  if (property=='handler') this.handler = value;
      if (property=='anim_type' && value>=1 && value<=4) this.anim_type=value;
	  if (property=='css_var' && value>=1 && value<=8) this.css_var=value;
	}
  }
  
  /* Animation.Start */
  function Start() { // alert('Animation.Start >>');
    if (this.element) {
	  this.action=true;
	  if (this.anim_type==1) this.setInitial();
	  this.step = 0;
	  if (this.anim_type==3 && this.fade_t>0.3/this.b) this.fade_t=0.3/this.b;
	}
  }
  
  /* Animation.Stop */
  function Stop() { // alert('Animation.Stop >>');
    if (this.element) this.action=false;
  }

  /* vypocet jedneho kroku zmeny aktualnych hodnot dynamickych premennych */
  function doStep() { // alert('doStep >> ');
  with (this) {
    if (anim_type==1) {
	  step += delta;
	  if (step<0) step=0;
	  if (step>=1) { step=1; Stop(); }
	}
	else {
	  t += T;
	  if (anim_type==3) { fade_t -= T; if (fade_t<0) fade_t=0; }
	  if (anim_type==4) { fade_t += T; if (fade_t>5/b) fade_t=5/b; }
	}
	
	// pre vsetky dynamicke premenne objektu: obj.x
	for (var i=0; i<x.length; i++) {
	  switch (anim_type) {
        case 1:
		  x[i].actual = x[i].from + step*x[i].span;   // ked step: 0->1 => actual: linearny prechod from->to
		  if ( (css_var==1 || css_var==2) && x[i].actual<0 ) x[i].actual=0;
		  if ( (css_var==1 || css_var==2) && x[i].actual>255 ) x[i].actual=255;
		  break;
		case 2: x[i].actual = x[i].span*(0.5*Math.sin((freq*t)-(Math.PI/2))+0.5) + x[i].from; break;
		case 3: case 4:
		  var amp = Math.exp(-b*fade_t);
		  x[i].actual = amp*x[i].span*Math.sin(freq*t) + x[i].from;
		  break;
	  }
	}		
  }}

  /* aktualizacia dynamickych CSS vlastnosti HTML elementu */
  function Refresh() {
    with (this) switch (css_var) {
	  case 1: case 2: case 3:
	    var color = '#' + decToHex(Math.round(x[0].actual)) + decToHex(Math.round(x[1].actual)) + decToHex(Math.round(x[2].actual));
		switch(css_var) {
		  case 1: element.style.backgroundColor = color; break;
		  case 2: element.style.color = color; break;
		  case 3: element.style.borderColor = color; break;
		}
		break;
	  case 4: element.style.letterSpacing = Math.round(x[0].actual) + 'pt'; break;
	  case 5: element.style.width = Math.round(x[0].actual) + 'px'; break;
	  case 6: element.style.height = Math.round(x[0].actual) + 'px'; break;
	  case 7: element.style.left = Math.round(x[0].actual) + 'px'; break;
	  case 8: element.style.top = Math.round(x[0].actual) + 'px'; break;
	}
  }

  /* akcia */
  function doAction() { //alert('animObj.doAction >>');
    if (this.action) {
      this.doStep();
	  this.Refresh();
	}
  }

  /* alertMe */
  function alertMe() {
    var txt = ' * Animation Information *\n-----------------------------------------\nHandler: '+ this.handler;
	txt += '\nHTML element ID: ' + this.element.id + '\nCSS property: ' + this.label + '\npocet premennych: ' + this.x.length;
	txt += '\nanimation type: ' + this.anim_type + '\nstep: ' + this.step + '\ndelta: ' + this.delta + '\nfreq: ' + getFloat(this.freq,3) + ' rad/s';
	txt += '\naction: ' + this.action + '\nCSS:\n';
	for (var i=0; i<this.x.length; i++) txt += '  ' + this.x[i].from + ' -> ' + this.x[i].to + '\n';
	alert(txt);
  }

}  // <- that was class Animation

/************************************************************************************************************/
/**************************   class Engine - Engine pre animovane HTML elementy   ***************************/
function Engine(instanceName, nT) {
  //alert('engine creation ..');
  /* vlastnosti */
  if (nT) this.T=nT; else this.T = 10;   // perioda vzorkovania realneho casu: T [ms] 
  T=this.T/1000;   // globalna prioda vzorkovania v sekundach
  
  //alert('engine timer setting ..\nthis.T = ' + this.T + '\ninstanceName = ' + instanceName);

  this.timer    = setInterval(instanceName+'.Tick()', this.T);   // casovac
  this.elementy = new Array();   // HTML elementy

  /* metody */
  this.addElement   = addElement;
  this.showElements = showElements;
  this.Tick         = Tick;
  this.startExp     = startExp;
  this.stopExp      = stopExp;

  /* pridaj HTML element do pola (elementy)*/
  function addElement(elem) { // alert('Engine.addElement >>\ntag: ' + elem.tagName);
	var i=0;
	while ( i < this.elementy.length && this.elementy[i] != elem ) i++;
	if ( i == this.elementy.length ) {
	  this.elementy[i] = elem;   // new element registration
	  elem.actions     = new Array();
	  elem.setCSS      = setCSS;
	  elem.addAction   = addAction;
	  elem.getAction   = getAction;
	  elem.doActions   = doActions;
	  elem.startAction = startAction;
	  elem.stopAction  = stopAction;
	  elem.showActions = showActions;
      
      elem.startExplode = startExplode;
      elem.stopExplode = stopExplode;
      elem.doExplodeStep = doExplodeStep;
      elem.exploding = false;
	}
  }
  
  /* Engine.showElements */
  function showElements() {
    var txt='registered elements:\n';
    for (var i=0; i<this.elementy.length; i++) txt += '  ['+i+'] -> ' + this.elementy[i].tagName + '#' + this.elementy[i].id + '\n';
	alert(txt);
  }

  /* The time is ticking away... every period (T) */
  function Tick() { // alert('the TICK  ..');
    for (var i=0; i<this.elementy.length; i++) {
	  this.elementy[i].doActions();
      if ( this.elementy[i].exploding ) this.elementy[i].doExplodeStep();
	}
  }

  /* start explosion */
  function startExp() {
    for (var i=0; i<this.elementy.length; i++) this.elementy[i].startExplode();
  }

  /* stop explosion */
  function stopExp() {
    for (var i=0; i<this.elementy.length; i++) this.elementy[i].stopExplode();
  }

/***********************   metody pre HTML Element   *************************/

  /* nastav CSS vlatnost pre (element) na hodnotu (value) */
  function setCSS(css, value) { // alert('setCSS >> ' + css+'->'+value);
    if (css && value) {
	  switch (css) {
	    case 'bck': this.style.backgroundColor = '#' + value; break;
		case 'col': this.style.color = '#' + value; break;
		case 'bor': this.style.borderColor = '#' + value; break;
		case 'let': this.style.letterSpacing = value + 'pt'; break;
		case 'wid': this.style.width = value +'px'; break;
		case 'hei': this.style.height = value + 'px'; break;
		case 'left': this.style.left = value + 'px'; break;
		case 'top': this.style.top = value +'px'; break;
	  }
	}
  }
  
  /* vrati referenciu na <Animation> objekt z pola (actions) podla (handler) a (css_var), alebo null ak taky neexistuje */
  function getAction(handler, css_var) { // alert('HTML  Element .getAction >>');
    for (var i=0; i<this.actions.length; i++)
	  if ( this.actions[i].handler == handler && this.actions[i].css_var == css_var ) return this.actions[i];
	return null;
  }

  /* ak neexistuje, pridaj novy objekt <Animation> s danym (handler) a (css_var) do pola actions, inak modifikuj existujuci */
  function addAction(handler, css_var, dest, xType, steps_freq) { // alert(this.tagName+'.addAction >>\nhandler: '+handler+'\ndest='+dest);
    var act = this.getAction(handler, css_var);
	if (!act) this.actions[this.actions.length] = new Animation(this, handler, css_var, dest, xType, steps_freq);
	else act.Init(this, handler, css_var, dest, xType, steps_freq);
  }

  /* HTML Element .doActions */
  function doActions() { // alert('HTML Element .doActions >>');
    for (var i=0; i<this.actions.length; i++) this.actions[i].doAction();
  }

  /* HTML Element .StartAction */
  function startAction(handler) { // alert(this.tagName+'#'+this.id+'.StartAction >>\nhandler: ' + handler);
    var i=0;
	with (this) {
	while ( i<actions.length && actions[i].handler!=handler ) i++;   // najdi v poli actions animaciu s danym (handler)
	if ( i<actions.length ) {   // ak sa nasla
	  var css_var = actions[i].css_var;   // zisti jej CSS vlastnost
	  for ( var j=0; j<actions.length; j++ )   // ak bezi ina akcia s touto CSS vlastnostou
	    if ( actions[j].css_var == css_var && actions[j].action) {
		  actions[i].t = actions[j].t;
		  actions[i].fade_t = actions[j].fade_t;
		  actions[j].Stop();   // zastav ju
		}
	  actions[i].Start();   // spusti animaciu s danym handlerom (handler)
	}}
  }

  /* HTML Element .showActions */
  function showActions() {
    var txt = 'Akcie ' + this.tagName + ' elementu s ID ' + ((this.id)?this.id:'none') +':\n';
    for (var i=0; i< this.actions.length; i++) {
	  txt += '\n  ' + i + ') Handler: ' + this.actions[i].handler + '\n      CSS: ' + this.actions[i].css_var + '\n';
	  txt += '     action: ' + this.actions[i].action + '\n';
	}
	alert(txt);
  }

  /* HTML Element .StopAction */
  function stopAction(No) { // alert('HTML Element .StopAction >>');
    //if (No>=1 && No<=this.actions.length) this.actions[No-1].Stop();
  }

  function startExplode() {
    this.style.position = 'relative';
    this.style.left = '0px';
    this.style.top = '0px';
    
    this.exploding = true;
    this.time = 0;           // time = 0.0 s
    this.v0 = Math.random()*15 + 5;             // v0 = <5,20> m/s
    this.alfa = Math.random()*Math.PI/2 + Math.PI/4;   // alfa = <0.25*pi,0.75*pi>
    this.g = 0.98;           // g = 0.98 m/s^2
  }
  
  function stopExplode() {
    this.exploding = false;
    this.style.left = '0px';
    this.style.top = '0px';
  }
  
  function doExplodeStep() {
  with (this) {
    var nx = v0*time*Math.cos(alfa);
    var ny = v0*time*Math.sin(alfa) - 0.5*g*time*time;
    if ( ny<-100) exploding=false;
    style.left = nx + 'px';
    style.top = -ny + 'px';
    time += 0.23;
    v0 += 0.01;
    }
    
  }
}   // <- class Engine
