/**
 * This script should be considered as a simple framework for coding flash-like
 * animations. 
 * This project based on the CSS standarts in order to able to get it work on
 * most browsers.
 * This Engine manages single motion or (multiple) group motions which I called 
 * animation in this context on a timeline. All animations are driven on the 
 * timeline. The basic idea is that making one step of any motion action
 * which is included and conducted by the engine at any particular moment. In the 
 * result of this approach, it gives the sense that each action is done concurrently.
 *   
 * Scenario Engine has classes. One is the main class that performs steps of 
 * given action for that moment. It consists of data structures in order to enable
 * keeping track of all actions. Thus, this class is also called a monitor of entire 
 * process. Beside Scenario class, Engine has action classes. They make possible motions
 * which could be carried out on html documents such as moving, resizing, dissappearing
 * and so forth. All classes have common methods and fields with same names. So as to
 * it to be called by the engine without need for recognizing type of the 
 * class which will be processed.
 *  
 * @author      Mehmet Dogan(memetdo@hotmail.com)
 * @version     2.71  08/05/2005  mm/dd/yyyy
 * @since       JS1.2, CSS 1.0/2.0
 *
 */
function Scenario()
{
    var o=this;    
    o.actives=new Array;
    o.runThread=null;		
    o.scT=0;
    o.max=500000; 
    o.createAction=function(arg)
    {  	   
       var obj=null;
       if(!o.argumentControl(arg)) return false;
       switch(arg.act)
       {
          case "Mv" : obj=new Mv(arg);
                      break;
          case "FRs": obj=new FRs(arg);
                      break;
          case "SRs": obj=new SRs(arg);
                      break;
          case "Tr" : obj=new Tr(arg);
                      break;
          case "Sc" : obj=new Sc(arg);
                      break;
       }
     
       o.updateActionStartTime(obj);
       o.addToActives(new Array(obj));
    }
		
    o.addToActives=function(arg)
    {  
       if(arg==null) return;
       var aLength=arg.length;
       for(var i=0;i<aLength;i++)
          o.actives.push(arg.pop());
       arg=null;		
    }	
			
    o.updateActionStartTime=function(obj)
    { 
		
		if((obj.arg.rp-1)==obj.rp){
       		obj.arg.sT=(obj.arg.sT+o.scT)%o.max+1;
		}
		else{
			obj.arg.sT=(o.scT)%o.max+1;				
		}
    }  	            

    o.startEngine=function()
    {
       oo=o;
       o.runThread=setInterval("oo.run()",1);		
    }
		
    o.stopEngine=function()
    {
       clearInterval(o.runThread);
    }
		
    o.run=function()
    {
       ++o.scT;
       for(var i=0;i<o.actives.length;i++)
       {
		  if(o.actives[i]==null) continue;
		  if(o.actives[i].arg.sT>o.scT) continue;
          if(!o.actives[i].isCompleted)
          {
             o.actives[i].run();
          }
          else
          {  
             if(o.actives[i].rp!=0)
             {
                if(o.actives[i].rp>0)
                	--o.actives[i].rp;                
                o.actives[i].init(false);
				o.updateActionStartTime(o.actives[i]);				
             }
             else
             {                 
                o.actives[i]=null;                
             }
          }
       }
       if(o.scT==o.max)
          o.scT=0;
    }
		
    o.argumentControl=function(arg)
    {       
       var errorString=""; 
       var count=0;
       if(arg==null) return false;       
       if(arg.gr!=undefined && o.registers[arg.gr]==undefined)   errorString+=++count+") The specified group name is not yet registered.\n";
       
       if(arg.act ==undefined) errorString+=++count+") Action Type has not been defined.\n      USAGE:       act:\"Mv\"\n";
       else if(arg.act!="Mv" && arg.act!="SRs" && arg.act!="FRs" && arg.act!="Tr" && arg.act!="Sc") errorString+=++count+") The only possible values for \"act\" means \"action\" are \"Mv\",\"SRs\",\"FRs\",\"Tr\",\"Sc\", .\n      USAGE:       act:\"Mv\"\n"; 
       
       if(arg.id  ==undefined) errorString+=++count+") Html Element ID has not been defined.\n      USAGE:       id:\"anExistingIdOfHtmlElement\"\n";        
       if(document.getElementById(arg.id)==null) errorString+=++count+") There is no element with specified ID on Html Document.\n";
       
       if(arg.sT  ==undefined) arg.sT=0; 
       else if(arg.sT<0) errorString+=++count+") Start Time must not be negative.\n      USAGE:       sT:aTimeInMiliseconds\n";
       
       if(arg.spT ==undefined) arg.spT=0;
       else if(arg.spT<0) errorString+=++count+") Stop Time must not be negative.\n      USAGE:       spT:aTimeInMiliseconds\n";
       
       if(arg.isR ==undefined) arg.isR=false;
       else if(typeof arg.isR!="boolean") errorString+=++count+") Is Reusable must be boolean.\n      USAGE:       isR:aBool\n";
       
       if(arg.rp  ==undefined) arg.rp=1;
       else if(arg.rp<-1 || arg.rp==0) errorString+=++count+") Repetition must not be zero or negative number less than -1 .\n      USAGE:       rp:aNuber\n";
       
       if(arg.aT  ==undefined) errorString+=++count+") Action Time has not been defined.\n      USAGE:       aT:aTimeInMiliseconds\n";
       else if(arg.aT<0) errorString+=++count+") Action Time must not be negative.\n      USAGE:       aT:aNuber\n";
       
       if(arg.bd  ==undefined) errorString+=++count+") Boundaries (such as X0,X1,H0,H1...) have not been defined.\n      USAGE:       bd:new Array(X0, Y0, X1, Y1)\n";
       else
       {
          if(arg.bd.length==4)
          {
             if(arg.bd[0]==arg.bd[2] && arg.bd[1]==arg.bd[3]) errorString+=++count+") Please, make sure that boundaries of the object are appropriate.\n      TIP:Do not use same values for all boundary numbers.\n";
          }	 
          else
          {
             if(arg.bd[0]==arg.bd[1]) errorString+=++count+") Please, make sure that boundaries of the object are appropriate.\n      TIP:Do not use same values for all boundary numbers.\n";
          }   
       }      
       if(arg.a==undefined) arg.a=0;
       if(arg.a!=-1 && arg.a!=0 && arg.a!=1) errorString+=++count+") The only possible values for \"a\" means \"acceleration\" are -1, 0, 1 .\n      USAGE:       a:1\n";       
       if(errorString!="")
       {
          var header="OBJECT ID: \""+arg.id+"\"  Following(s) are(is) missing or erroneous!\n-----------------------------------------------------------------------------------\n"+
                     "GENERAL USAGE:\nscenarioInstance.createAction\n( {\nact:\"aMotionName\", id:\"anID\", aT:act, bd:new Array(X0,Y0[,X1,Y1])\n[,gr:undefined] [,sT:0] [,spT:0] [,isR:false] [,rp:1] [,a:0]\n} )\n\n"+
                     "*[...] means that this part of argument list is optional.\n*Given values inside brackets are default values.\n-----------------------------------------------------------------------------------\n\nERRORS:\n\n";
          errorString=header+errorString;
          alert(errorString);
          return false;
       }               
       return true;      
    }
    o.startEngine();
}

function Mv(arg)
{   
    var o=this;
    o.arg=arg;
    o.DomObj=document.getElementById(o.arg.id);
		
    o.getNextLeft=function(curPos,nextDist)
    {  
       curPos+=Math.abs(nextDist)*o.cosConst;       
       return curPos;
    }
    o.getNextTop=function(curPos,nextDist)
    {         
       curPos+=Math.abs(nextDist)*o.sinConst;
       return curPos;
    } 		
    
    o.init=function(cnd)
    {
       if(cnd)
       {			    
          o.dist=Math.sqrt(Math.pow((arg.bd[0]-arg.bd[2]),2)+Math.pow((arg.bd[1]-arg.bd[3]),2));			
          o.toLeft=((arg.bd[2]-arg.bd[0])==0)?0:(((arg.bd[2]-arg.bd[0])<0)?-1:1);
          o.toTop=((arg.bd[3]-arg.bd[1])==0)?0:(((arg.bd[3]-arg.bd[1])<0)?-1:1);    
          o.sinConst=Math.abs((arg.bd[3]-arg.bd[1])/o.dist)*o.toTop;
          o.cosConst=Math.abs((arg.bd[2]-arg.bd[0])/o.dist)*o.toLeft;
          o.speedCal=new SpeedCalculator({dist:o.dist,arg:o.arg });
          o.rp=o.arg.rp-1;
       }else --o.rp;	
       o.DomObj.style.position="absolute";
       o.DomObj.style.left=o.arg.bd[0];
       o.DomObj.style.top=o.arg.bd[1];
       o.isCompleted=false;
       o.left=o.arg.bd[0];
       o.top=o.arg.bd[1];
       o.counter=0;
    }
    
    o.run=function()
    {
       if(o.counter==(o.arg.aT+o.arg.spT))
       {
          o.DomObj.style.left=o.arg.bd[2];
          o.DomObj.style.top=o.arg.bd[3];
          o.isCompleted=true;
       }
       if(!o.isCompleted)
       {
          if(o.counter<o.arg.aT)
          {  
             o.left=o.getNextLeft(o.left,o.speedCal.getNext(o.counter));						 
             o.top=o.getNextTop(o.top,o.speedCal.getNext(o.counter));						 					 
             o.DomObj.style.left=o.left;
             o.DomObj.style.top=o.top;
          }
       }
       ++o.counter;
    }
	
    o.init(true);
}

function FRs(arg)
{
    var o=this;
    o.arg=arg;
    o.DomObj=document.getElementById(o.arg.id);    
		
    o.init=function(cnd)
    {
       if(cnd)
       {				    	 
          o.dist=o.arg.bd[0]-o.arg.bd[1];
          o.dir=(o.dist==0)?0:((o.dist<0)?-1:1);
          o.speedCal=new SpeedCalculator({dist:o.dist,arg:o.arg});
          o.rp=o.arg.rp-1;					
       }else --o.rp;
       o.size=0;	 
       o.DomObj.style.fontSize=arg.bd[0];
       o.isCompleted=false;
       o.counter=0;		   
    }
		 
    o.run=function()
    {
       if(o.counter==(o.arg.aT+o.arg.spT))
       {           
          o.DomObj.style.fontSize=arg.bd[1];
          o.isCompleted=true;
          return;
       }
       if(!o.isCompleted)
       {
          if(o.counter<o.arg.aT)
          {
             o.size+=Math.abs(o.speedCal.getNext(o.counter));						 
             o.DomObj.style.fontSize=o.arg.bd[0]-o.size*o.dir;
          }	 
       }
       ++o.counter;
    }
    o.init(true);
}

function SRs(arg)
{
    var o=this;
    o.arg=arg;
    o.DomObj=document.getElementById(o.arg.id);   
	   
    o.init=function(cnd)
    {  
       if(cnd)
       {	
          o.DomObj.style.overflow="hidden";			    	 
          o.dist1=arg.bd[0]-arg.bd[2];
          o.dist2=arg.bd[1]-arg.bd[3];
          o.dir1=(o.dist1==0)?0:((o.dist1<0)?-1:1);
          o.dir2=(o.dist2==0)?0:((o.dist2<0)?-1:1);  
          o.speedCal1=new SpeedCalculator({dist:o.dist1,arg:o.arg});
          o.speedCal2=new SpeedCalculator({dist:o.dist2,arg:o.arg});
          o.rp=o.arg.rp-1;					
	   }else  --o.rp;
       o.height=0;
       o.width=0;
       o.isCompleted=false;
       o.counter=0;
       o.DomObj.style.height=arg.bd[0];
       o.DomObj.style.width=arg.bd[1];

    }

    o.run=function()
    {
       if(o.counter==(o.arg.aT+o.arg.spT))
       {
          o.DomObj.style.height=o.arg.bd[2];
          o.DomObj.style.width=o.arg.bd[3];          
          o.isCompleted=true;
          return;
       }
       if(!o.isCompleted)
       {
          if(o.counter<o.arg.aT)
          { 
             o.height+=Math.abs(o.speedCal1.getNext(o.counter));
             o.DomObj.style.height=o.arg.bd[0]-o.height*o.dir1;
             o.width+=Math.abs(o.speedCal2.getNext(o.counter));
             o.DomObj.style.width=o.arg.bd[1]-o.width*o.dir2;
          }   
       }
       ++o.counter;
    }
    o.init(true);
}

function Tr(arg)
{
    var o=this;
    o.arg=arg;
    o.DomObj=document.getElementById(o.arg.id);   

    o.init=function(cnd)
    {
       if(cnd)
       {		
          o.dist=o.arg.bd[0]-o.arg.bd[1];
          o.dir=(o.dist==0)?0:((o.dist<0)?-1:1); 
          o.speedCal=new SpeedCalculator({dist:o.dist,arg:o.arg});
          o.rp=o.arg.rp-1;
       }else --o.rp;
       o.DomObj.style.position="absolute";
       o.isCompleted=false;
       o.trp=0;
       o.counter=0;
       o.changeOpac(o.DomObj,o.arg.bd[0]);	   	   
    }
    o.changeOpac=function(obj,opacity)
    {
       obj.style.opacity=(opacity/100);
       obj.style.MozOpacity=(opacity/100);
       obj.style.KhtmlOpacity=(opacity/100);
       obj.style.filter="alpha(opacity="+opacity+")";
    }
    o.run=function()
    {
       if(o.counter>(o.arg.aT+o.arg.spT))
       {
          o.changeOpac(o.DomObj,o.arg.bd[1]);
          o.isCompleted=true;
          return;
       }
       if(!o.isCompleted)
       {
          if(o.counter<o.arg.aT)
          {		
             o.trp+=Math.abs(o.speedCal.getNext(o.counter))*o.dir;
             var op=o.arg.bd[0]-o.trp;           	 
             o.changeOpac(o.DomObj,op);		
          }	 
       }
       ++o.counter;
    }
    o.init(true);	
}

function Sc(arg)
{
    var o=this;    
    o.isCompleted=false;    
    o.event=arg.evt;
    o.init=function()
    {
       o.isCompleted=false;
    }
		
    o.run=function()
    {
       o.event();
       o.isCompleted=true;
       return;
    }
}

function SpeedCalculator(arg)
{
    var o=this;
    o.distance=Math.abs(arg.dist);
    o.actTime=arg.arg.aT;
    o.acc=arg.arg.a;
    o.constant1=o.distance/Math.pow(o.actTime,2);
    o.constant2=o.distance/o.actTime;
    o.getNext=function(timer)
    {
       if(o.acc>0)
          return o.constant1*(2*timer+1);
       else if(o.acc<0)
          return 2*o.constant2-o.constant1*(2*timer+1);
       else 
          return(o.constant2);
    }
}
