function slidebox(slideconfig)
{
    this.updatebox = function()
    {
        switch(this.mode)
        {
            case "slide":
                this.leftnode.updatenode();
                break;
                
            case "beforegap":
                this.beforegap();
                break;
                
            case "makegap":                
                this.makegap();
                break;
                
            case "aftergap":
                // everything is stopped, so does nothing here
                break;
                
            case "closegap":
                this.closegap();
                break;
        }
//        livelog("l:"+this.leftnode.imagequeue.length+" r:"+ this.rightnode.imagequeue.length+
//            " mode="+this.mode +" lstep = "+this.leftnode.step+" rstep = "+this.rightnode.step );
    }
    
    this.beforegap = function()
    {
        // check to see if we can make a gap now
        if(this.direction=="right")
        {
            var ofirstimage = this.leftnode.imagequeue[0];
            var leftedge = pixeltoint(ofirstimage.style.left)+ofirstimage.clientWidth;
            if(leftedge>=this.gapleft)
            {
		// add an image to fill the left node
		this.leftnode.AddImageLeft();
            }

	    //split the queue
	    this.rightnode.imagequeue = this.leftnode.imagequeue.slice(1, this.leftnode.imagequeue.length);
	    this.leftnode.imagequeue.splice(1, this.leftnode.imagequeue.length-1);
	    // init node to gap mode
	    this.rightnode.step = this.leftnode.step;
	    this.rightnode.acceptnewimage = false;
	    this.rightnode.stepbuffer = 0;
	    this.leftnode.acceptnewimage = true;
	    this.mode = "makegap";
        }
        else if(this.direction=="left")
        {
            var olastimage = this.leftnode.imagequeue[this.leftnode.imagequeue.length-1];
            var rightedge = pixeltoint(olastimage.style.left)
            if(rightedge<=this.gapright)
            {
		// add an image to the right node
		this.leftnode.AddImageRight();
            }
	    // split the queue
	    this.rightnode.imagequeue = this.leftnode.imagequeue.slice(this.leftnode.imagequeue.length-1, this.leftnode.imagequeue.length);
	    this.leftnode.imagequeue.splice(this.leftnode.imagequeue.length-1, 1);
	    // init node to gap mode
	    this.rightnode.step = this.leftnode.step;
	    this.rightnode.acceptnewimage = true;
	    this.rightnode.stepbuffer = 0;
	    this.leftnode.acceptnewimage = false;
	    this.mode = "makegap";
        }
    }
    
    this.makegap = function()
    {
        // calculate step size for the next move
	var LeftEdge = this.GetLeftEdge();
	var NextLeftStep = this.CalculateNextDownStep(LeftEdge, this.gapleft, this.leftnode);
	var RightEdge = this.GetRightEdge();
	var NextRightStep = this.CalculateNextDownStep(RightEdge, this.gapright, this.rightnode);
	// set the value to the node
        this.leftnode.step = NextLeftStep;
	this.rightnode.step = NextRightStep;
            
        // move to the next mode as all node are stopped
        if(NextLeftStep==0&&NextRightStep==0)
        {
            // move to the aftergap mode
            // enable the event handling
            this.acceptmouseevent = true;
            this.mode = "aftergap";   
        } 
        else
        {
            // do the update
            this.leftnode.updatenode();
            this.rightnode.updatenode();     
            this.updatecontentpanel();   
        }
    }

    this.CalculateNextDownStep = function(CurrentPos, DestPos, Node){
	var NextStep = (DestPos - CurrentPos)/2;
	if(Math.abs(NextStep)<1&&Math.abs(NextStep)>0){
	    NextStep = DestPos - CurrentPos;
	    Node.ClearStepBuffer();
	}
	return NextStep;
    }
    
    this.CalculateNextUpStep = function(CurrentPos, DestPos, Node){
	var NextStep = Node.step==0?1:Node.step*2;
	NextStep = Math.abs(NextStep);
	// set the sign of the next step
	if(DestPos-CurrentPos<0){
	    NextStep = 0-NextStep;
	}
	//	log("Original step: "+NextStep + "CD: "+ CurrentPos  + " " + DestPos);
	// see if next step is too big
	if(Math.abs(NextStep)>Math.abs(DestPos - CurrentPos)){
	    NextStep = DestPos - CurrentPos;
	    Node.ClearStepBuffer();
	}
	return NextStep;
    }
    
    this.closegap = function()
    {
	// calculate step size for the next move
	var LeftEdge = this.GetLeftEdge();
	var RightEdge = this.GetRightEdge();
	var NextLeftStep = 0;
	var NextRightStep = 0;
	if(this.direction=="left"){
	    NextRightStep = this.CalculateNextUpStep(RightEdge, LeftEdge, this.rightnode);
	}
	else{
	    NextLeftStep = this.CalculateNextUpStep(LeftEdge, RightEdge, this.leftnode);
	}

	// set the value to the node
        this.leftnode.step = NextLeftStep;
	this.rightnode.step = NextRightStep;
	//	log("Left:" + LeftEdge+ " " + NextLeftStep+" Right: " + RightEdge + " " + NextRightStep);
        // move to the next mode as all node are stopped
        if(NextLeftStep==0&&NextRightStep==0)
        {
            //gap is closed move to the next mode
            // merge the queue
            this.leftnode.imagequeue = this.leftnode.imagequeue.concat(this.rightnode.imagequeue);
            this.rightnode.imagequeue = new Array();
   
            // set node status
            this.leftnode.acceptnewimage = true;
	    // set the step after closed
	    if(this.direction=="right"){
		this.leftnode.step = Math.abs(this.initialstep);
	    }
	    else{
		this.leftnode.step = 0 - Math.abs(this.initialstep);
	    }
            this.acceptmouseevent = true; 
            this.mode = "slide";
        }
        else
        {
	    // calculate next step
            // do the update
            this.leftnode.updatenode();
            this.rightnode.updatenode();      
            this.updatecontentpanel();    
        }
    }
    
    this.updatecontentpanel = function()
    {
	// have the content panel follow with the edge of left node
	var oleftlast = this.leftnode.imagequeue[this.leftnode.imagequeue.length-1];  
	var leftedge = pixeltoint(oleftlast.style.left) + oleftlast.clientWidth; 
	this.contentpanel.style.left = leftedge + "px";
    }
    
    this.GetLeftEdge = function(){
        var oleftlast = this.leftnode.imagequeue[this.leftnode.imagequeue.length-1];
        var leftedge = pixeltoint(oleftlast.style.left) + oleftlast.clientWidth;
	return leftedge;
    }

    this.GetRightEdge = function(){
        var orightfirst = this.rightnode.imagequeue[0];
        var rightedge = pixeltoint(orightfirst.style.left);
	return rightedge;
    }
    
    this.mousemovehandler = function(e)
    {
        if(!this.acceptmouseevent)
            return;            
        if(typeof(e)=="undefined"&&typeof(window.event)!="undefined")
        {
            e = window.event;
        }

        // adjust the speed for the mouse position   
        this.leftnode.step = Math.abs(this.speed)*2*(this.panelcenter - e.clientX)/this.clientWidth/this.fps;

        // sync the direction from step        
        if(this.leftnode.step>0)        
            this.direction = "right";
        else
            this.direction = "left";
    }    
   
    this.mouseclickhandler = function(e)
    {
        if(!this.acceptmouseevent)
            return;
        if(typeof(e)=="undefined"&&typeof(window.event)!="undefined")
        {
            e = window.event;
        }
                    
        // disable event handle
        if(this.mode=="slide")
        {
            this.acceptmouseevent = false;    
	    //	    this.leftnode.step = 10;
            this.mode = "beforegap";
        }
        else if(this.mode=="aftergap")
        {
            // move to the close mode
            this.acceptmouseevent = false;
            // get the speed for the mouse position   
            var currentspeed = Math.abs(this.speed)*2*(this.panelcenter - e.clientX)/this.clientWidth/this.fps;
            // sync the direction
            if(currentspeed>0)
            {
                this.direction = "right";
                // set the node status
                this.leftnode.step = currentspeed;
                this.leftnode.acceptnewimage = true;
                this.rightnode.acceptnewimage = false;
                this.rightnode.step = 0;
                
            }
            else
            {
                this.direction = "left";
                // set the node status
                this.rightnode.step = currentspeed;
                this.rightnode.acceptnewimage = true;
                this.leftnode.acceptnewimage = false;
                this.leftnode.step = 0;
            }
            this.mode = "closegap";
        }
    } 
    
    this.addimage = function(leftpos, edge, visible)
    {
        var oimage = document.createElement("img");
        oimage.style.visibility = visible;
        oimage.style.left = leftpos+"px";
        oimage.style.top = "0px";
        oimage.style.zIndex = 1;
        if(edge=="right")
        {
            oimage.src = this.imagestore.getrightimage();
        }
        else
        {
            oimage.src = this.imagestore.getleftimage();
        }
        oimage.style.position = "absolute";
	// link the onmouse click handler
	oimage.onclick = ImageClickGeneralHandler;
        this.container.appendChild(oimage);
        return oimage;
    }
        
    this.removeimage = function(image)
    {
        this.container.removeChild(image);    
    }    

    this.init = function(leftImage, rightImage, isfade, fadeduration)
    {
	// it's a new start
	if(leftImage==""){
	    var insertpos = 0;
	    do{
		var oimage = null;
		oimage = this.addimage(insertpos, "right", "");
		this.leftnode.imagequeue.push(oimage);   
		insertpos += oimage.clientWidth;
		// fix unkown error
		if(!this.fixerror(oimage)){
		    return;
		}
	    }while(insertpos<this.clientWidth);
	}
	else{
	    // we got parameter, so stop and step to aftergap mode
	    // 0. load image from parameter
	    if(fade && !this.imagestore.hasimage(leftImage)){
		var oleftfadeimage = new Element("img",{
			"styles":{
			    "left": "-5000px",
			    "top": "0px",
			    "position": "absolute",
			    "zIndex": 2
			},
			src: leftImage});
		oleftfadeimage.injectInside($(this.container));
		var leftimagepos = this.gapleft - oleftfadeimage.clientWidth;
		oleftfadeimage.setStyle("left", leftimagepos);
		var leftEffect = oleftfadeimage.effect("opacity", {
			duration: fadeduration,
			"onComplete":function(){
			    this.remove();
			}.bind(oleftfadeimage)
		    });
		leftEffect.start(1,0);
	    }
	    if(fade && !this.imagestore.hasimage(rightImage)){
		var orightfadeimage = new Element("img",{
			"styles":{
			    "left": this.gapright+"px",
			    "top": "0px",
			    "position": "absolute",
			    "zIndex": 2
			},
			src: rightImage});
		orightfadeimage.injectInside($(this.container));
		var rightEffect = orightfadeimage.effect("opacity", {
			duration: fadeduration,
			"onComplete":function(){
			    this.remove();
			}.bind(orightfadeimage)
		    });
		rightEffect.start(1,0);
	    }

	    // 1. restore left image
	    var oleftimage = this.addimage(-5000, "left", "");
	    this.leftnode.imagequeue.push(oleftimage);
	    // 2. fix possible error
	    if(!this.fixerror(oleftimage)){
		return;
	    }
	    var leftimagepos2 = this.gapleft - oleftimage.clientWidth;
	    $(oleftimage).setStyle("left", leftimagepos2);
	    // 3. restore right image
	    var orightimage = this.addimage(this.gapright, "right", "");
	    this.rightnode.imagequeue.push(orightimage);
	    // 4. restore content panel
	    this.updatecontentpanel();
	    // 5. restore status
	    this.mode = "aftergap";
	}
	this.contentpanel.style.visibility = "visible";
    }

    this.fixerror = function(oimage){
	if(oimage.clientWidth==0){
	    window.setTimeout(function(){window.location.href = window.location.href;},0);
	    return false;
	}
	else{
	    return true;
	}
    }
    
    this.start = function()
    {
        oslidebox.updatebox();
        window.setTimeout(oslidebox.start, 1/oslidebox.fps*1000);
    }

    this.CalcSlideBoxDimension = function(){
	// fix the container width for ie6
	if(window.ie6){
	    this.container.setStyle("width", "98.4%");
	    if(this.container.clientWidth<900){
		this.container.setStyle("width", 900);
	    }
	}
	this.clientWidth = this.container.clientWidth;
	// get the center of the container
	var obj = this.container;
	var containerleft = 0;
	do
	    {
		containerleft += obj.offsetLeft;
		obj = obj.offsetParent;    
	    }while(obj);
	this.panelcenter = containerleft + this.clientWidth/2;
    }
    
    // from config
    this.fps = slideconfig.fps;
    this.speed = slideconfig.speed;
    // computed property
    this.container = $(slideconfig.containerid);
    this.contentpanel = $(slideconfig.contentid);
    this.imagestore = new imageinventroy(slideconfig.imagelist);
    this.mode = "slide";

    // set the gap position
    this.gapleft = slideconfig.gapleft;
    this.gapright = this.gapleft + this.contentpanel.clientWidth;

    // set dimension for the slide box
    this.CalcSlideBoxDimension();
    window.onresize= function(){
	this.CalcSlideBoxDimension();
	}.bind(this);

    // set half of the maximun speed as initial step
    this.initialstep = slideconfig.speed/slideconfig.fps/2;
    this.leftnode = new slidenode(this.initialstep, this);
    this.rightnode = new slidenode(this.initialstep, this);
    
    // set the direction (calculate from initialstep)
    if(this.initialstep>0)
        this.direction = "right";
    else
        this.direction = "left";
    
    // link mouse move event
    this.container.onmousemove=function(event){oslidebox.mousemovehandler(event);};
    this.acceptmouseevent = true;
}

function slidenode(step, container)
{
    this.acceptnewimage = true;
    this.container = container;
    this.imagequeue = new Array(0);
    this.stepbuffer = 0;
    this.step = step;

    this.ClearStepBuffer = function(){
	this.stepbuffer = 0;
    }    

    this.dump = function(){
	if(this.container.mode=="aftergap"&&this.imagequeue.length>=1){
	    var src = this.imagequeue[0].src;
	    var pos = pixeltoint(this.imagequeue[0].style.left);
	    return {src:src, pos:pos};
	}
    }

    this.updatenode = function()
    {
        // no images, we are done
        if(this.imagequeue.length<=0)
            return;
            
        this.stepbuffer += this.step;
        var move = Math.floor(this.stepbuffer);
        // don't move if different less than 1pixel
        if(Math.abs(move)<1)
        {
            return;
        }
        this.stepbuffer -= move;
            
        // 1. compute the position for the next frame
        var i;
        for(i=0;i<this.imagequeue.length;++i)
        {
            this.imagequeue[i].style.left = pixeltoint(this.imagequeue[i].style.left) + move + "px";
        }
        // 2.fix node edge
        // left 
        var oimage;
        if(move>0){
            // new image to fill the left gap
            if(pixeltoint(this.imagequeue[0].style.left)>0){
		this.AddImageLeft();
            }
            // remove image out of scene
            if(pixeltoint(this.imagequeue[this.imagequeue.length-1].style.left)>this.container.clientWidth-1){
		
                oimage = this.imagequeue.pop();
                this.container.removeimage(oimage);
                delete oimage;
                oimage = null;
            }
	    // add image right
	    if(this.container.mode=="slide"){
		var rightedge = pixeltoint(this.imagequeue[this.imagequeue.length-1].style.left)+this.imagequeue[this.imagequeue.length-1].clientWidth;
		if(rightedge<=this.container.clientWidth-1){
		    this.AddImageRight();
		}
	    }
	}
        else{
	    // add image right
	    var rightedge = pixeltoint(this.imagequeue[this.imagequeue.length-1].style.left)+this.imagequeue[this.imagequeue.length-1].clientWidth;
	    if(rightedge<=this.container.clientWidth-1){
		this.AddImageRight();
	    }
	    
	    // remove on the left
	    if(pixeltoint(this.imagequeue[0].style.left)+this.imagequeue[0].clientWidth<=0){
		oimage = this.imagequeue.shift();
		this.container.removeimage(oimage);
		delete oimage;
		oimage = null;
	    }
	}
    }

    this.AddImageLeft = function(){
	// add on the left
	if(this.acceptnewimage){
	    oimage = this.container.addimage(0, "left", "hidden");
	    oimage.style.left = pixeltoint(this.imagequeue[0].style.left) - oimage.clientWidth + "px";
	    oimage.style.visibility = "";
	    this.imagequeue.unshift(oimage);
	}
    }

    this.AddImageRight = function(){
	//add on the right
	var rightedge = pixeltoint(this.imagequeue[this.imagequeue.length-1].style.left)+this.imagequeue[this.imagequeue.length-1].clientWidth;
	if(this.acceptnewimage){
	    oimage = this.container.addimage(rightedge, "right", "hidden");
	    oimage.style.visibility = "";
	    this.imagequeue.push(oimage);
	}           
    }
}

function ImageClickGeneralHandler(event)
{
    oslidebox.mouseclickhandler(event);
}

function imageinventroy(imagelist)
{
    this.imagelist = imagelist;
    this.leftindex = 0;
    this.rightindex = -1;
    
    this.getleftimage = function()
    {
        this.leftindex = (this.leftindex + imagelist.length -1) % imagelist.length;
        return this.imagelist[this.leftindex];
    }
    
    this.getrightimage = function()
    {
        this.rightindex = (this.rightindex + 1) % imagelist.length;
        return this.imagelist[this.rightindex];
    }
    
    this.hasimage = function(imagetofind){
//	for(i=0;i<imagelist.length;++i){
//	    if(imagetofind.contains(imagelist[i])){
//		return true;
//	    }
//	}
	return false;
    }
}

function pixeltoint(str)
{
    return parseInt(str.replace("px",""));
}

function GetFromUrl(key){
    var url = window.location.href;
    var myre = new RegExp(key+"=([^&]+)");
    var result = myre.exec(url);
    var value = "";
    if(result!=null){
	value = result[1];
    }
    return value;
}

function GoToPage(url){
    var oleftstatus = oslidebox.leftnode.dump();
    var orightstatus = oslidebox.rightnode.dump();
    var ls = oleftstatus.src;
    var rs = orightstatus.src;
    //var lp = oleftstatus.pos;
    //var rp = orightstatus.pos;
    var querystring = "/?lsrc="+ls+"&rsrc="+rs;
    window.location.href=url+querystring;
}

function preloadImages(imageArray)
{
    var cachedImages = new Array();
    for(var i=0; i<imageArray.length; ++i)
    {
        var imageToBeCached = new Image();
        imageToBeCached.src = imageArray[i];
        cachedImages.push(imageToBeCached);
    }
    return cachedImages;
}

var ologdiv = null;
var enableLog = true;
function log(str)
{
    if(!enableLog)
	return;
    if(ologdiv==null){
	ologdiv = document.createElement("div");
	ologdiv.style.position = "absolute";
	ologdiv.style.zindex = 10;
	document.body.appendChild(ologdiv);
    }
    ++i;
    ologdiv.innerHTML+=(i+": "+str+"<br/>");
}
i=0;
