/**
 * Copyright (c) 2007, Matt Snider, LLC. All rights reserved.
 * Version: 1.2
 */

var Core = {};
Core.Widget = {};

/**
 * The PhotoViewer class manages the slide show logic , requires a data object and a configuration object of DOM elements.
 * @namespace Core.Widget
 * @class PhotoViewer
 * @dependencies core, object
 * @param ds {Object} Required. The data JSON object.
 * @param cfg {Object} Required. The DOM nodes to attach events to.
 * @static
 */
Core.Widget.PhotoViewer = function(ds, cfg) {
	// Constant variables
	var $E = YAHOO.util.Event,
		$ = YAHOO.util.Dom.get;

	// Module variables
	var config = Object.is(cfg) ? cfg : {},
        data = Object.is(ds) ? ds : {temp: ds},
        F = function() {},
		that = null, // this value will contain a global reference to 'this' Object
		index = 0, // the current index in the desired Image set
		isArray = isType(ds, 'array'),
		set = []; // the current Image set

	// Module dom namespace
	var dom = {
        // this node will be used to force certain browsers to pre-cache images
        hnode: document.getElementsByTagName('body')[0].appendChild(document.createElement('div')),
        cap: $(config.caption), // optional, ID attribute to the caption or title tag
        img: $(config.image), // ID attribute to the 'img' tag
		next: $(config.next), // ID attribute to the 'a' tag to show the next image
		prev: $(config.prev) // ID attribute to the 'a' tag to showthe prev image
	};

    // ensure that the required values are present
    if (! dom.img) {alert('PhotoViewer - Main image is missing in DOM, an <IMG> tag with ID: ' + config.image + ' is required.'); return;}
    if (! dom.next) {alert('PhotoViewer - Next anchor is missing in DOM: an <A> tag with ID: ' + config.next + ' is required.'); return;}
    if (! dom.prev) {alert('PhotoViewer - Previous anchor is missing in DOM: an <A> tag with ID: ' + config.prev + ' is required.'); return;}

    // make hnode invisible
    dom.hnode.style.overflow = 'hidden';
    dom.hnode.style.height = '0px';

    // Module event namespace
	var evt = {

		/**
		 * Callback function for changing a set; the ID of the trigger element is used to determine the set
		 * @method onChange
		 * @param e {Event} Required. The triggered JavaScript Event; onclick.
		 * @private
		 */
		onChange: function(e) {
			$E.stopEvent(e); // stops the anchor tag from executing its normal behavior; this way only this action occurs
			that.changeSet($E.getTarget(e).id); // changes the active set to the image group identified by the Attribute ID of triggering anchor tag
		},

		/**
		 * Callback function for click next; increases the index by 1; when the index exceeds the greatest position in the set (set.length-1), the index is set to the first position in the set (0).
		 * @method onNext
		 * @param e {Event} Required. The triggered JavaScript Event; onclick.
		 * @private
		 */
		onNext: function(e) {
			$E.stopEvent(e); // stops the anchor tag from executing its normal behavior; this way only this action occurs
			index++; // increase the index of set by one
			if (index === set.length) {index = 0;} // if exceeding the maximum length of the set, index should wrap to the first value
			that.changeImage(); // call global Function to actually change the image
		},

		/**
		 * Callback function for click previous; reduces the index by 1; when index is less than the smallest position in the set (0), the index is set to the last value in the set (set.length-1).
		 * @method onNext
		 * @param e {Event} Required. The triggered JavaScript Event; onclick.
		 * @private
		 */
		onPrev: function(e) {
			$E.stopEvent(e); // stops the anchor tag from executing its normal behavior; this way only this action occurs
			index--; // decrement the index of set by one
			if (0 > index) {index = set.length-1;} // if exceeding the minimum length of the set, index should wrap to the last value
			that.changeImage(); // call global Function to actually change the image
		}
	};

	// Public methods and constants
	F.prototype = {

		/**
		 * function to call when adding an image to a set; the last image added determines the set that is active
		 * @public
		 */
		addImage: function(group, url) {
			// is there a data set for group of images; when no, add one to the 'data' Object
			if (! data[group]) {
				data[group] = []; // creates a new Array to fill with Images
			}

			set = data[group]; // retrieves the set (Array of Images) for group
			var img = new Image(); // creates a new instance of Image
			img.src = url; // links the Image Object to the image URL
			set.push(img); // appends the Image Object into the set
		},

		/**
		 *  function to change the image to the one in the current set @ index
		 *  @public
		 */
		changeImage: function() {
			var img = set[index].image; // grab the Image from active set @ index
			dom.img.src = img.src; // actually change the image

            // update the caption, when present
            if (dom.cap) {
                dom.cap.innerHTML = set[index].caption || '';
            }
        },

		/**
		 *  function to call when switching from one set to another
		 *  @public
		 */
		changeSet: function(group) {
			var o = data[group];

			if (isType(o, 'array')) { // verify that 'group' is a valid set
				index = 0; // reset the index
				set = o; // change the set to that of group
				that.changeImage(); // update the image
			}
			else {
				alert('You are trying to change to an image set that is not defined: ' + group);
			}
		}
	};

	that = new F();

	// attach the next and previous events
	$E.on(dom.next, 'click', evt.onNext);
	$E.on(dom.prev, 'click', evt.onPrev);

	// iterate through the data sets to find the Image that is initially visible
    Object.forEach(data, function(o, k) {
        var changeLink = $(k); // retrieve the set chooser 'a' tag from DOM

        // ensure that the required 'a' tag exists and attach onChange event
        if (changeLink) {
            $E.on(changeLink, 'click', evt.onChange);
        }
        // if data isn't an array, then we expect the change links to be present in the DOM
        else if (! isArray) {
            alert('PhotoViewer Error - required set chooser link is missing for: ' + k);
        }

        for (var i = o.length - 1; 0 <= i; i -= 1) {
            var img = new Image(); // creates a new instance of Image
            img.src = o[i].image || o[i]; // links the Image Object to the image URL
            o[i] = {image: img, caption: o[i].caption};
            dom.hnode.appendChild(img); // adding to DOM to force image to cache

            if (o[i].image.src === dom.img.src) {
				index = i;
				set = o;
			}
		}
    });

	if (! set.length) {
		alert('Warning: the initial image (' + dom.img.src + ') was not added to the image collection.');
	}

	return that;
};
