/*
Class: ViewPort
	Creates a ViewPort. 
	 an assumtion is made that the scoller will never tell us to scroll past our viewable area.
	 if this is not true the viewport will seemingly become lost.
Note:
	The ViewPort requires an XHTML doctype.

Arguments:
	element - the knob container
	knob - the handle
	numberOfSteps - the number of steps
	options - see Options below

Options:
	mode - either 'horizontal' or 'vertical'. defaults to horizontal.
	offset - relative offset for knob position. default to 0.
	
Events:
	onChange - a function to fire when the value changes.
	onComplete - a function to fire when you're done dragging.
	onTick - optionally, you can alter the onTick behavior, for example displaying an effect of the knob moving to the desired position.
		Passes as parameter the new position.
*/
var ViewPort = new Class({
	
	options: {
		onComplete: Class.empty,
		onChange: Class.empty,
		defaultImage: Class.empty,
		defaultCaption: '',	
		imageServer : '',
		feedbackElement : Class.empty,

		scrollerOptionsH: {
			inUse: false,
			startingIndex: 1,
			pageRetrievalBuffer: 0,
			elementsPerPage: 0,
			elementName: "hpage",
			mode: "horizontal",
			increaseArrowName: "scrollRight",
			decreaseArrowName: "scrollLeft",
			totalElements: 0
		}
	},
	
	initialize: function(wrapper, viewport, options){
		this.wrapper = $(wrapper);
		this.viewport = $(viewport);
		this.currentRow = 0;
		this.currentColumn = 0;
		this.scrollerV = null;
		this.scrollerH = null;
		this.setOptions(options);
		
		this.fxScroll = new Fx.Scroll(wrapper, {
			wait: false,
			duration: 500,
			offset: {'x': -0, 'y': 0},
			transition: Fx.Transitions.linear
			//transition : viewport.effects({transition : Fx.Transitions.Quad.easeInOut}).chain(function () {this.start({'opacity': 0.3})})

		});
	
		this.itemRequest = new ItemRequest({elementsPerPage : this.options.scrollerOptionsH.elementsPerPage, 
											totalElements : this.options.scrollerOptionsH.totalElements});
		// add the event for the completed data retrieval
		this.itemRequest.addEvent('onComplete',function(event) {this.dataRetrievalComplete(event);}.bind(this));
		this.itemRequest.addEvent('onError',function(event) {this.dataRetrievalError(event);}.bind(this));
		
		// create the page cleaup object
		this.pagecleaner = new PageCleanUp(this,{interval : 1000, maxItems : 10});
		// add the event to handle the page deletion request.
		this.pagecleaner.addEvent('removeItem',function(event) {this.removePage(event);}.bind(this));
		
		// create the simple prediction object
		// this simple prediction will make requests 
		// directly to the itemRequest object
		this.prediction = new SimplePrediction(this, this.itemRequest,{interval : 500, 
		            maxElements : this.options.scrollerOptionsH.elementsPerPage,
		            elementThreshold : this.options.scrollerOptionsH.pageRetrievalBuffer,
		            elementsPerPage : this.options.scrollerOptionsH.elementsPerPage});
		

		if(this.options.scrollerOptionsH.inUse){
			
			this.scrollerH = new ViewPortScroller(
				{	//options
					increaseArrowName: this.options.scrollerOptionsH.increaseArrowName,
					decreaseArrowName: this.options.scrollerOptionsH.decreaseArrowName,
					onScrollHorizontal: function(step){
						this.moveHorizontal(step);
					}.bind(this)
				}
			);
			
		}		
	},
	

	loadPage : function(pageNumber){
		// try to load all the previous pages first
		for ( i = 1; i <= pageNumber; i++ ){
			if (( !LoadedItemManager.isLoaded(i) ) &&
			    ( i > 0 )){
			    this.startViewportChange();
				this.itemRequest.retrieveItem(i,100);
			}	
		}
	},
	selectItem : function (element){
		this.move(element.id);
		this.currentRow = parseInt(element.id.split('column')[1]);
		
	},
	moveHorizontal: function(direction) {
		//alert("hallo   " + step);
		if(direction > 0){
			this.currentRow = this.currentRow+4;
			this.move('row0column' + this.currentRow);
		}
		else if(direction < 0){
			this.currentRow = this.currentRow-4 > 0 ? this.currentRow - 4 : 1 ;
			this.move('row0column' + this.currentRow);
		}		
		
	    var requestedPage = parseInt((this.currentRow + 1 -1) / this.options.scrollerOptionsH.elementsPerPage) + 1;
        var pageIndex = ((requestedPage -1) * this.options.scrollerOptionsH.elementsPerPage) + this.currentRow;
        if (this.currentRow >= (this.options.scrollerOptionsH.totalElements - 3)) {
            this.currentRow = (this.options.scrollerOptionsH.totalElements - 3);
            var requestedPage = parseInt((this.currentRow + 1 -1) / this.options.scrollerOptionsH.elementsPerPage) + 1;
            var pageIndex = ((requestedPage -1) * this.options.scrollerOptionsH.elementsPerPage) + this.currentRow;
        }
		//find requested page
		//check if loaded, check if exceed total elements count
		if (( !LoadedItemManager.isLoaded(requestedPage) ) &&
		    ( requestedPage > 0 ) &&
		    ( (this.currentRow + 4) <= this.options.scrollerOptionsH.totalElements)){
		    this.startViewportChange();
		    this.itemRequest.retrieveItem(requestedPage,100);
		    this.currentRow = 1;
		}
		else if ((this.currentRow + 4) <= this.options.scrollerOptionsH.totalElements) {
			this.itemRequest.retrieveItem(requestedPage,100);
		}

	},
	
	move: function(elemName){
		if (($(elemName)) && (this.wait) == null)
			this.fxScroll.toElement(elemName);
	},
	resetView : function() {
		this.fxScroll.scrollTo(0,0);
	},
	
	dataRetrievalComplete: function(event) {
	    //console.log(event.response);
	    if (event.response != "") this.addPage(event.itemNumber,event.response);
	    
	    // if the current row is part of the page received then move to the current item
	    if (( this.currentRow >= (event.itemNumber * this.options.scrollerOptionsH.elementsPerPage)) &&
	        ( this.currentRow < ((event.itemNumber + 1) * this.options.scrollerOptionsH.elementsPerPage)))
	    {
	        this.move("row" + this.currentColumn + 'column' + this.currentRow);
	    }
	},
	dataRetrievalError: function(itemNumber, err) {
	    // need to inform the user that something has gone awry.
	    // create a div centered within the viewport.
	    // informing the user an error has occured retrieving the products
	    var errorDiv = new Element('div',{'id':'errorDiv'}).setHTML(err);
	    this.wrapper.injectTop(errorDiv);
	},
	
	// this function needs to take the html passed in and add it to the wrapper
	// this will be added to the top most section of the page.
	addPage: function (pageNumber, xml) {
		var xmlDoc = null;
		var imageToLoad = "";
		var captionForReplacement = "";
		var imageToDisplay = '';		

		var newPageName = this.options.scrollerOptionsH.elementName + pageNumber;
		var pageElement = new Element('div',{'id':newPageName}); //setHTML(html);
		var neighborPageNumber = LoadedItemManager.findItemNeighbor(pageNumber);
		var pageNumberDifference = pageNumber - neighborPageNumber;
		var neighborPage = null;
		
	    // if I connot find the page then insert it
	    if (! $(newPageName) ) {
		    //check if ie
			try {
		    if(window.ie){
			    xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
 			    xmlDoc.async="false";
  			    xmlDoc.loadXML(xml);
		    } else {
			    var parser = new DOMParser();
			    xmlDoc = parser.parseFromString(xml, "text/xml");
		    }
			} 
			catch (err) {
				alert (err);
			}
    		
		    var elements = new Array();
		    var images = xmlDoc.getElementsByTagName('userInput');	
    		
		    for( index = 0; index < images.length; index++ ) {
		        var uiid = images[index].getAttribute('id');
    		    
		        var attributeNodes = images[index].getElementsByTagName('attribute');

		        var captionForImage = 'Caption Not Found!';
		        for(i=0; i < attributeNodes.length; i++){
			        var code = attributeNodes[i].getAttribute('code');
        			
			        switch(code){
				        case 'photoCaption': 
					        captionForImage = attributeNodes[i].firstChild.firstChild.nodeValue;
					        break;
					    case 'thumb104URL': 
					        imageToDisplay = this.options.imageServer + attributeNodes[i].firstChild.firstChild.nodeValue;
				        case 'fileURL':
					        imageToLoad = this.options.imageServer + attributeNodes[i].firstChild.firstChild.nodeValue;
				        default:
					        //maybe default load failed caption and image
					        break;
			        }
		        }
		        var itemIndex = (index + 1) + ((pageNumber-1) * this.options.scrollerOptionsH.elementsPerPage);
		        var anchor = new Element('a',
    	            {id : 'row0column' + itemIndex,
    	             //class : 'grow',
    	             rel : uiid, 
	                 href : imageToLoad });
	            anchor.addClass('grow');
		        var image = Asset.image(imageToDisplay,
    	            {id : uiid,title : captionForImage }).injectInside(anchor);
	            var span = new Element('span').injectAfter(image);
            
                anchor.inject(pageElement);	
            }

            
			for ( i = neighborPageNumber; i >= 0; i--)
			{
			    if ($(this.options.scrollerOptionsH.elementName + (neighborPageNumber)))
			    {
			        neighborPage = $(this.options.scrollerOptionsH.elementName + (neighborPageNumber));
	                pageNumberDifference = pageNumber - i;
			        break;
			    }
			}
			
			if (neighborPage){
				if (pageNumberDifference == 0 ) {
				    pageElement.injectTop(this.viewport);
				} else if (pageNumberDifference > 0) {
					// inserting html after the last element of the neighborPage
					// get the last item number of the neighbor page
					//lastItemNumber = this.options.elementsPerPage * neighborPageNumber;
					pageElement.injectAfter(neighborPage);
				} else if( pageNumberDifference < 0 ) {
					// inserting html before the first element of the neighborPage
					// get the first of the neighbor page
					lastItemNumber = this.options.elementsPerPage * neighborPageNumber;
					pageElement.injectBefore(neighborPage);
				}  
		    	} 
		    
		    else {
		 		pageElement.inject(this.viewport);
		    }
    	}
   	    this.endViewportChange();
    	this.fireEvent('onDataLoad',pageElement);		
	},
	
	
	// this function will remove (this.options.ElementsPerPage ) elements 
	// from the top of the page.
	removePage: function(pageNumber) {
		$(this.elementName + pageNumber).remove();
	},

	
	// this function will set the viewport to a visual state to proide feedback for the user.
	// this should come from the options
	startViewportChange: function(step)	{
	    try
	    {
			if ( this.feedbackElement ) {
			if ( this.feedbackElement.hasClass('off') == true ) {
	            this.feedbackElement.toggleClass('off');
	        }
			}
	        if ( this.wait == null )
	        {
	            // lets create a div to put a waiting image into.
	            this.wait = new Element("div", {'id':'wait','class': 'processing'});
                this.wait.injectTop(document.body);
    	    }
		    var fx = new Fx.Style(this.viewport.id,'opacity');
		    fx.start(0.3);
		}
		catch (err)
		{
		
		}
	},
	
	// this function will set the viewport to a visual state to proide feedback for the user.
	// this should come from the options
	endViewportChange: function(step)	{
		if ( this.feedbackElement) {
	    if ( this.feedbackElement.hasClass('off') == false ) {
			this.feedbackElement.toggleClass('off');
	    }
		}
	    if ( this.wait ) 
	    {
	        this.wait.remove();
	        this.wait = null;
	    }
		var fx = new Fx.Style(this.viewport.id,'opacity');
		
	    fx.start(1);
	}
});

ViewPort.implement(new Events);
ViewPort.implement(new Options);

