/*jslint browser: true*/
/*global $, jQuery, Cav*/

/* * * * * * * * * * * * * * * *
 *                             *
 * @author Mike Cavaliere      *
 *                             *
 * http://cavaliere.org        *
 *                             *
 * * * * * * * * * * * * * * * */

if (typeof Cav === 'undefined')
{
    Cav = {};
}

/*
 * IE doesn't implement these constants
 */
if (typeof document.ELEMENT_NODE === 'undefined')
{
  document.ELEMENT_NODE = 1;  
}
if (typeof document.TEXT_NODE === 'undefined')
{
  document.TEXT_NODE = 3;  
}

(function()
{
    $.ajaxSetup({
       cache: false 
    });
    
    Cav.Config = {
      debug: true  
    };
    
    Cav.Util = {
        log: function()
        {
          if (!Cav.Config.debug) { return; }
          
          if (!window.cav) 
          {
            window.cav = {};
            window.cav.log = [];
          }

          function write(str)
          {
            try 
            {
              var d = new Date();
              window.cav.log.push('(' + d.getTime() + ') ' + d.toString() + ": " + str);
            } 
            catch (err1) 
            {
            
            }
          }
          
          function dump()
          {
            var log;
            if (arguments[0]) 
            {
              var regexp = arguments[0];
              log = window.cav.log.filter(function(str)
              {
                return regexp.test(str);
              });
            }
            else 
            {
              log = window.cav.log;
            }
            
            try 
            {
              console.log(log.join("\n"));
            } 
            catch (err) 
            {
              try 
              {
                var logWindow = window.open("about:blank", "logWindow", "toolbar=no,width=500,height=500,resizable=yes,scrollbars=yes");
                logWindow.document.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >' +
                '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' +
                window.cav.log.join('<br />') +
                '</body></html>');
              } 
              catch (err2) 
              {
                alert(window.cav.log.join("\n"));
              }
            }
          }
                    
          // Determine what to do based on params
          if (arguments[0]) 
          {
            switch (arguments[0].constructor)
            {
              // Util.log(/pattern/) - filter log and output it
              case RegExp:
                dump(arguments[0]);
                break;
                
              // Util.log("some string") - add "some string" to the log
              case String:
                write(arguments[0]);
                break;
                
              default:
                throw new Error("Util.log called with invalid argument.");     
            }
          }
          else 
          {
            // Util.log() - no params, output the log
            dump();
          }
        },
        
        
        /*
         * Convert a tree of XML nodes to the HTML equivalent.
         * 
         * Taken from http://www.alistapart.com/articles/crossbrowserscripting 
         */
        XmlToHtml: function(node, allChildren) 
        {              
            switch (node.nodeType)
            {
              case document.ELEMENT_NODE:
                var newNode = document.createElement(node.nodeName);
                
                /* does the node have any attributes to add? */
                if (node.attributes && node.attributes.length > 0) 
                {
                  Cav.Util.log('element node has attributes...');
                  for (var i = 0, il = node.attributes.length; i < il; i++ )
                  {
                    Cav.Util.log('i: '+i);
                    newNode.setAttribute( node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName) );  
                  }
                }

                /* are we going after children too, and does the node have any? */
                if (allChildren && node.childNodes && node.childNodes.length > 0)
                {
                  Cav.Util.log('element node has children...');
                  for (var i = 0, il = node.childNodes.length; i < il; i++)
                  {
                    Cav.Util.log('i: '+i);
                    var children = Cav.Util.XmlToHtml(node.childNodes[i], allChildren);
                    newNode.appendChild(children);  
                  }   
                }
                
                return newNode;
                
              case document.TEXT_NODE:
              case document.CDATA_SECTION_NODE:
              case document.COMMENT_NODE:
                return document.createTextNode(node.nodeValue);
            }
         }
    };
    



    
    /*
     * Constructor
     */
    Cav.Gallery = function(el)
    {
        Cav.Util.log('Gallery() constructor');
        var gallery = this, projectId;
        
        this.element     = el;
        this.viewer      = this.element.find('.viewer');
        this.thumbList   = this.element.find('.thumbs');
        this.captionList = this.element.find('.descriptions');
        
        this.loader = $('<div class="loader"><img src="images/bubble.gif" alt="Please wait...loading..." /></div>');
        gallery.element.append( this.loader );
        
        gallery.on('after-loader-show', function() {
            gallery.fetchSettings();
        });
        
        gallery.on('retrieve-xml', function() {
            Cav.Util.log('retrieve-xml');
        
            projectId = gallery.firstProject();

            gallery.on('insert-image-tag-'+projectId+'-0', function() {
                gallery.hideLoader();
            });
                
            gallery.loadImage( projectId, 0 );
            gallery.updateThumbs( projectId, 0 );
            gallery.updateDescription( projectId, 0 );
        });

        gallery.showLoader();
        
        $('#sub-nav li a').each(function(i)
        {
            var projectId = $(this).parent().eq(0).attr('class');
            
            
            // Strategy: set up a progression of events when clicked;
            //  then set the ball rolling with the first one
            $(this).bind('click', function(e)
            {   
                gallery.on([
                    [
                        'after-loader-show', function() {
                            gallery.loadImage( projectId, 0 );
                            gallery.updateThumbs( projectId, 0 );
                            gallery.updateDescription( projectId, 0 );
                        }
                    ],
                    
                    [
                        'insert-image-tag-' + projectId + '-' + 0, function() {
                            gallery.hideLoader();
                        }
                    ]
                ]);
                
                gallery.showLoader();
            }); 
        });
    };
    
    Cav.Gallery.prototype = {
        /*
         * Custom event binding wrapper
         */
        on: function()
        {
            Cav.Util.log('on('+arguments[0]+')');
            
            // Function that does the actual binding
            var list, event, f;
            
            // If it's an array, 
            if (arguments.length === 1 && arguments[0].constructor === Array)
            {
                list = arguments[0];
            } 
            else if (arguments.length === 2)
            {
                list = [[arguments[0], arguments[1]]];
            }
            else { throw new Error('on() parameters follow incorrect format.'); }
            
            for (var i=0,l=list.length; i<l; i++)
            {
                event = list[i][0];
                f     = list[i][1];
                
                this.element.unbind(event).bind(event, f);
            }
        },
        
        /*
         * Initiate request for project data
         */
        fetchSettings: function( projectId )
        {
            Cav.Util.log('fetchSettings()');
            
            var gallery = this;

            // Fetch the xml and trigger event
            $.get('gallery.xml', {}, function(data, status)
            {
                gallery.saveConfig(data);
                Cav.Util.log('retrieve-xml');
                gallery.element.trigger('retrieve-xml');
            }, 'xml');            
        },
        
        /*
         * Get image information from xml
         */
        imageData: function( projectId, imageIndex )
        {
           Cav.Util.log('imageData('+projectId+', '+imageIndex+')');
           return this.images[projectId][imageIndex];
        },
        
        imageCount: function( projectId )
        {
            return this.data
                    .find('project#' + projectId + ' image')
                    .length;
        },
        
        /*
         * Return id of first project in section
         */
        firstProject: function()
        {
          return this.data.find('project:first').attr('id');  
        },
        
        /*
         * Fetch and insert image data
         */
        loadImage: function( projectId, imageIndex )
        {
            Cav.Util.log('loadImage()');
            var gallery = this, 
                eventName;

            var imageData = gallery.imageData( projectId, imageIndex ),
                url       = imageData.img.getAttribute('src');
     
            // After fetching the image, insert it
            gallery.on('retrieve-image-data-' + projectId + '-' + imageIndex, function()
            {
                gallery.viewer                                // Remove existing images
                            .children().remove().end()        // Add new image
                            .append( Cav.Util.XmlToHtml(imageData.img, true) );
             
                Cav.Util.log('insert-image-tag-' + projectId + '-' + imageIndex);
                gallery.element.trigger('insert-image-tag-' + projectId + '-' + imageIndex);
            });
                
            gallery.element.trigger('retrieve-image-data-' + projectId + '-' + imageIndex);


            // Fetch the image via ajax, fire event on completion
            $.get(url, {}, function(data, status)
            {
                Cav.Util.log('retrieve-image-data-' + projectId + '-' + imageIndex);
                gallery.element.trigger('retrieve-image-data-' + projectId + '-' + imageIndex);
            });
            
            Cav.Util.log('executed image ajax call');         
        },
        
        /*
         * Append linked thumbnails for the given project
         */
        updateThumbs: function( projectId )
        {
            Cav.Util.log('updateThumbs('+projectId+')');
            
            var gallery = this,
                count   = this.imageCount( projectId ),
                images;

            // Clear existing thumbs
            gallery.thumbList.children().remove();
            
            // No need to show thumbs if there's only one
            if ( count < 2 ) { return; }
                        
            for (var i=0, l=count; i<l; i++)
            {
                // Add thumbnail icons
                gallery.thumbList  
                    .append( '<a href="javascript:void(0)" title=""><img class="'+projectId+'" src="images/icons/thumb-off.gif" alt="" /></a>' );                
            }
            
            // TODO - move this into a shared method; call it every time an image is requested
            images = gallery.thumbList.find('img');
            
            // Set the first one to "on"
            images.filter(":first").imageToggle(true);
                
            
            images
                // Basic UI events
                .hoverToggle(0).clickToggle(0)
                
                // Image swapping
                .click(function(e)
                {
                   var index = gallery.thumbList.find('img').index( $(e.target) );
                   
                   gallery.loadImage( projectId, index );
                   gallery.updateDescription( projectId, index );
                });
        },
        
        /*
         * Update descriptive text
         */
        updateDescription: function( projectId, imageIndex )
        {
            var gallery = this,
                desc = this.captionList.html(''),
                children = this.imageData( projectId, imageIndex ).description.childNodes;
            
            for (var i=0; i<children.length; i++)
            {
                desc.append(Cav.Util.XmlToHtml(children[i], true));   
            }
        },
        
        /*
         * Save xml to internal structure
         */
        saveConfig: function(xml)
        {  
           // Find the <section> for this page and make it our root
           this.data = $(xml.documentElement).find('section#'+document.body.id);
           
           var sections = xml.documentElement.getElementsByTagName('section');
           
           this.images = {};
           
           for(var i=0; i<sections.length; i++)
           {
               var sectionId = sections[i].getAttribute('id');
               
               // We only want this section; ignore the others
               if (sectionId !== document.body.id) { continue; }
               
               var projects = sections[i].getElementsByTagName('project');
               
               for (var j=0; j<projects.length; j++)
               {
                   var projectId = projects[j].getAttribute('id');
                   var images  = projects[j].getElementsByTagName('image');
                   
                   this.images[projectId]  = [];
                   
                   for (var k=0; k<images.length; k++)
                   {        
                       this.images[projectId].push({
                           img: images[k].getElementsByTagName('img')[0],
                           description: images[k].getElementsByTagName('description')[0]
                       });
                   } 
               }
               
           }
        },
        
        blockUI: function()
        {
            if ( !this.blocker )
            {
                this.blocker = $('<div class="ui-blocker"></div>');
                
                $('#body').append( this.blocker );
            }
            
            this.blocker.fadeIn();
        },
        
        unblockUI: function()
        {
            this.blocker.fadeOut();  
        },
        
        showLoader: function()
        {
            var gallery = this;

            this.loader.fadeIn(200, function()
            {
                Cav.Util.log('after-loader-show');
                gallery.element.trigger('after-loader-show');
                
            });
        },
        
        hideLoader: function()
        {
            Cav.Util.log('hideLoader()');
            
            var gallery = this;
            
            this.loader.fadeOut(200, function()
            {
                Cav.Util.log('after-loader-hide');
                gallery.element.trigger('after-loader-hide');
            });
        },
        
        /*
         * Image transitions
         */
        showImage: function( index )
        {
            var gallery = this;

            gallery.viewer.find('img')
            
                    // Hide existing images
                    .filter(':visible').hide().end()
                    
                    // Show desired image
                    .eq(index).show(); 
                    
           gallery.captions.hide().eq(index).show();           
        },
        
        /*
         * Set the active thumbnail
         */
        setThumb: function( index )
        {
            var thumbs   = this.thumbList.children(),
                newThumb = thumbs.eq(index);
            
            // Turn off old one
            if ( !!this.currentThumb && this.currentThumb[0] !== newThumb )
            {
                this.currentThumb.children('img').imageToggle(false).hoverToggle();
            }
            
            this.currentThumb = newThumb;
            
            // Turn on current one
            this.currentThumb.children('img').unbind().imageToggle(true);
        }
    };
   
    Cav.Gallery._instances = [];
   
    

    
})();












