/* Raul Parolari, March 2007  */

/*
    Raul Parolari, October 12 2007: 

    This is the drawing automata for AC::Helpers
*/

var schedule = {
   
  tasks: 
  [
    function() {
                 //create_canvas(schedule.id_holder, schedule.id_drawing);
	             tutor.reinitialize();
                 schedule.update_start_button_to_start();
                 schedule.interval();
	           },
	
// Speak 1: AC::Base is loaded
    function() {
	   tutor.speak();
	   schedule.interval();
    },   
  
     function() { schedule.delay(1.5 * pause.second) },

    // now draw (simultaneously) ActCntr::Base and its meta
    function() { 
                 var Ob = 'ActCntr::Base';
                 hsh_ds[Ob] = draw_class(Ob, TYPE_CLASS, ['process' ], null);
                 
                 var Ob = Ob + 'p';
                 hsh_ds[Ob] = draw_class(Ob, TYPE_META, ['process' ], null);
		   
		         schedule.interval();
    },

    // small pause before the connections
    function() { schedule.delay(0.5 * pause.second) },

    // connect ActCntr::Base to meta
    function() { 
	            issue_connect(CLASS_TO_META, 'ActCntr::Base', 'ActCntr::Basep',
                               schedule.interval); 
               },

    // small pause 
    function() { schedule.delay(0.2 * pause.second) },
// Speak 2: Base include Helpers
    function() {
	   tutor.speak();
	   schedule.interval();
    },

    function() { schedule.delay(1.5 * pause.second) },

     // draw module AC::Helpers
    function() {
	   // position AC::Helpers relative to ActCntr::Base
       var class_name = 'ActCntr::Base';   
       var dscr       = hsh_ds[class_name];
       var x_class    = parseInt(dscr.elem.style.left);
       var y_class    = parseInt(dscr.elem.style.top);

       var mod_name = 'AC::Helpers';
       var x_mod    = x_class + X_DELTA_MOD_FROM_CLASS;
       var y_mod    = y_class + Y_DELTA_MOD_FROM_CLASS;
       hsh_ds[mod_name] = draw_mini_module(mod_name, ['included'],
                                      x_mod, y_mod, schedule.interval);
     },
    
     // Pause: 
    function() { schedule.delay(1.5 * pause.second); }, 

    // draw Proxy for AC::Helpers
    function() {
       // first retrieve module descriptor
       var dscr_mod      = hsh_ds['AC::Helpers'];  // dscr for module
       var dscr_proxy    = draw_proxy_in_place_for_module(dscr_mod, null);
       hsh_ds[dscr_proxy.name] = dscr_proxy;
       schedule.next_task();
    },
      
    function() { schedule.delay(0.5 * pause.second); }, 

    // connect AC::Base to Proxy for module AC::Helpers
	function() { 
	   /* connect Class to Proxy module:
	      the connection works like old 'class to proxy', as the 'super' 
          pointer is set to the same y (in the class and in the mini proxy)
       */
       var class_name = 'ActCntr::Base';
       var proxy_name = 'AC::Helpers' + 'p'; 
	   issue_connect(CLASS_TO_PROXY, class_name, proxy_name, schedule.interval);
	},

	function() { schedule.delay(0.2 * pause.second); }, 
	
	// connect Proxy for AC::Helpers  --> Module
	function() { 								
	   var mod_name   = 'AC::Helpers';
	   var proxy_name = mod_name + 'p'; 
       issue_connect(MINI_PROXY_TO_MODULE, proxy_name, mod_name, schedule.interval);
	},
	
    // after drawing completion, brief pause before message
	function() { schedule.delay(0.1 * pause.second); }, 

// Speak 1c: Helpers has callback 'included'
    function() {
	   tutor.speak();
	   schedule.interval();
    },	
    // let user see msg
	function() { schedule.delay(2.4 * pause.second); }, 
		
// Speak 2.0, 2a: clear window +: included actions are: instantiates Anon module
    function() {
	   tutor.speak();
	   schedule.interval();
    },	
	function() { schedule.delay(0.3 * pause.second); },
    function() {
	   tutor.speak();
	   schedule.interval();
    },
	function() { schedule.delay(3 * pause.second); },
			
	// draw Anonymous module for ActCntr::Base and 'connect' it to mhm reference
    function() {
	   // position Anonym_B relative to ActCntr::Base
       
       var class_name = 'ActCntr::Base';   
       var dscr       = hsh_ds[class_name];
       var x_class    = parseInt(dscr.elem.style.left);
       var y_class    = parseInt(dscr.elem.style.top);

       var mod_name = 'Anon Module%Base';
       var x_mod    = x_class + X_ANON_MOD_TO_CLASS;
       var y_mod    = y_class + Y_DELTA_ANON_MOD_FROM_CLASS;
       hsh_ds[mod_name] = draw_mini_module(mod_name, [ ],
                                      x_mod, y_mod, schedule.interval);   
    },	
	
	function() { schedule.delay(2 * pause.second); }, 
	
	function() { 
		var class_name = 'ActCntr::Base';
		var dscr = hsh_ds[class_name];
		update_class_with_mhm(dscr, schedule.interval2); 
	},
	
	function() { 
		var class_name = 'ActCntr::Base';
		var mod_name = 'Anon Module%Base';
		issue_connect(CLASS_TO_MHM_MOD, class_name, mod_name, 
										schedule.interval2)
	},


	function() { schedule.delay(1.5 * pause.second); },
	
// Speak 2b: extend ClassMethods + inherited with helper
    function() {
	   tutor.speak();
	   schedule.interval();
    },	

    // Big Pause for user to read 3 lines about AC::ClassMethods
    function() { schedule.delay(3.5 * pause.second); },      
														

    // draw module AC::ClassMethods
    function() {
       // position module relative to ActCntr::Basep
       var class_name = 'ActCntr::Basep';   
       var dscr       = hsh_ds[class_name];
       var x_class    = parseInt(dscr.elem.style.left);
       var y_class    = parseInt(dscr.elem.style.top);

       var mod_name = 'ClassMethods';
       var x_mod    = x_class + X_DELTA_MOD_FROM_CLASS;
       var y_mod    = y_class + Y_DELTA_MOD_FROM_CLASS;
       hsh_ds[mod_name] = draw_mini_module(mod_name, ['helper', 'helper_method'],
                                      x_mod, y_mod, null);
       schedule.next_task();
    },

    // Pause
    function() { schedule.delay(1.5 * pause.second) },    
														// since last speak: 2sec
    // draw proxy for ActCntr::ClassMethods
    function() {
	   // retrieve module descriptor
       var dscr_mod      = hsh_ds['ClassMethods'];  // dscr for module
       var dscr_proxy    = draw_proxy_in_place_for_module(dscr_mod, null);
       hsh_ds[dscr_proxy.name] = dscr_proxy;
       schedule.next_task();
    },

    // Pause
    function() { schedule.delay(0.5 * pause.second) },
														// since last speak: 3 sec

    // connect meta  AC::Base' --> Proxy for AC::ClassMethods
	function() { 
	   /* connect Class to Proxy module:
	      the connection works like old 'class to proxy', as the 'super' 
          pointer is set to the same y (in the class and in the mini proxy)
       */
       var class_name = 'ActCntr::Basep';
       var proxy_name = 'ClassMethods' + 'p'; 
	   issue_connect(CLASS_TO_PROXY, class_name, proxy_name, 
									schedule.interval);
	},
	
    function() { schedule.delay(0.2 * pause.second) },

	// connect Proxy for AC::ClassMethods --> Module
	function() { 								
	   var mod_name = 'ClassMethods';
	   var proxy_name = mod_name + 'p';
	   issue_connect(MINI_PROXY_TO_MODULE, proxy_name, mod_name,
		                            schedule.interval);
	},
														
    // brief Pause
    function() { schedule.delay(0.2 * pause.second) },

    // draw method into MetaClass
    function() { 
	   var dscr = hsh_ds['ActCntr::Basep'];
	   update_class_with_inherited_w_h(dscr, schedule.interval);
	},

    function() { schedule.delay(1.5 * pause.second) },

// Speak 3.0, 3a: ApplicationController subclassed
    function() {
	   tutor.speak();
	   schedule.interval();
    },	
	function() { schedule.delay(0.4 * pause.second); },
    function() {
	   tutor.speak();
	   schedule.interval();
    },
	function() { schedule.delay(3 * pause.second); },
	
    // draw class ApplicationController
    function() {
       var Ob_cl = 'ApplController';
       hsh_ds[Ob_cl] = draw_class(Ob_cl, TYPE_CLASS, [ ], null);
      
       var Ob_mt = Ob_cl + 'p';
       hsh_ds[Ob_mt] = draw_class(Ob_mt, TYPE_META, [ ], schedule.interval);
    }, 

    function() { schedule.delay(0.5 * pause.second); }, 
    // connect class to meta 
    function() {
	   var Ob_cl = 'ApplController';
	   var Ob_mt = Ob_cl + 'p';
	
	   // simultaneous drawing vertical (class-meta) and horiz (class-next class)
	   issue_connect(CLASS_TO_META, Ob_cl, Ob_mt, schedule.interval);  

	   issue_connect(CLASS_TO_CLASS, 'ApplController', 'ActCntr::Base',
	                               null);
	   issue_connect(META_TO_META, 'ApplControllerp', 'ActCntr::Basep',
	                               schedule.interval);
	}, 

    // buffer to absorb the winner of the 2 timeouts
    function() { ; },

    // this is the second timeout: the drawing is completed: brief pause
	function() { schedule.delay(0.5 * pause.second); }, 								       

//  Speak (3b): load an Anonymous module (yet another one!)
	function() { 
		tutor.speak();  
		schedule.interval(); 
    }, 

    function() { schedule.delay(2 * pause.second); },
    

	// draw anonymous module for ApplicationController
    function() {
	   // position Anonym_B relative to ActCntr::Base

       var class_name = 'ApplController';   
       var dscr       = hsh_ds[class_name];
       var x_class    = parseInt(dscr.elem.style.left);
       var y_class    = parseInt(dscr.elem.style.top);

       var mod_name = 'Anon Module%Appl';
       var x_mod    = x_class + X_ANON_MOD_TO_CLASS;
       var y_mod    = y_class + Y_DELTA_ANON_MOD_FROM_CLASS;
       hsh_ds[mod_name] = draw_mini_module(mod_name, [ ],
                                      x_mod, y_mod, schedule.interval);
    },          

    function() { schedule.delay(1 * pause.second); },

	function() { 
		var class_name = 'ApplController';
		var dscr = hsh_ds[class_name];
		update_class_with_mhm(dscr, schedule.interval2); 
	},

    function() { 
	    var class_name = 'ApplController'; 
	    var mod_name   = 'Anon Module%Appl';
	    issue_connect(CLASS_TO_MHM_MOD, class_name, mod_name, schedule.interval); 
	},
	
    function() { schedule.delay(1 * pause.second); },

//  Speak 3c: makes this module include the previous (time wise) Anonym
	function() { 
		tutor.speak();  
		schedule.interval(); 	
    }, 

    function() { schedule.delay(2 * pause.second); },  

    /* Pause: Application controller tells its anon module to Include parent's anon
              2 Proxies (one per Anon mod) are needed: create them simultaneously
    */
    // draw proxy for mod 'Anon Module%Base'
    function() {
       // retrieve mod descriptor
       var dscr_mod      = hsh_ds['Anon Module%Base'];  // dscr for module
       var dscr_proxy    = draw_proxy_in_place_for_module(dscr_mod, null);
       hsh_ds[dscr_proxy.name] = dscr_proxy;
       //alert(hsh_ds[dscr_proxy.name].name);
       schedule.next_task();
    },

    // draw proxy for 'Anon Module%Appl'
    function() {
       // draw proxy for module ApplController
       var dscr_mod    = hsh_ds['Anon Module%Appl'];  // dscr for module
       var dscr_proxy  = draw_proxy_in_place_for_module(dscr_mod, null);
       hsh_ds[dscr_proxy.name] = dscr_proxy;
       //alert(hsh_ds[dscr_proxy.name].name);
       schedule.next_task();
    },

    function() { schedule.delay(0.5 * pause.second) },

	function() { 
	   var prx1 = 'Anon Module%Appl' + 'p';
	   var prx2 = 'Anon Module%Base'   + 'p';
	   issue_connect(PROXY_TO_PROXY, prx1, prx2, schedule.interval); 
    },

	// connect Proxy for  Anon Module%Appl  --> Module  simultaneously to
	// connect Proxy for  Anon Module%Base  --> Module
	function() { 
	   var mod_name   = 'Anon Module%Appl';								
	   var proxy_name =  mod_name + 'p'; 
       issue_connect(MINI_PROXY_TO_MODULE, proxy_name, mod_name, null);
		
	   var mod_name = 'Anon Module%Base';
	   var proxy_name = mod_name + 'p';
	   issue_connect(MINI_PROXY_TO_MODULE, proxy_name, mod_name,
		                            schedule.interval);
	},
	
//  Speak 3d: it also tells the Anonymous mod to include the ApplicationHelper
	function() { 
		tutor.speak();  
		schedule.interval(); 	
    }, 

// Pause: bring in ApplicationHelper

    function() { schedule.delay(2 * pause.second) },

    // first remove existing connections
    function() {
	   var left_box = 'Anon Module%Appl' + 'p';
       var right_box = 'Anon Module%Base'   + 'p';	  
	   remove_connections_between_given_boxes(left_box, right_box);
	   schedule.interval();
    },

    function() { schedule.delay(0.5 * pause.second) },

    //  draw module ApplicationHelper
   function() {
	  /* position ApplicationHelper relative to Anon Module%Appl */
      var ref_name  = 'Anon Module%Appl';   
      var dscr  = hsh_ds[ref_name];
      var x_ref = parseInt(dscr.elem.style.left);
      var y_ref = parseInt(dscr.elem.style.top);
                      
      var mod_name = 'ApplHelper';
      var x_mod    = x_ref + X_DELTA_MOD_FROM_MOD;
      var y_mod    = y_ref;							  
      hsh_ds[mod_name] = draw_mini_module(mod_name, ['logged_in?'],
                                     x_mod, y_mod, schedule.interval);
    }, 

    function() { schedule.delay(1.5 * pause.second) },
    
    // draw the proxy for ApplHelper
    function() {   
	   var dscr_mod    = hsh_ds['ApplHelper'];
       var dscr_proxy  = draw_proxy_in_place_for_module(dscr_mod, null);
       hsh_ds[dscr_proxy.name] = dscr_proxy;

       schedule.interval();
    },

    function() { schedule.delay(0.8 * pause.second) },

    // connect ApplHelper proxy -> module
    function() {
	   var mod_name   = 'ApplHelper';
	   var proxy_name = mod_name + 'p'; 
       issue_connect(MINI_PROXY_TO_MODULE, proxy_name, mod_name,
	                 schedule.interval2);	 
    },

    function() { schedule.delay(0.5 * pause.second) },

    function() { 
	   var prx1 = 'Anon Module%Appl' + 'p';
	   var prx2 = 'ApplHelper' + 'p';
	   issue_connect(PROXY_TO_PROXY, prx1, prx2, schedule.interval); 
	},
	
    function() { 
	   var prx1 = 'ApplHelper' + 'p';	
	   var prx2 = 'Anon Module%Base' + 'p';
	   issue_connect(PROXY_TO_PROXY, prx1, prx2, schedule.interval); 
    },
                                                
    // a bit of delay
    function() { schedule.delay(2 * pause.second); }, 

    function() { tutor.speak();
	             schedule.interval();
	},
	
    null    // last array entry must be without the (damned) comma
  ],

/*****************  End of demo  ******************/

  interval: function() {
    setTimeout(schedule.next_task, INTERVAL);
  },

  interval2: function() {
    setTimeout(schedule.next_task, 2 * INTERVAL);
  },

 delay: function(time) {
    setTimeout(schedule.next_task, time);
 },

 /*  status of the demo: running, suspended, resuming/starting, suspending
     at the beginning, it is 'suspended'
 */ 
 status: 'suspended',   

 /* demo transitions from 'suspended'  to 'resume'
                     from 'running'    to 'suspended'
 */
 transition: { 
               suspended :  { label: 'resume  demo', color: 'orange' },
               running :    { label: 'suspend demo', color: 'aqua'   }
             },

 task_index:  -1,

 next_task: function() {
    info = 'schedule.next_task: ';

    var startstop_el = schedule.startstop_el; 

    switch (schedule.status) {

       case 'suspending' :
          //alert(info + schedule.status);
          schedule.status = 'suspended';
          var transition  = schedule.transition[schedule.status];
          startstop_el.value = transition.label;
          startstop_el.style.backgroundColor =  transition.color;
          break;

       case 'starting': 
       case 'resuming': 
          //alert(info + schedule.status);
          schedule.status = 'running';
          var transition  = schedule.transition[schedule.status];
          startstop_el.value = transition.label;
          startstop_el.style.backgroundColor =  transition.color;
          break;

       case 'suspended':
          alert(info + schedule.status + ', should not be running!');
          break;

       case 'running': 
          break;
       default:
          alert(info + schedule.status + ', unknown status!');
       break;
    }

    if (schedule.status != 'running') { 
       //alert(info + ' blocking execution in status=' + schedule.status);
       return;
    }

    schedule.task_index++;

    // if task is null or end of array, we are done
    var next_method = schedule.tasks[schedule.task_index];
    if ((schedule.task_index < schedule.tasks.length ) && next_method) { 
       next_method();
    }
    else { 
       // END OF DEMO
       schedule.request_suspend();
       var elem   = document.getElementById('start_stop');
       elem.value = ' play demo ';
       schedule.startstop_el.style.backgroundColor = 'orange';
       schedule.task_index = -1;
    }
 }, // next_task()

 // element ids
 id_holder    : 'placeholder_1',  // element exists when drawing does not exist
 id_drawing   : 'id_drawing',     // (replaces id_holder)
 id_startstop : 'start_stop',      
 id_fastfwd   : 'fast_fwd',
 // element values
 holder_el    : null,
 startstop_el : null,               
 fastfwd_el   : null,               

 update_start_button_to_start: function() {
    schedule.startstop_el.value = 'suspend demo';
    schedule.startstop_el.style.backgroundColor = 'aqua'; 
 }, //  update_start_button_to_start

 /* 
    request_start: called from 'process_startstop_eventHdlr()', which is 
    called at user click (and at init, simulating user click)
 */
 request_start:  function() {
    var info = 'schedule.request_start: ';
    //alert(info);

    // tell schedule we are starting 
    schedule.status = 'starting';

    // change element shape (to tell user the status)
    var elem = schedule.startstop_el;
    elem.value = 'starting..'; 
    elem.style.backgroundColor = 'silver'; // transition color?

    schedule.delay(0);
 }, // request_start

 request_suspend: function() {
    var info = 'schedule.request_suspend: ';
    //alert(info);

    // tell schedule to stop
    schedule.status = 'suspending';

    // change element status
    var elem   = schedule.startstop_el;
    elem.value = 'suspending..'; 
    elem.style.backgroundColor = 'silver'; // transition color?

    // the scheduler will update button when current timer will hit
    //schedule.delay(0);  
 }, // request_suspend

 request_resume: function() {
    var info = 'schedule.request_resume: ';
    //alert(info);

    // tell schedule to restart
    schedule.status = 'resuming';

    // change element status
    var elem = schedule.startstop_el;
    elem.value = 'resuming..'; 
    elem.style.backgroundColor = 'silver'; // transition color?

    schedule.delay(300);
    return true;
 },

 toggle_fastfwd: function() {
    var elem = schedule.fastfwd_el;
    var info = 'fastfwd_el.onclick: ';
    elem.checked = ! elem.checked;
    this.process_fastfwd_request();
 },

 set_fastfwd: function() {
    var elem = schedule.fastfwd_el;
    var info = 'fastfwd_el.onclick: ';
    elem.checked = true;
    this.process_fastfwd_request();
 },

 reset_fastfwd: function() {
    var elem = schedule.fastfwd_el;
    var info = 'fastfwd_el.onclick: ';
    elem.checked = false;
    this.process_fastfwd_request();
 },

 process_fastfwd_request: function() {
    var elem = schedule.fastfwd_el;
    var info = 'fastfwd_el.onclick: ';
    if (elem.checked) {
       pause.set_fast(); 
       draw_fast();
    }
    else {
       pause.set_normal();
       draw_slow();
    }
    //elem.style.backgroundColor = 'black';
 },

 /* the state of machine "startstop" resides in the (visible) label
    initial value is 'play the demo'; thus, first time this method is called
    (at init), the demo starts

   add_app_handlers()           
            |                        
     init_demo()                 click 'play demo'
        - create_canvas               |
        -  --------->      process_startstop_eventHdlr()  
 									 |
                             - reinit_canvas --> reinits adm_coord_boxes
									   if (drawing && drawing.div)
                                                remove_children_of(drawing.div)
			                 - schedule.request_start
	
 */
 process_startstop_eventHdlr: function() {
    var info='process_startstop_event: ';
    var elem = schedule.startstop_el;
    //alert(info + 'entered');
    var pattern_suspend = /suspend/i;
    var pattern_resume  = /resume/i;
    var pattern_play    = /play/i;

    var label = elem.value;
    if ( label.match(pattern_suspend) ) {  
       schedule.request_suspend();
    }
    else if ( label.match(pattern_resume) ) {  
       schedule.request_resume(); 
    }
    else if ( label.match(pattern_play) ) { 
       // reinit drawing logic (administr boxes, remove_children of drawing.div)
       reinit_canvas(schedule.id_holder);
       // tutor is now reinitialized after create_canvas
       schedule.request_start();
    }
    else {  // user is clicking repeatedly too fast: ignore
       // alert(info + 'ignore click with transition pattern: ' + label); 
    }
 },  // process_startstop_eventHdlr

 init_demo: function() {
    var info = 'schedule.init_demo: ';

    // retrieve placeholder 
    schedule.elem_holder = document.getElementById(schedule.id_holder);
    // retrieve button, checkbox 
    schedule.startstop_el = document.getElementById(schedule.id_startstop);
    schedule.fastfwd_el   = document.getElementById(schedule.id_fastfwd);

    // ensure buttons are present
    var button_missing = '';
    button_missing += schedule.elem_holder  ? ''  : 'no placeholder! ';
    button_missing += schedule.startstop_el ? ''  : 'no startstop button! ';
    button_missing += schedule.fastfwd_el   ? ''  : 'no fastfwd button! ';
    if (button_missing.length != 0) {
       alert(button_missing + 'demo cannot proceed');
       return false;
    }

    schedule.fastfwd_el.onclick   = schedule.process_fastfwd_request;
    schedule.startstop_el.onclick = schedule.process_startstop_eventHdlr;

    //alert(info + "creating canvas");
    create_canvas(schedule.id_holder, schedule.id_drawing);
    // window.scrollTo(0, 130);

    // if user reloaded the page, ensure to process fast flag if set
    if (schedule.fastfwd_el.checked) {
       schedule.process_fastfwd_request();
    }

    /* on init, fake click of user; used to call schedule.request_start()
       but now we really simulate click of user, calling process_starstop())
    */
    schedule.process_startstop_eventHdlr();
 }, // init_demo

 end: null   // last entry gets no comma
};   // end schedule     
