/**
 *  @author : Adam Portilla
 *  
 *  @permissions : This is work in progress code, so please
 *                 do not use without permission.
 *
 *  @author-website :  http://blackasteroid.net
 */
 
/**
 *  Verify AP namespace
 *  
 */
if (typeof AP != 'object'){
    var AP = {};
}

/*
 *  Handle logging
 */
AP.log = function(blah){
/*
    if (typeof blah == 'string'){
        //document.getElementById('message').innerHTML +=  blah;
    } else {
        
    } 
    
    console.log(blah);
*/
};

/*
 *  Register early calls to the YUI dependant modules
 *  so that they can be fired off module by module
 *  when the needed YUI scripts are loaded
 */
AP.catcher = function(my){
    
    my = my || {};
    
    my.calls = {};
    
    return {
    
        register : function(p){
            my.calls[p.module] = my.calls[p.module] || [];
            my.calls[p.module].push(p);
        },
        
        fireModuleCalls : function(module){
            if (typeof my.calls[module] == 'object'){
                for (var i = 0; i < my.calls[module].length; i++) {
                    AP[module][my.calls[module][i].func](my.calls[module][i].params);
                }
            }
        }
    };
    
}();


/*
 *  Create the functions that can be called before the YUI script is loaded
 *  and have them register with the catcher so that they can be called once 
 *  the needed YUI scripts are loaded.
 */
AP.mediaplayer = {
    create : function(p){
        AP.catcher.register({
            module : 'mediaplayer',
            func   : 'create',
            params : p
        });
    }
};

AP.slider = {
    create : function(p){
        AP.catcher.register({
            module : 'slider',
            func   : 'create',
            params : p
        });
    }
};

AP.quicktime = {
    create : function(p){
        AP.catcher.register({
            module : 'quicktime',
            func   : 'create',
            params : p
        });
    }
};



/**
 *  Use YUI3 Autoloader
 */
YUI().use('node','slider',function(Y) {

    /*
     *  The AP.mediaplayer module enables the creation of a custom
     *  themed quicktime player using the AP.quicktime and
     *  AP.slider modules.  Creating the necesary sliders and 
     *  buttons and subscribing them to the quicktime events
     */
	AP.mediaplayer = function(MP){
		
		MP = MP || {};
		
		MP.customPlayer = function(p,my){

			var that = {};
			p = p || {};
			my = my || {};
				
			my.container = p.container || 'defaultContainer';
            my.movieUrl = p.movieUrl || '';
            my.movieWidth = p.movieWidth || 640;
            my.movieHeight = p.movieHeight || 360;
            my.progressWidth = p.progressWidth || 200;
            my.controlOrder = p.controlOrder || ['StepBackward','PlayPause','StepForward','Progress','Time'];
			
			my.containerNode = Y.get('#'+my.container);
			my.containerNode.addClass('apqt-container');
			my.movieNode = Y.get(document.createElement('div'));
			my.movieNode.addClass('apqt-movie');
			my.controllerNode = Y.get(document.createElement('div'));
			my.controllerNode.addClass('apqt-controller');
			my.containerNode.appendChild(my.movieNode);
			my.containerNode.appendChild(my.controllerNode);
            my.uniqueId = p.uniqueId || 'uniqueId'+parseInt(Math.random()*100000);
            my.movieId = p.movieId || 'apqt-'+parseInt(Math.random()*100000);
            my.movieNode.set('id', my.movieId);
            
            /*
             *  add the movie to the page immediatly upon object instantiation
             */
	        my.movieObj = AP.quicktime.create({
	            uniqueId : my.uniqueId,
                element : my.movieId,
	            url: my.movieUrl,
	            controller:'false',
	            height: my.movieHeight,
	            width: my.movieWidth,
	            bgcolor:'#000000'
	        });
	        
            /*
             *  provide functions for placing controls into the controllerNode
             */
	        my.addPlayPause = function(ADD){
	            ADD = {};
    			ADD.node = Y.get(document.createElement('div'));
    			ADD.node.addClass('apqt-playpause');
                ADD.btn = AP.button.create({
                	node           : ADD.node
                });
                ADD.btn.subscribeToPlayPause(my.movieObj);
                my.controllerNode.appendChild(ADD.node);
	        }; 

            my.addStepForward = function(ADD){
                ADD = {};
    			ADD.node = Y.get(document.createElement('div'));
    			ADD.node.addClass('apqt-stepforward');
                ADD.btn = AP.button.create({
                	node           : ADD.node
                });
                ADD.btn.setAction(function(){
        		    my.movieObj.Stop();
                    my.movieObj.Step(1);
                });
                my.controllerNode.appendChild(ADD.node);
            };

            my.addStepBackward = function(ADD){
                ADD = {};
    			ADD.node = Y.get(document.createElement('div'));
    			ADD.node.addClass('apqt-stepbackward');
                ADD.btn = AP.button.create({
                	node           : ADD.node
                });
                ADD.btn.setAction(function(){
        		    my.movieObj.Stop();
                    my.movieObj.Step(-1);
                });
                my.controllerNode.appendChild(ADD.node);
            };

	        my.addVolume = function(ADD){
                ADD = {};
    			ADD.node = Y.get(document.createElement('div'));
    			ADD.node.addClass('apqt-volume');
                my.controllerNode.appendChild(ADD.node);      
    	        ADD.slider = AP.slider.create({
    	        	node : ADD.node,
    	        	width: 50
    	        });
    	        ADD.slider.trackMovieVolume(my.movieObj);
	        };
	        
	        my.addTime = function(ADD){
                ADD = {};
    			ADD.node = Y.get(document.createElement('div'));
    			ADD.node.addClass('apqt-time');
                my.controllerNode.appendChild(ADD.node);
                my.movieObj.Events.on('playing',function(data){
                    ADD.node.setContent(data.stamp);
                });    
                my.movieObj.Events.on('timechanged',function(data){
                    ADD.node.setContent(data.stamp);
                });      
	        };

            my.addProgress = function(ADD){
                ADD = {};
    			ADD.node = Y.get(document.createElement('div'));
    			ADD.node.addClass('apqt-progress');
                my.controllerNode.appendChild(ADD.node);      
    	        ADD.slider = AP.slider.create({
    	        	node : ADD.node,
    	        	width: my.progressWidth
    	        });
    	        ADD.slider.trackMovieProgress(my.movieObj);
            };
            
            /*
             *  Loop through the control order, calling the add
             *  functions if they exist
             */
            for (var i = 0; i < my.controlOrder.length; i++) {
                if (typeof my['add'+my.controlOrder[i]] == 'function'){
                    my['add'+my.controlOrder[i]]();
                }
            }
       
	        return that;
		};
	
		return {
			create : function(p){
				return MP.customPlayer(p);
			}
		};
	}();
	
	AP.button = function(BT){
		
		BT = BT || {};
		
		BT.createButton = function(p,my){
			
			var that = {};
			my = my || {};
			p = p || {};
			
			my.node = p.node;
            my.innerNode = Y.get(document.createElement('div'));
            my.innerNode.addClass('apqt-button');
			
            my.node.appendChild(my.innerNode);
            
			my.state = 0;
			
			my.setStateClass = function(name){
                my.innerNode.removeClass('stateone').removeClass('statetwo').removeClass('stateoneclicked').removeClass('statetwoclicked');
                my.innerNode.addClass(name);
			};

            my.setStateOne = function(){
                my.state = 1;
                my.setStateClass('stateone');
            };
            
            my.setStateTwo = function(){
                my.state = 2;
                my.setStateClass('statetwo');
            };
            
            my.setStateOneClicked = function(){
                my.setStateClass('stateoneclicked');
            };
            
            my.setStateTwoClicked = function(){
                my.setStateClass('statetwoclicked');
            };
            
            my.fireActionOne = function(){}; 
            my.fireActionTwo = function(){};
			
			my.mousedown = function(){
                if (my.state == 1){
                    my.setStateOneClicked();
                } else {
                    my.setStateTwoClicked();
                }
			};
			
			my.click = function(){
                my.setStateOne();
                my.fireActionOne();
            };
			
			my.innerNode.on('mousedown',function(){my.mousedown();});
			
            my.innerNode.on('click',function(){my.click();});
            
            my.setStateOne();
            
			that.subscribeToPlayPause = function(movie){
                if (typeof movie == 'object'){
                    my.click = function(){
        				if(my.state == 1){
                            my.setStateTwo();
                            my.fireActionTwo();
        				} else {
                            my.setStateOne();
                            my.fireActionOne();
        				}
                    };
        			
        			my.fireActionOne = function(){
        				movie.Stop();
        			};
        			
        			my.fireActionTwo = function(){
                        movie.Play();
        			};
        			
        			movie.Events.on('play',function(){
                        my.setStateTwo();
        			});
        			
        			movie.Events.on('pause',function(){
                        my.setStateOne();
        			});
                }
			};

			that.setAction = function(func){
                if (typeof func == 'function'){
        			my.fireActionOne = func;
    			}
			};
			
			return that;
			
		};
		
		return {
			create : function(p){
				return BT.createButton(p);
			}
		};
	}();
	
	/*
	 *  create slider objects
	 */
    AP.slider = function(SL) {
        
        SL = SL || {};
        
        SL.generateSlider = function(p,my){
            
            var that = {};
            my = my || {};
            p = p || {};
            
            my.width = p.width || 480;
            my.movie = p.movie || 0;
            my.elementId = p.elementId || 'slidercontainer';
            my.node = p.node || Y.get('#'+my.elementId);
            my.node.addClass('apqt-slidercontainer');
            my.sliding = false;
            my.playing = false;

            my.sliderMarkup = '<div class="slider">'
                               +'<div class="track">'
                               +'<div class="rail"><div class="lcap"></div><div class="btile"></div><div class="rcap"></div>'
                               +'<div class="innerRails">'
                               +'<div class="innerRail"><div class="lcap"></div><div class="btile"></div><div class="rcap"></div>'
                               +'<div class="innerRail2"><div class="lcap"></div><div class="btile"></div></div>'
                               +'</div>'
                               +'</div>'
                               +'<div class="thumb"></div>'
                               +'</div>'
                               +'</div>'
                               +'</div>';
            
            my.node.setContent(my.sliderMarkup);
                       
            my.thumb = my.node.one('.slider .thumb');
            
            my.rlCap = my.node.one('.slider .rail > .lcap');
            my.rbTile = my.node.one('.slider .rail > .btile');
            my.rrCap = my.node.one('.slider .rail > .rcap');
            
            my.innerRail = my.node.one('.slider .innerRail');
            my.irlCap = my.node.one('.slider .innerRail > .lcap');
            my.irbTile = my.node.one('.slider .innerRail > .btile');
            my.irrCap = my.node.one('.slider .innerRail > .rcap');
            
            my.innerRail2 = my.node.one('.slider .innerRail2');
            //my.ir2lCap = my.node.one('.slider .innerRail2 > .lcap');
            //my.ir2bTile = my.node.one('.slider .innerRail2 > .btile');
                        
            my.slider = new Y.Slider({
                axis        : 'x',
                railSize    : my.width + 'px',
                boundingBox : my.node.one('.slider'),
                contentBox  : my.node.one('.slider .track'),
                rail        : my.node.one('.slider .rail'),
                thumb       : my.node.one('.slider .thumb'),
                min         : 0,
                max         : 1000,
                value       : 0,
                minGutter   : 0,
                maxGutter   : 0
            });

            my.syncInnerRailUI = function(){
                var railSize = parseInt(my.slider.get('railSize'));
                my.railRange = railSize - my.slider.get('minGutter') - my.slider.get('maxGutter');
                my.irbTile.setStyle('width',parseInt(my.railRange - 100) + 'px');
                my.irrCap.setStyle('left',parseInt(my.railRange - 50) + 'px');
    
                my.rbTile.setStyle('width',parseInt(railSize - 100) + 'px');
                my.rrCap.setStyle('left',parseInt(railSize - 50) + 'px');
            };            

            my.setInnerRail = function(value){
                var percent = value / my.slider.get('max');
                var railRange = parseInt(my.slider.get('railSize')) - my.slider.get('minGutter') - my.slider.get('maxGutter');
                var railWidth = Math.round(railRange * percent);
				my.innerRail.setStyles({
				    'width' : railWidth + 'px',
				    'left' : my.slider.get('minGutter') + 'px'
				});
            };
            
            my.setInnerRail2 = function(value){
                var percent = value / my.slider.get('max');
                var thumbWidth = my.thumb.get('offsetWidth');
                var railRange = parseInt(my.slider.get('railSize')) - my.slider.get('minGutter') - my.slider.get('maxGutter');
                var railWidth = Math.round((railRange * percent) - (thumbWidth * percent) + (thumbWidth / 2));
				my.innerRail2.setStyles({
				    'width' : railWidth + 'px'
				});
            };
            
            my.slideStart = function(){
            	my.sliding = true;
				my.setThumbClickedState();
            };
            
            my.slideChange = function(num){};
            
            my.slideEnd = function(){
            	my.sliding = false;
				my.setThumbUnclickedState();
            };

			my.setThumbClickedState = function(){
                my.thumb.addClass('thumbclicked');
			};
			
			my.setThumbUnclickedState = function(){
                my.thumb.removeClass('thumbclicked');
			};

			my.subscribeToProgressEvents = function(){
			
				if (typeof my.movie == 'object'){
						
		            my.slideStart = function(){
		                my.sliding = true;
		            	if (typeof my.movie == 'object'){
							my.movie.Stop();
		                }
		                my.setThumbClickedState();
		            };

		            my.slideChange = function(num){
						my.movie.setPercent(num / 1000);
                        my.setInnerRail2(num);
		            };

		            my.slideEnd = function(){
		            
		                my.sliding = false;
		                var curLoaded = my.movie.getLoadedPercent();
		                
		                /*
		                 *  The slider was moved beyond the loaded point
		                 */
		                if (curLoaded <= my.slider.getValue() / 1000) {
			                my.slider.syncUI();
			                var loadedValue = Math.round(curLoaded * 1000);
			                my.slider.setValue(loadedValue);
							my.setInnerRail(loadedValue);
		                    my.playing = false;
		                
		                /*
		                 *  The movie should be restarted
		                 */
		                } else if(my.playing && my.movie.getPercent() != 1){
		                    my.movie.Play();
		                    
		                /*
		                 *  The movie should not be restarted
		                 */
		                } else {
		                    my.playing = false;
		                }
		                
		                my.setThumbUnclickedState();
		            };
				
		            my.movie.Events.on("progress",function(t){
		            	my.setInnerRail(Math.round(t.percent * 1000));
		            });
		
		            my.movie.Events.on("load",function(t){
		            	my.setInnerRail(1000);
		            });
		
		            my.movie.Events.on("playing",function(t){
		                if (!my.sliding){
		                    my.slider.syncUI();
		                    var newValue = Math.round(t.percent * 1000);
		                    my.setInnerRail2(newValue);
		                    my.slider.setValue(newValue);
		                }
		            });
		            
		            my.movie.Events.on("timechanged",function(t){
		                if (!my.sliding){
		                    my.slider.syncUI();
		                    var newValue = Math.round(t.percent * 1000);
		                    my.setInnerRail2(newValue);
		                    my.slider.setValue(newValue);
		                }
		            });
		            
		            my.movie.Events.on("play",function(t){
		                if(!my.sliding){
		                    my.playing = true;
		                }
		            });
		
		            my.movie.Events.on("pause",function(t){
		                if(!my.sliding){
		                    my.playing = false;
		                }
		            });
		            
		            my.movie.Events.on("loadedmetadata",function(d){
		            	my.setInnerRail(Math.round(d.loaded.percent * 1000));
		            });
		            
		            my.movie.Events.on("ready",function(d){
		            	my.setInnerRail(Math.round(d.loaded.percent * 1000));
		            });
	            }
			};
			
			my.subscribeToVolumeEvents = function(){
			
				if (typeof my.movie == 'object'){
					
					my.slider.set('max',256);
						
		            my.slideStart = function(){
		                my.sliding = true;
		                my.setThumbClickedState();
		            };

		            my.slideChange = function(num){
		            	var newVolume = my.slider.getValue();
		            	my.movie.SetVolume(newVolume);
		            	my.setInnerRail(newVolume);
		            };

		            my.slideEnd = function(){
		                my.sliding = false;
                        my.slider.syncUI();
		            	var newVolume = my.slider.getValue();
		                var curVolume = my.movie.GetVolume();
		            	var newVolume = my.slider.getValue();
		            	my.movie.SetVolume(newVolume);
		                my.setThumbUnclickedState();
		            };
				
		            my.movie.Events.on("volumechange",function(v){
		            	if (!my.sliding){
                            my.slider.syncUI();
		            		my.slider.setValue(v.value);
		            		my.setInnerRail(v.value);
		            	}
		            });
		            
		            my.movie.Events.on("loadedmetadata",function(d){
                        my.slider.syncUI();
	            		my.slider.setValue(d.volume.value);
	            		my.setInnerRail(d.volume.value);
		            });
		            
		            my.movie.Events.on("ready",function(d){
                        my.slider.syncUI();
	            		my.slider.setValue(d.volume.value);
	            		my.setInnerRail(d.volume.value);
		            });
		            
	            }
			};

            my.thumb.on('mousedown',function(evt){
                if (!my.sliding){
                    my.slideStart();
                }
            },this);

            my.thumb.on('mouseup',function(evt){
                if (my.sliding){
                    my.slideEnd();
                }
            },this);
            
            my.slider.on('valueChange',function(evt){
                if (my.sliding && typeof my.movie == 'object'){
                	my.slideChange(evt.newVal);
                }
            },this);
            
            my.slider.on('slideStart',function(evt){
                if (!my.sliding){
                    my.slideStart();
                }
            },this);
            
            my.slider.on('slideEnd',function(evt){
                if (my.sliding){
                    my.slideEnd();
                }
            },this);
            
            my.syncInnerRailUI();
            my.slider.render();
		
            return {
            	trackMovieProgress : function(m){
            		my.movie = m;
            		my.subscribeToProgressEvents();
            	},
            	trackMovieVolume : function(m){
            		my.movie = m;
            		my.subscribeToVolumeEvents();
            	}
            }
            
        };

        return {
            create : function(p){
                return SL.generateSlider(p);
            }
        };
    }();

	/*
	 *  create quicktime objects
	 */
    AP.quicktime = function(QT) {
        
        /*
         *  Define the private internal namespace of this object
         */
        QT = QT || {};

        /*
         *  store created movies here so they can be referenced 
         *  via the AP.quicktime.getMovie function.
         */
        QT.movies = {};

        /*
         *  Get the markup for the quicktime embed, providing
         *  alternate markup for IE that is compatible with
         *  the QT dom js source interactions
         */
        QT.getMarkup = function(cfg){
            
            /*
             *  All qt configs
             */
            cfg.rand = parseInt(cfg.rand) || parseInt(Math.random()*10000);
            cfg.url = cfg.url || '';
            cfg.width = cfg.width || 640;
            cfg.height = cfg.height || 480;
            cfg.scale = cfg.scale || 'ASPECT';
            cfg.enablejavascript = cfg.enablejavascript || 'true';
            cfg.postdomevents = cfg.postdomevents || 'true';
            cfg.controller = cfg.controller || 'true';
            cfg.starttime = parseInt(cfg.starttime) || 0;
            cfg.autoplay = cfg.autoplay || 'false';
            cfg.bgcolor = cfg.bgcolor || '#000000';
            cfg.name = cfg.name || 'qtname' + cfg.rand;
            cfg.id = cfg.id || 'qtid' + cfg.rand;
            
            /*
             *  Adjust height if the controller is desired
             */
            if (cfg.controller != 'false'){
                if (!cfg.height.match('%')){
                    cfg.height = parseInt(cfg.height) + 16;
                }
            }
  
            /*
             *  use different markup for IE
             */
            if (QT.isIE()){
            
                return '<object style="position:absolute;margin-left:-4000px;" id="'+cfg.id+'_qt_event_source" classid="clsid:CB927D12-4FF7-4a9e-A169-56E4B8A75598"></object><object width="'+cfg.width+'" height="'+cfg.height+'" classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" id="'+cfg.id+'"  style="behavior:url(#'+cfg.id+'_qt_event_source);" ><param name="src" value="'+cfg.url+'"><param name="scale" value="'+cfg.scale+'"><param name="bgcolor" value="'+cfg.bgcolor+'"><param name="autoplay" value="'+cfg.autoplay+'"><param value="'+cfg.starttime+'" name="starttime"/><param name="enablejavascript" value="'+cfg.enablejavascript+'"><param name="postdomevents" value="'+cfg.postdomevents+'"><param name="controller" value="'+cfg.controller+'"></object>';

            } else {

                return '<embed src="'+cfg.url+'" id="'+cfg.id+'" width="'+cfg.width+'" height="'+cfg.height+'" bgcolor="'+cfg.bgcolor+'" autoplay="'+cfg.autoplay+'" starttime="'+cfg.starttime+'" controller="'+cfg.controller+'" postdomevents="'+cfg.postdomevents+'" enablejavascript="'+cfg.enablejavascript+'" scale="'+cfg.scale+'" name="'+cfg.name+'" pluginspage="http://www.apple.com/quicktime/download/"/>';
            }
            
        };
        
        /*
         *  Just a quick IE check utility
         */
        QT.isIE = function(){
            var ua = navigator.userAgent.toLowerCase();
            var msie = /msie/.test(ua) && !/opera/.test(ua);
            return msie;
        };

        /*
         *  Embed the appropriate qt embed markup into a node, configuring
         *  the qt based on provided params, or attributes of the node,
         *  or default values.
         */
        QT.createQtObj = function(node,p,my){
            
            var that = {};
            my = my || {};
            p = p || {};
            
            my.node = node || Y.get(document.createElement('div'));

            my.url = p.url || my.node.getAttribute('url') || '';

            /*
             *  first, check that a movie url is available...
             */
            if (my.url == ''){
            
                /*
                 *  if not, place an error message in the container and move on...
                 */
                my.node.setContent('<div class="apqt-error">No Quicktime Url Specified</div>');
                return that;
            }

            /*
             *  Grab all the config params, providing defaults.
             */
            my.width = p.width || my.node.getAttribute('width') || 640;
            my.height = p.height || my.node.getAttribute('height') || 360;
            my.bgcolor = p.bgcolor || my.node.getAttribute('bgcolor') || '#000000';
            my.controller = p.controller || my.node.getAttribute('controller') || 'true';
            my.autoplay = p.autoplay || my.node.getAttribute('autoplay') || 'true';
            my.uniqueId = p.uniqueId || 'uniqueId' + parseInt(Math.random()*10000);


            my.initializeMovie = function(){

                my.node.setStyles({
                    width: (parseInt(my.width) + 5) + 'px',
                    height: parseInt(my.height) + 'px'
                });

                /*
                 *  Place the quicktime markup into the container
                 */
                my.node.setContent(QT.getMarkup({
                    url : my.url,
                    id : my.uniqueId,
                    width : '100%',
                    height : '100%',
                    bgcolor : my.bgcolor,
                    controller : my.controller
                }));
                
                /*
                 *  Return a reference to the movie el (dom event source)
                 */
                my.movie = document.getElementById(my.uniqueId);
            };
            
            my.initializeMovie();

            
            /*
             *  Define Public Interface to Quicktime Object
             */
            that.Play               = function(){};
            that.Stop               = function(){};
            that.Rewind             = function(){};
            that.Step               = function(){};
            that.SetRate            = function(){};
            that.SetTime            = function(){};
            that.GetTime            = function(){};
            that.SetVolume          = function(){};
            that.GetVolume          = function(){};
            that.SetMute            = function(){};
            that.GetMute            = function(){};
            that.SetStartTime       = function(){};
            that.SetBgColor         = function(){};
            that.GetBgColor         = function(){};
            that.SetHREF            = function(){};
            that.GetHREF            = function(){};
            that.SetTarget          = function(){};
            that.GetTarget          = function(){};
            that.SetQTNEXTUrl       = function(){};
            that.GetQTNEXTUrl       = function(){};
            that.SetURL             = function(){};
            that.GetURL             = function(){};
            that.SetKioskMode       = function(){};
            that.GetKioskMode       = function(){};
            that.GetDuration        = function(){};
            that.GetMaxTimeLoaded   = function(){};
            that.GetTimeScale       = function(){};
            that.GetMovieSize       = function(){};
            that.GetMaxBytesLoaded  = function(){};
            that.GetMIMEType        = function(){};
            that.GetQuickTimeVersion = function(){};
            that.GetPluginStatus    = function(){};
            that.setPercent         = function(){};
            that.getPercent         = function(){};
            that.getLoadedTime      = function(){};
            that.getLoadedPercent   = function(){};


            /*
             *  Handle all the JS quicktime api interactions
             *  and make them a part of the public return
             *  object 'that'
             *
             *  @todo : figure out whether it's truly
             *          necesary to wrap the js api calls
             *          in a proxie function like this...
             *
             */
            my.assignJavascriptApi = function(){
                that.Play               = function(){my.movie.Play();};
                that.Stop               = function(){my.movie.Stop();};
                that.Rewind             = function(){my.movie.Rewind();};
                that.Step               = function(f){my.movie.Step(f);};
                that.SetRate            = function(r){my.movie.SetRate(r);};
                that.SetTime            = function(t){my.movie.SetTime(t);};
                that.GetTime            = function(){return my.movie.GetTime();};
                that.SetVolume          = function(v){my.movie.SetVolume(v);};
                that.GetVolume          = function(){return my.movie.GetVolume();};
                that.SetMute            = function(){my.movie.SetMute();};
                that.GetMute            = function(){return my.movie.GetMute();};
                that.SetStartTime       = function(t){my.movie.SetStartTime(t);};
                that.SetBgColor         = function(c){my.movie.SetBgColor(c);};
                that.GetBgColor         = function(){return my.movie.GetBgColor();};
                that.SetHREF            = function(url){my.movie.SetHREF(url);};
                that.GetHREF            = function(){return my.movie.GetHREF();};
                that.SetTarget          = function(t){my.movie.SetTarget(t);};
                that.GetTarget          = function(){return my.movie.GetTarget();};
                that.SetQTNEXTUrl       = function(url){my.movie.SetQTNEXTUrl(url);};
                that.GetQTNEXTUrl       = function(){return my.movie.GetQTNEXTUrl();};
                that.SetURL             = function(url){my.movie.SetURL(url);};
                that.GetURL             = function(){return my.movie.GetURL();};
                that.SetKioskMode       = function(m){my.movie.SetKioskMode(m);};
                that.GetKioskMode       = function(){return my.movie.GetKioskMode();};
                that.GetDuration        = function(){return my.movie.GetDuration();};
                that.GetMaxTimeLoaded   = function(){return my.movie.GetMaxTimeLoaded();};
                that.GetTimeScale       = function(){return my.movie.GetTimeScale();};
                that.GetMovieSize       = function(){return my.movie.GetMovieSize();};
                that.GetMaxBytesLoaded  = function(){return my.movie.GetMaxBytesLoaded();};
                that.GetMIMEType        = function(){return my.movie.GetMIMEType();};
                that.GetQuickTimeVersion = function(){return my.movie.GetQuickTimeVersion();};
                that.GetPluginStatus    = function(){return my.movie.GetPluginStatus();};
    
                /*
                 *  create some slightly more helpful
                 *  js calls to control quicktime
                 */
                that.setPercent = function(percent){
                    var newTime = percent * my.movie.GetDuration();
                    my.movie.SetTime(Math.floor(newTime));
                };
                
                that.getPercent = function(){
                    return my.movie.GetTime() / my.movie.GetDuration();
                };
                
                that.getLoadedTime = function(){
                    return my.getLoadedTime();
                };
    
                that.getLoadedPercent = function(){
                	try {
                        var t = my.movie.GetMaxTimeLoaded();
                        var d = my.movie.GetDuration();
                        var p = t / d;
                	} catch(err) {
                		var p = 0;
                	}
                    return p;
                };
            };
            


            /**
             *  Create the event publisher from which
             *  events may be fired and subscribed-to.
             *
             *  Uses YUI3 EventTarget
             */
            that.Events = {};
            Y.augment(that.Events, Y.EventTarget, null, null, {});


            /*
             *  Format Time
             *  returns a string representing the timecode format
             *  of the provided 'secs'
             */
            my.formatTime = function(secs,showDecimal){
            	// secs is the full number of seconds to be shown in timecode format
            	// showDecimal is a boolean value that determines whether or
            	// not the time is shown to the nearest hundredth of a second
        		secs += 0;
        		var timeStr = "";
        		var hours = Math.floor(secs / 3600);
        		secs -= hours * 3600 ; 
        		var minutes = Math.floor(secs / 60);
        		secs -= minutes * 60 ; 
        		var seconds = Math.floor(secs);
        		if (hours < 10){hours = "0" + hours;}
        		if (minutes < 10){minutes = "0" + minutes;}
        		if (seconds < 10){seconds = "0" + seconds;}
        		timeStr = hours + ":" + minutes + ":" + seconds;
        		if (showDecimal) {
        			var decimal = Math.floor((secs - seconds) * 100);
        			if (decimal < 10){decimal = "0" + decimal;}		
        			timeStr += "." + decimal;
        		}
        		return timeStr;	
            };

            /*
             *  return an object literal with the current
             *  time unit and the percentage complete
             *
             *  @todo : look into optimizing this function
             *          since duration and timescale are
             *          not changing during playback, so it
             *          may not be necesary to go back through
             *          the quicktime js api for each call 
             */
            my.getCurrentTime = function(){

                var t = my.movie.GetTime();
                var p = t / my.movie.GetDuration();
                var s = my.movie.GetTimeScale();
                
                return {
                    stamp : my.formatTime((t / s), false),
                    time : t,
                    percent : p
                };
            };
            
            /*
             *  return an object literal with the loaded
             *  time unit and the percentage loaded 
             */
            my.getLoadedTime = function(){
                try 
                {
                    var t = my.movie.GetMaxTimeLoaded();
                    var d = my.movie.GetDuration();
                    var p = t / d;
                }
                catch(err)
                {
                    var t = 0;
                    var p = 0;
                }
                return {
                    time : t,
                    percent : p
                };
            };
            
            /*
             *  return an object literal with the volume
             *  unit and the percentage volume 
             */
            my.getVolumeData = function(){
            
                try 
                {            
	                var v = my.movie.GetVolume();
	                var p = v / 256;
                }
                catch(err)
                {  
	                var v = 128;
	                var p = .5;
                }

                return {
                    value : v,
                    percent : p
                };
            };
            
            /*
             *  return an object literal with 
             *  the movie metadata
             */
            my.getMetaData = function(){

                var metadata = {
                    'duration' : my.movie.GetDuration(),
                    'loaded' : my.movie.GetMaxTimeLoaded(),
                    'time' : my.movie.GetTime(),
                    'timescale' : my.movie.GetTimeScale(),
                    'size' : my.movie.GetMovieSize()
                };
                    
                return metadata;
            };
            
            /*
             *  return an object literal with 
             *  the movies error data
             */
            my.getErrorData = function(){
                /*
                 *  @todo: return error data
                 */
                return {};
            };
            
            /*
             *  add a listener to the quicktime event publisher
             */
            my.addListener = function(evt, handler){
                if ( document.addEventListener ) {
                    my.movie.addEventListener(evt, handler, false); 
                } else { 
                    // IE 
                    my.movie.attachEvent('on' + evt, handler); 
                }
            };
            
            /*
             *  Check if Quicktime is ready for DOM event interaction
             *
             *  @note : an important function, since attempts to 
             *          interact via the qt js api before the plugin
             *          is ready will create js errors,  we must
             *          use a try/catch method and poll the player
             *          until it is ready
             */
            my.isDomReady = function(){
                try
                {
                    var pluginStatus = my.movie.GetPluginStatus();
                    
                    if (pluginStatus == 'Complete'
                        || pluginStatus == 'Playable'
                        || pluginStatus == 'Loading'){
                        AP.log('Dom Interactions are Available');
                        return true;
                    } else {
                        AP.log('Dom Interactions Not Available['+my.movie.GetPluginStatus()+']');
                        return false;
                    }
                }
                catch(err)
                {
                    AP.log('Dom Interactions Not Available[qt not ready]');
                    return false;
                }  
            };
            
            /*
             *  Keep track of the number of failed attempts to 
             *  connect to the qt dom events
             */
            my.domSetupFailCount = 0;


            /*
             *  Attempt to connect to the qt dom events
             */
            my.initializeDom = function(){

                AP.log('init dom check');
                
                if (my.isDomReady()){


                    my.assignJavascriptApi();

                    /*
                     *  jiggle fix
                     */
                    my.node.setStyles({
                        width: my.width + 'px'
                    });

                    if (QT.isIE()){
                        my.movie.SetResetPropertiesOnReload(true);
                        my.movie.SetURL(my.url);
                    }
                    
                    my.addListener("qt_loadedfirstframe", function(){
                        that.Events.fire("loadedfirstframe");
                    }); 
                    
                    my.addListener("qt_canplaythrough", function(){
                        that.Events.fire("canplaythrough",my.getCurrentTime());
                        if (my.autoplay){
                            my.movie.Play();
                        }
                    }); 
                    
                    my.addListener("qt_durationchange", function(){
                        that.Events.fire("durationchange",my.getCurrentTime());
                    }); 
                    
                    my.addListener("qt_error", function(){
                        that.Events.fire("error",my.getErrorData());
                    }); 
                    
                    my.addListener("qt_waiting", function(){
                        that.Events.fire("waiting");
                    }); 
                    
                    my.addListener("qt_stalled", function(){
                        that.Events.fire("stalled",my.getLoadedTime());
                    }); 
                    
                    my.addListener("qt_volumechange", function(){
                        that.Events.fire("volumechange",my.getVolumeData());
                    }); 
                    
                    my.addListener("qt_ended", function(){
                        that.Events.fire("ended",my.getCurrentTime());
                    }); 
                    
                    my.addListener("qt_progress", function(){
                        that.Events.fire("progress",my.getLoadedTime());
                    }); 
                    
                    my.addListener("qt_timechanged", function(){
                        that.Events.fire("timechanged",my.getCurrentTime());
                    }); 
                    
                    my.addListener("qt_load", function(){
                        that.Events.fire("load",my.getCurrentTime());
                    }); 
                    
                    my.addListener("qt_canplay", function(){
                        that.Events.fire("canplay",my.getCurrentTime());
                    }); 
                    
                    my.addListener("qt_loadedmetadata", function(){
                        that.Events.fire("loadedmetadata",{
                            'loaded' : my.getLoadedTime(),
							'volume' : my.getVolumeData()
                        });
                    });
                    
                    my.addListener("qt_play", function(){
                        that.Events.fire("play",my.getCurrentTime());
                        my.checking.start();
                    }); 
                    
                    my.addListener("qt_pause",  function(){
                        that.Events.fire("pause",my.getCurrentTime());
                        my.checking.stop();
                    });
                    
                    /*
                     *  if autoplay is true, and the movie is ready
                     *  to play, start playing...
                     */
                    if (my.autoplay && my.movie.GetPluginStatus() != 'Waiting'){
                        my.movie.Play();
                    } 

                    that.Events.fire("ready",{
                        'loaded' : my.getLoadedTime(),
                        'volume' : my.getVolumeData()
                    });


                } else {
                    /*
                     *  Quicktime isn't ready to have its dom events
                     *  subscribed to, so try again in a bit, but 
                     *  only up to 25 times, then give up because 
                     *  something is clearly wrong :(
                     */
                    my.domSetupFailCount++;
                    if (my.domSetupFailCount <= 25){
                        setTimeout(my.initializeDom, 500, this);
                    } else {
                        AP.log('quicktime dom setup has failed 25 times.  will not try again');
                    }
                }
            };


            /*
             *
             * checking
             * - refreshrate | the interval at which the movie will be polled while playing
             * - interval | the setInterval is assigned to this attribute
             * - action | the function called during each interval
             *            gets the time from the movie and triggers the
             *            progress bar update
             * - start | starts the interval
             * - stop | clears the interval
             *
             */ 
    		my.checking = {
    		    refreshrate : 250,
    			interval : {},
    			start : function(){
    				this.interval = setInterval(this.action, this.refreshrate, this);
    			},
    			stop : function(){
    				clearInterval(this.interval);
    			},
    			action : function(){
                    that.Events.fire("playing",my.getCurrentTime());
    			}
    		};


            /*
             *  Add the new movie object to the QT.movies object
             *
             *  @note:  QT.movies is an object literal,  but we use
             *          array notation to interact with it in order
             *          to assign a variable (my.uniqueId) as the 
             *          key to the movie object (that) within the 
             *          object literal
             */
            QT.movies[my.uniqueId] = that;

            /*
             *  Wait to initialize dom connection until the browser
             *  catches up with the newly inserted markup...
             *
             *  @todo:  see if we can remove this delay, since the try/catch
             *          'should' take care of any errors in the initialization
             *          attempt
             *
             */
            setTimeout(my.initializeDom, 100, this);
            
            that.changeUrl = function(newUrl){
                my.movie.Stop();
                my.url = newUrl;
                
                setTimeout(my.initializeMovie, 100, this);
                setTimeout(my.initializeDom, 200, this);
            };
            
            that.changeMedia = function(media){
            
                var newUrl = media.url || '';
                var newHeight = media.height || 480;
                var newWidth = media.width || 640;
                
                my.movie.Stop();
                my.url = newUrl;
                my.width = newWidth;
                my.height = newHeight;
                
                setTimeout(my.initializeMovie, 100, this);
                setTimeout(my.initializeDom, 200, this);
            };
            
            that.destroy = function(){
                my.checking.stop();
                p = {};
            };
            
            
            
            /*
             *  Return 'that' which contains the public interface of
             *  this object
             */
            return that;
        };
        
        return {

            /*
             *  Look for all document nodes with 
             *  the class .apqt and create a 
             *  quicktime embed for each
             */
            init : function(){

                var movieNodes = Y.all(".apqt");
                movieNodes.each(function(node){
                    QT.createQtObj(node);
                },this);
                
                return true;
            },
            
            /*
             *  Manually create and return a movie object
             */
            create : function(params){
            
                params = params || {};
            
                if (typeof params.element == 'string'){
                    var movieNode = Y.one('#'+params.element);
                } else if (typeof params.element.invoke == 'function'){
                    var movieNode = params.element;
                } else {
                    var movieNode = Y.one(params.element);
                }
                
                return QT.createQtObj(movieNode,params);
            },

            /*
             *  Get a specific movie from the movies that
             *  have been auto or manually created
             */
            getMovie : function(p){
                if(typeof QT.movies[p] == 'object'){
                    return QT.movies[p];
                } else {
                    return {};
                }
            }
            
        };
        
    }();

    /*
     *  Call the init function as soon as AP.quicktime is instantaited
     *
     *  @todo : check if the doc is loaded, and if not, add this to the load
     *          event...
     */
    //AP.quicktime.init();
    
    /*
     *  Use the catcher to fire off any qt calls made 
     *  before the YUI scripts were ready
     */
    AP.catcher.fireModuleCalls('mediaplayer');  
    AP.catcher.fireModuleCalls('slider');  
    AP.catcher.fireModuleCalls('quicktime');     
});
