var gPrevEffect = Slide_From;
var gNextEffect = Slide_To;

var gThumbs = new Thumbnails();
var gMouse = new Mouse();
var gLastCat = false;

gBrowser.Init_OnLoad(function(){gThumbs.Init();});

function Thumbnails ()
{
    //  Class properties.
    this.element = 0;
    this.thumbsNav = 0;
    this.width = 0;
    this.displayWidth = 0;
    this.selected = false;
    //  Class methods.
    this.Init = Init;
    this.LoadThumbs = LoadThumbs;
    this.Select = Select;
    this.TrackMouse = TrackMouse;
    
    function Init ()
    {
        //  Fire up the "loading..." background image.
        SetLoadingBackground();
        //  Locate the thumbsnav wrapper div.
        //  For now, assume that there is exactly one of these in the DOM.
        this.thumbsNav = gBrowser.getElementsByClassName('thumbsnav', 'div', document.body)[0];
        //  Go through the thumbnails and attach an onclick event handler to them.
        var xThumbnails = gBrowser.getElementsByClassName('thumbnail', 'img', this.thumbsNav);
        for ( var xThumbnail = 0; xThumbnail < xThumbnails.length; xThumbnail++ )
        {
            globals[xThumbnails[xThumbnail].getAttribute('id')] = new Array;
            globals[xThumbnails[xThumbnail].getAttribute('id')].image_path = 'galleries/' + xThumbnails[xThumbnail].parentNode.id + '/' + xThumbnails[xThumbnail].getAttribute('id');
            xThumbnails[xThumbnail].onclick = Gallery_Next;
        }
        //  Do the same for the categories.
        var xCategories = gBrowser.getElementsByClassName('category', '', document.body);
        for ( var xCategory = 0; xCategory < xCategories.length; xCategory++ )
        {
            xCategories[xCategory].onclick = Load_Thumbnails;
        }
        //  Set the this.element variable to the first thumbnails div in the set.
        this.element = gBrowser.getElementsByClassName('thumbnails', 'div', this.thumbsNav)[0];
        with ( this )
        {
            //  Use the number specified by the developer in the CSS.
            displayWidth = parseInt(gBrowser.getElementStyle(this.thumbsNav, 'width'));
            //  "Actual" width.
            width = parseInt(element.parentNode.scrollWidth);
        }
        //  Now boot-strap LoadThumbs on that set of thumbnails.
        gBrowser.getElementsByClassName('category', 'img', document.body)[0].onclick();
        //  Start tracking mouse movements.
        //  I wish I didn't have to do it this way -- I would prefer to use mouseover on the
        //  element(s) I'm interested in. Unfortunately, EVERY SINGLE BROWSER handles
        //  mouseover/mouseout badly.
        //  I compromised by attaching a very simple, fast function to mousemove events,
        //  and then a more complex function that runs on a regular interval.
        gMouse.Start();
        setInterval("gThumbs.TrackMouse()", 15);
        return true;
    }
    
    function LoadThumbs ( pThumbsID )
    {
        //  This is a fun function! It repeatedly calls itself through a series
        //  of stages, because JavaScript concurrency is a horrible abomination!
        if ( pThumbsID )
        {
            //  Initial stage. Fade out the old set of thumbnails.
            var xOpacity = parseFloat(gBrowser.getElementOpacity(this.element));
            if ( xOpacity > 0.1 )
            {
                gBrowser.setElementOpacity(this.element, xOpacity - 0.08);
                setTimeout('gThumbs.LoadThumbs("' + pThumbsID + '")', 10);
            }
            else
            {
                //  Done fading out. Remove element...
                this.element.style['display'] = 'none';
                //  ...replace with next new one...
                this.element = document.getElementById(pThumbsID);
                //  ...set its initial transparency...
                gBrowser.setElementOpacity(this.element, 0);
                //  ...put it into position...
                this.element.style['display'] = 'block';
                //  ...set the new width and displayWidth variables...
                this.displayWidth = parseInt(gBrowser.getElementStyle(this.thumbsNav, 'width'));
                this.width = parseInt(this.element.parentNode.scrollWidth);
                //  ...some browsers (IE in particular) need to have the width set on the element...
                this.element.style.width = this.width + 'px';
                //  ...start loading the first image in the series...
                gBrowser.getElementsByClassName('thumbnail', 'img', this.element)[0].onclick();
                //  ...and call itself again. The pThumbsID parameter no longer matters.
                this.LoadThumbs();
            }
        }
        else
        {
            var xOpacity = parseFloat(gBrowser.getElementOpacity(this.element));
            if ( xOpacity < 1 )
            {
                gBrowser.setElementOpacity(this.element, xOpacity + 0.04);
                setTimeout('gThumbs.LoadThumbs()', 13);
            }
            else
            {
                //  Set up scrolling nav, if necessary.
                if ( this.width > this.displayWidth )
                {
                    //  Hard-set the thumbnails element's style-left value, or scrolling won't work in Safari.
                    this.element.style.left = '0px';
                }
            }
        }
    }
    
    function Select ( pThumbnailID )
    {
        var xSelected = gBrowser.getElementsByClassName('selected', 'img', this.element);
        for ( var x = 0; x < xSelected.length; x++ )
        {
            xSelected[x].setAttribute('class', 'thumbnail');
            //  Next line for IE; above line doesn't work for IE.
            xSelected[x].className = 'thumbnail';
        }
        xSelected = gBrowser.getElementByID(pThumbnailID, 'img', this.element);
        if ( xSelected != null )
        {
            xSelected.setAttribute('class', 'selected');
            //  See above; IE bug fix.
            xSelected.className = 'selected';
        }
    }

    function TrackMouse ()
    { 
        with ( this )
        {
            if ( ! width > displayWidth ) { return; }
            var xElementBounds = new Bounds(element.parentNode.offsetLeft - 15, element.parentNode.offsetLeft + displayWidth + 15, element.parentNode.offsetTop - 10, element.parentNode.offsetTop + element.parentNode.offsetHeight + 10);
            //  Mouse in thumbnails?
            if ( xElementBounds.CoordsInBounds(gMouse.x, gMouse.y) )
            {
                //  Let's try this scroll method instead:
                //  Thumbnails will automatically scroll left-right proportionally to
                //  the distance of the mouse point from the left edge. So, if the mouse pointer
                //  is at 65% of the distance from the left edge of the thumbnails (relative
                //  to the total display width of the thumbnails), then the thumbnails will scroll
                //  65% of their available scrollable distance.
                //  Hey, this works kinda nice!
                var xThumbs = element.getElementsByTagName('img');
                var xCurScroll = parseInt(element.style.left);
                var xNewScroll = 0;
                //  Shrink the display width a little.
                var xMousePos = (gMouse.x - parseInt(element.parentNode.offsetLeft) - parseInt(xThumbs[0].width) - 20) / (displayWidth - parseInt(xThumbs[0].width) - parseInt(xThumbs[xThumbs.length - 1].width) - 40);
                //  Normalize xMousePos.
                if ( xMousePos < 0 ) { xMousePos = 0; }
                if ( xMousePos > 1 ) { xMousePos = 1; }
                //  Adjust scroll.
                //  Following lines set up a kind of delay effect in the scrolling action. Helps smooth it out.
                xNewScroll = ((width - displayWidth) * xMousePos) * -1;
                if ( xNewScroll < (xCurScroll - 2) )
                {
                    element.style.left = (xCurScroll - 2) + 'px';
                }
                else if ( xNewScroll > (xCurScroll + 2) )
                {
                    element.style.left = (xCurScroll + 2) + 'px';
                }
                else
                {
                    element.style.left = xNewScroll + 'px';
                }
            }
        }
    }

}

function Load_Thumbnails ()
{
    if ( gLastCat )
    {
        gLastCat.setAttribute('class', 'category');
        gLastCat.className = 'category';
    }
    gLastCat = this;
    this.setAttribute('class', 'selected');
    //  Yet another Internet Explorer bug fix.
    this.className = 'selected';
    gThumbs.LoadThumbs(this.getAttribute('id'));
}

function SetLoadingBackground ()
{
    var xImages = document.getElementById('photo').getElementsByTagName('img');
    if ( xImages.length == 0 )
    {
        document.getElementById('photo').style.background = 'url("http://davidmckayphotography.com/production/loading.gif") no-repeat center center';
    }
    else
    {
        document.getElementById('photo').style.background = 'url("none") no-repeat center center';
    }
}

function Next_Init ( pImage )
{
    if ( this.vars )
    {
        //  Start with image off the right of the screen.
        this.vars['element'].style.left = screen.width + 'px';
        //  Attach the element to the 'photo' div.
        //  Some browsers won't return any properties for the element until you do.
        document.getElementById('photo').appendChild(this.vars['element']);
        //  Some browsers (Safari) need the width to be set manually.
        this.vars['element'].style.width = this.vars['element'].width + 'px';
        //  Reset the "loading..." background image.
        SetLoadingBackground();
        //  Launch the appropriate image effect.
        var xNextEffect = GetTask(gNextEffect(this.vars['element'], Point(((parseInt(gBrowser.getElementStyle(this.vars['element'].parentNode, 'width')) - parseInt(this.vars['element'].style.width)) / 2), parseInt(this.vars['element'].style.top))));
        if ( xNextEffect != null )
        {
            xNextEffect.SetName('Next_Image');
        }
        //  Kill any prev effects, if any are running.
        var xPrevEffect = GetNamedTask('Prev_Image');
        while ( xPrevEffect != null )
        {
            xPrevEffect.Die();
            xPrevEffect = GetNamedTask('Prev_Image');
        }
        //  Make double-extra-super sure that the image area is clear.
        //  Given the code in the Gallery_Next() function, you might think that
        //  this loop was redundant.
        //  You would be wrong! I have tested it. Amazingly, under a high-stress
        //  load, it's possible for an image to slip through all of the kills in
        //  the Gallery_Next() function, and then hang around.
        var xImages = document.getElementById('photo').getElementsByTagName('img');
        for ( var x = 0; x < xImages.length; x++ )
        {
            if ( xImages[x] != this.vars['element'] )
            {
                Prev_Cleanup(xImages[x]);
            }
        }
    }
    else if ( pImage )
    {
        //  Set up a delayed call to this to give Prev_Effect a head start.
        var xTask = new TimeTask(Next_Init);
        xTask.SetVar('element', pImage);
        setTimeout("RunTask(" + xTask.id + ")", 300);
    }
}

function LoadImage ( pImagePath, pCallback )
{
    if ( pImagePath && pCallback )
    {
        var xImageTask = new TimeTask(LoadImage);
        var xImage = document.createElement('img');         //  Can't use "new Image()", Safari will choke on it later.
        xImage.src = pImagePath;
        xImageTask.SetVar('image', xImage);
        xImageTask.SetVar('callback', pCallback);
        xImageTask.SetVar('count', 0);
        xImageTask.SetName('LoadImage');
        xImageTask.SetCleanup(LoadImage);
        xImageTask.Run();
    }
    else if ( this.vars )
    {
        if ( this.dying )
        {
            this.vars['callback'](this.vars['image']);
        }
        else
        {
        	this.vars['count'] += 1;
            if ( ! this.vars['image'].width )
            {
            	if ( ! document.all && (this.vars['count'] > 1) )
            	{
            		//	Try attaching the image to the document tree?
            		//	Internet Explorer will mysteriously shrink the image down
            		//	to 28x28 if the image is attached to an element at any point in its
            		//	load process. *sigh*
					//	Some browsers (ahem, Safari) do an annoying job of never supplying any
					//	information on properties for an object unless it's been attached to the document
					//	tree. Sooooo .... LoadImage now has its very own hidden element to pre-attach images.
					var xMagic = document.getElementById('_LoadImageMagic');
					if ( ! xMagic )
					{
						xMagic = document.createElement('div');
						xMagic.setAttribute('id', '_LoadImageMagic');
						//	Can't set display to 'none' or Safari 2 will still refuse to set properties for dynamically loaded images.
						gBrowser.setElementStyle(xMagic, 'width', '0px');
						gBrowser.setElementStyle(xMagic, 'height', '0px');
						gBrowser.setElementStyle(xMagic, 'overflow', 'hidden');
						//	Try moving it offscreen instead.
						gBrowser.setElementStyle(xMagic, 'left', '-5000px');
						document.body.appendChild(xMagic);
					}
					xMagic.appendChild(this.vars['image']);
            	}
                return 100;
            }
        }
    }
}

function Prev_Cleanup ( pElement )
{
    if ( pElement.parentNode ) { pElement.parentNode.removeChild(pElement); }
    if ( ! document.all ) { delete pElement; }
}

function Gallery_Next ()
//  The main function called whenever the next image needs to be displayed.
{
    //  If there are any images waiting to be loaded, kill them immediately.
    var xImageLoaders = GetNamedTask('LoadImage');
    while ( xImageLoaders != null )
    {
        xImageLoaders.Die();
        xImageLoaders = GetNamedTask('LoadImage');
    }
    //  If an image is still being slid into the display area, kill that too.
    var xNextEffects = GetNamedTask('Next_Image');
    while ( xNextEffects != null )
    {
        xNextEffects.Die();
        xNextEffects = GetNamedTask('Next_Image');
    }
    //  If an image is still being slid out of the display area ... eh, kill it.
    var xPrevEffect = GetNamedTask('Prev_Image');
    while ( xPrevEffect != null )
    {
        xPrevEffect.Die();
        xPrevEffect = GetNamedTask('Prev_Image');
    }
    //  If there happen to be any extra images hanging out in the display area
    //  (there really shouldn't be), kill them too.
    //  My, aren't we the murderous bastards?
    var xImages = document.getElementById('photo').getElementsByTagName('img');
    while ( xImages.length > 1 )
    {
        Prev_Cleanup(xImages[0]);
        xImages = document.getElementById('photo').getElementsByTagName('img');
    }
    //  If there is an image left to be moved out, then move it out.
    if ( xImages.length > 0 )
    {
        xPrevEffect = GetTask(gPrevEffect(xImages[0], Point((parseInt(xImages[0].style.width) + 20) * -1, parseInt(xImages[0].style.top)), Prev_Cleanup));
        if ( xPrevEffect != null )
        {
            xPrevEffect.SetName('Prev_Image');
        }
    }    
    //  Now, select the appropriate thumbnail for the user while the image is downloading.
    gThumbs.Select(this.getAttribute('id'));
    //  JavaScript in Internet Explorer is not multithreaded, so unfortunately
    //  the next image can't be loaded until this function is complete.
    SetLoadingBackground();
    LoadImage(globals[this.getAttribute('id')].image_path, Next_Init);
}
