var SubFeatureViewer = Class.create();
Object.extend(Object.extend(SubFeatureViewer.prototype, AC.ViewMaster.Viewer.prototype), {
	initialize: function(contents, view, triggerClassName, options) {
		this.triggerClassName = triggerClassName;
		this.sections = $H();
		this.orderedSections = [];
		
		this.options = options || {};
		
		
		var initialSection = null;
		if(contents) {
			for (var i = 0; i < contents.length; i++) {
				//contents could be a NodeList, so we're going to use that API
				//I added an item method to Array in apple_core
				section = this.addSection(contents.item(i))
			
				if (!initialSection) {
					initialSection = section;
				}
			}
		}
		//Moved down to workaround a bug: in Safari, the results of getElementsByClassName is a NodeList.
		//If we do new AC.SwapView(view) before looping on the NodeList, the NodeList get emptied....
		this.view = new AC.SwapView(view);
		this.view.setDelegate(this);
		
		var hashInitialId = document.location.hash.replace(/#/, ''), hashSection;
		var initialFirstId = (hashInitialId.length > 0) ? hashInitialId.split('_')[1] : this.options.initialId;
			
		if(hashInitialId !== this.view._viewId) {
            hashSection = this.initialSectionFromId(hashInitialId);
        }
		if(hashSection) initialSection = hashSection;
		
		if(!initialSection && typeof this.options.initialId === "string" && this.options.initialId.length > 0) {
			initialSection = this.initialSectionFromId(this.options.initialId);
		}

		//TODO do we want to show the initial section right away? seems like we have to but if no delegates are set yet this will be a bit different than subsequent calls to show
		this.show(initialSection);
		
		Event.observe(document, 'click', this._triggerClicked.bindAsEventListener(this));
		//In IE click event isn't sent when there is no text/image physically under the mouse, but the mouseup is, so we need to listen to that
		if(AC.Detector.isIEStrict()) {
			Event.observe(document, 'mouseup', this._triggerClicked.bindAsEventListener(this));
		}
	},
	
	setLoadedContent:function(content) {

		if (typeof(this.delegate.willShow) == 'function') {
		 content = this.delegate.willShow(this, this.currentContent, content);
		}
		
		if (typeof(this.delegate.willClose) == 'function') {
			this.delegate.willClose(this, this.currentContent);
		}
		
		var shouldAnimate = true;
		if (typeof(this.delegate.shouldAnimateContentChange) == 'function') {
			shouldAnimate = this.delegate.shouldAnimateContentChange(this, this.currentContent, content);
		}
		
		if (shouldAnimate && typeof(this.delegate.willAnimate) == 'function') {
			//While animating we can assume we'll need both outgoing and
			//incoming content in the view at the same time, so just
			//append the incoming content prior to the animation
			
			//Note that in this case the content of the swapview should be
			//positioned absolutely so we can layer them on top of each other
			//if you can't accommodate that then just don't provide a
			//willanimate function in your delegate and you'll rely on the
			//immediate swapping
			this.didAnimate = true;
			if(this.currentContent !== content) this.view().appendChild(content);
			var animation = this.delegate.willAnimate(this, this.currentContent, content, this.didShow.bind(this, content));
		} else {
			
			this.didAnimate = false;
			//With no animation we don't assume both nodes are ever in the view at the same time
			//so remove the current content before appending the incoming content
			if(this.currentContent !== content) {
				if (this.currentContent && this.currentContent.parentNode) {
					this.currentContent.parentNode.removeChild(this.currentContent);
				}

				if(content) this.view().appendChild(content);
			}
			if(content) $(content).setOpacity(1.0);
			
			this.didShow(content);
		}
	},
	
	show: function(section) {
		if (this._locked || section == this.currentSection) {
			return;
		}

		this._locked = true;

		this.previousSection = this.currentSection;
		this.currentSection = section;

		this.view.setContent(section);
		
		if (typeof this.options.ensureInView === "boolean" && this.options.ensureInView) {
			if (this._didShowInitial) {

				var yOffset = section.content.viewportOffset()[1];
				//if the content is above viewport to pretty far down the page bring it into view
				if (yOffset < 0 || yOffset > (document.viewport.getHeight() * .75)) {
					new Effect.ScrollTo(section.content, {duration: 0.3});
				}
			} else {
				//ensure we're at the top of the page when the page has 
				//'loaded' otherwise a requested anchor is followed and the 
				//page may have started where the element was prior to styling
				$(document.body).scrollTo();
			}
		}
	
	},
	
	_categoryChanged: function(element) {
		
		// Stop as early as possible if there's no need to continue
		
		if (this._locked) {
			return;
		}
		
		var trigger = element;
		
		if(AC.Detector.isIEStrict()) {
			if(trigger && trigger.nodeName.toUpperCase() === 'A' ) {
				trigger = trigger.down("."+this.triggerClassName);
			}
		}
		else {
			while (trigger && trigger.nodeName.toUpperCase() != 'A' && trigger.nodeName.toUpperCase() != 'BODY') {
				trigger = trigger.parentNode;
			}
		}
		//ignore if the element is not a trigger
		if (trigger && trigger.href && Element.Methods.hasClassName(trigger,this.triggerClassName)) {
			this.categoryChanged(trigger);
		} 
		
	},

	categoryChanged: function(element) {
		
		var section = null;	
	
		if (!!element.href.match(/#previous/)) {
			section = this.getPreviousSection();
		} else if (!!element.href.match(/#next/)) {
			section = this.getNextSection();
		} else {
			var matches = element.href.match(/#(.*)$/);
			if(matches) {
				var contentId = matches[1];
			}
			else if(element.name) {
				contentId = element.name;
			} else {
				contentId = element.id;
			}
			section = this.sections.get(contentId);
		}

		//No section means either a lazy initialization of sections
		//or a section for which the content is remote.
		if(!section) {
			section = this.addSection(element);
		}
		
		
		//stop if the trigger is trying to open the current section
		if (!section || section == this.currentSection) {
			return;
		}

		this._didShowInitial = true;
		this.show(section);
	},
	
	_triggerClicked: function(evt) {
		
		// Stop as early as possible if there's no need to continue
		
		if (this._locked) {
			Event.stop(evt);
			return;
		}
		
		var trigger = evt.element();
		
		if(AC.Detector.isIEStrict() && evt.type === "mouseup") {
			if(trigger && trigger.nodeName.toUpperCase() === 'A' ) {
				trigger = trigger.down("."+this.triggerClassName);
			}
		}
		else {
			while (trigger && trigger.nodeName.toUpperCase() != 'A' && trigger.nodeName.toUpperCase() != 'BODY') {
				trigger = trigger.parentNode;
			}
		}
		//ignore if the element is not a trigger
		if (trigger && trigger.href && Element.Methods.hasClassName(trigger,this.triggerClassName)) {
			this.triggerClicked(evt, trigger);
		} 
		
	},
	
	triggerClicked: function(evt, element) {
		
		var section = null;	
		if (this.options.silentTriggers) {
			Event.stop(evt);
		}
	
		if (!!element.href.match(/#previous/)) {
			section = this.getPreviousSection();
		} else if (!!element.href.match(/#next/)) {
			section = this.getNextSection();
		} else {
			var matches = element.href.match(/#(.*)$/);
			if(matches) {
				var contentId = matches[1];
			}
			else if(element.name) {
				contentId = element.name;
			} else {
				contentId = element.id;
			}
			section = this.sections.get(contentId);
		}

		//No section means either a lazy initialization of sections
		//or a section for which the content is remote.
		if(!section) {
			section = this.addSection(element);
		}
		
		if(section.isContentRemote() && !section.isContentLoaded()) {
			Event.stop(evt);
		}
		
		//stop if the trigger is trying to open the current section
		if (section == this.currentSection) {
			Event.stop(evt);
			return;
		} else if (!section) {
			return;
		}

		this._didShowInitial = true;
		this.show(section);
	},
	
	_repaintTriggers: function(outgoing, incoming) {
		if(outgoing) {
			var outgoingTriggers = outgoing.triggers();
			for(var i=0, iTrigger;(iTrigger = outgoingTriggers[i]);i++) {
				iTrigger.removeClassName('active');
			}
			outgoingTriggers = outgoing.relatedElements();
			for(var i=0, iTrigger;(iTrigger = outgoingTriggers[i]);i++) {
				iTrigger.removeClassName('active');
			}
		}
		
		if(incoming) {
			var incomingTriggers = incoming.triggers();
			for(var i=0, iTrigger;(iTrigger = incomingTriggers[i]);i++) {
				iTrigger.addClassName('active');
				iTrigger.addClassName('visited');
			}
			incomingTriggers = incoming.relatedElements();
			for(var i=0, iTrigger;(iTrigger = incomingTriggers[i]);i++) {
				iTrigger.addClassName('active');
				iTrigger.addClassName('visited');
			}
		}		
	},

	isContentLoaded: function(swapView, content) {
		//content her is a Section instance
		if(typeof content != "object") {
			content = this.sections.get(content);
		}
		return content.isContentLoaded();
	}
});
