/**
 * DocsByDean App
 */

YUI.add('dbd-app', function(Y){
	
	var Announcements, Photos,
		
		DOT = '.',
		
		getCN = Y.ClassNameManager.getClassName,
		
		L = Y.Lang,
		isString = L.isString,
		isNumber = L.isNumber,
		isArray = L.isArray;
	
	
	// *** Announcements *** //
	
	Announcements = Y.Base.create('announcements', Y.Widget, [], {
		
		// *** Prototype *** //
		
		// *** Lifecycle Methods *** //
		
		initializer : function (config) {
			
			if ( ! this.get('username')) {
				Y.error('Announcements needs a Tumblr username');
			}
			
			this.publish('addAnnouncements', { defaultFn: this._defAddAnnouncementsFn });
		},
		
		// *** Public Methods *** //
		
		getAnnouncements : function (callback) {
			
			var query = new Y.yql(Y.substitute(Announcements.ANNOUNCEMENTS_YQL, { username: this.get('username'), num: this.get('num') }), function(r){
				callback(r.query && r.query.results ? r.query.results.posts.post : null);
			});
			
			return this;
		},
		
		addAnnouncements : function (data) {
			
			this.fire('addAnnouncements', { announcements: data });
			return this;
		},
		
		// *** Private Methods *** //
		
		_defAddAnnouncementsFn : function (e) {
			
			var contentBox = this.get('contentBox'),
				df = Y.Markout().getNode(),
				announcements = isArray(e.announcements) ? e.announcements : e.announcements ? [e.announcements] : [];
			
			Y.Array.each(announcements, Y.bind(function(announcement){
				df.append(this._renderAnnouncement(announcement).setData(announcement));
			}, this));
			contentBox.append(df);
		},
		
		_renderAnnouncement : function (data) {
			
			var date = new Date(data.date),
				announcement = Y.Markout().div({ 'class': this.getClassName('announcement') }),
				bodyContent = Y.Markout().div().getNode().set('text', data['regular-body']).get('text'),
				dateNode;
			
			announcement.h3({ 'class': this.getClassName('announcement', 'title') }).text(data['regular-title']);
			dateNode = announcement.p({ 'class': this.getClassName('announcement', 'date') });
			dateNode.small().text(Announcements.MONTHS[date.getMonth()]);
			dateNode.space();
			dateNode.big().text(date.getDate());
			return announcement.getNode().append(bodyContent);
		}
		
	}, {
		
		// *** Static *** //
			
		ATTRS : {
		
			num			: { value: 1, validator: isNumber, initOnly: true },
			username	: { validator: isString, initOnly: true }
		
		},
		
		CONTENT_TEMPLATE : null,
		
		MONTHS : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
		
		ANNOUNCEMENTS_YQL : 'SELECT * FROM tumblr.posts WHERE username="{username}" AND num="{num}"'
		
	});
	
	
	// *** Photo Viewer *** //
	
	PhotoViewer = Y.Base.create('photoViewer', Y.Widget, [Y.BaseComponentMgr], {
		
		// *** Prototype *** //
		
		// *** Lifecycle Methods *** //
		
		initializer : function (config) {
			
			if ( ! this.get('photosetId')) {
				Y.error('PhotoViewer needs a photosetId');
			}
			
			this.publish('addPhotos', { defaultFn: this._defAddPhotosFn });
		},
		
		destructor : function () {
			
			var photoOverlay = this.getComponent('photoOverlay');
			if (photoOverlay) {
				photoOverlay.destroy();
			}
		},
		
		bindUI : function () {
			
			this.get('boundingBox').delegate('click', Y.bind(this._onPhotoClick, this), DOT + this.getClassName('photo'));
		},
		
		// *** Public Methods *** //
		
		getPhotos : function (start, num, callback) {
			
			var query = new Y.yql(Y.substitute(PhotoViewer.PHOTOS_YQL, { id: this.get('photosetId'), start: start, num: num }), function(r){
				callback(r.query && r.query.results ? r.query.results.photo : null);
			});
			
			return this;
		},
		
		addPhotos : function (data) {
			
			this.fire('addPhotos', { photos: data });
			return this;
		},
		
		loadPhoto : function (photo, callback) {
			
			photo = (photo instanceof Y.Node) ? photo.getData() : photo;
			
			if (photo.loaded) {
				callback(photo);
				return this;
			}
			
			var query = new Y.yql(Y.substitute(PhotoViewer.PHOTO_YQL, { id: photo.id }), Y.bind(function(r){
				Y.mix(photo, r.query && r.query.results ? r.query.results.photo : null);
				this.loadImg(this.getPhotoURL(photo), function(){
					photo.loaded = true;
					callback(photo);
				});
			}, this));
			
			return this;
		},
		
		getPhotoURL : function (photo, size) {
			
			photo = (photo instanceof Y.Node) ? photo.getData() : photo;
			return Y.substitute(PhotoViewer.PHOTO_URL_TEMPLATE, Y.merge(photo, { size: size ? '_'+size : '' }));
		},
		
		getPhotoPageURL : function (photo) {
			
			photo = (photo instanceof Y.Node) ? photo.getData() : photo;
			return photo.urls.url.content;
		},
		
		loadImg : function (src, callback) {
			
			// insired by: Lucas Smith (http://lucassmith.name/2008/11/is-my-image-loaded.html)
			
			var img = new Image(),
				prop = img.naturalWidth ? 'naturalWidth' : 'width';
			
			img.src = src;
			
			if (img.complete) {
				callback(img[prop] ? img : null);
			} else {
				img.onload = Y.bind(callback, this, img);
				img.onerror = Y.bind(callback, this, null);
			}
		},
		
		// *** Private Methods *** //
		
		_initPhotoOverlay : function () {
		
			var overlay, bb;
			
			overlay = new Y.Overlay({
				visible			: false,
				align			: { node: this.get('boundingBox'), points: [Y.WidgetPositionAlign.CC, Y.WidgetPositionAlign.CC] },
				width			: '550px',
				zIndex			: 100,
				headerContent	: Y.Markout().span({ 'class': 'close', title: 'Close' }).getNode(),
				render			: true,
				plugins			: [
				
					{ fn: Y.Plugin.WidgetAnim },
					{ fn: Y.Plugin.OverlayModal },
					{ fn: Y.Plugin.OverlayKeepaligned },
					{ fn: Y.Plugin.OverlayAutohide }
					
				],
				after			: {
					
					photoChange : Y.bind(function(e){
						overlay.setStdModContent(Y.WidgetStdMod.BODY, this._renderPhoto(e.newVal));
						if ( ! Y.UA.ie) {
							overlay.syncUI();
						}
						overlay.show();
					}, this)
					
				}
			});
			
			overlay.addAttr('photo', { validator: function(v){ return ( v instanceof Y.Node ); } });
			
			bb = overlay.get('boundingBox');
			bb.one('.close').on('click', Y.bind(overlay.hide, overlay));
			bb.delegate('click', Y.bind(function(e){
				var photo = e.currentTarget.test(DOT + this.getClassName('prev')) ?
							overlay.get('photo').previous() : overlay.get('photo').next();
				
				bb.one(DOT + this.getClassName('photo', 'large')).addClass('loading');
				this.loadPhoto(photo.addClass('loading'), function(){
					overlay.set('photo', photo.removeClass('loading'));
				});
				e.halt(true);
			}, this), DOT + this.getClassName('prev') + ', ' + DOT + this.getClassName('next'));
			
			return overlay;
		},
		
		_defAddPhotosFn : function (e) {
			
			var listNode = this.get('listNode'),
				df = Y.Markout().getNode(),
				photos = isArray(e.photos) ? e.photos : e.photos ? [e.photos] : [];
			
			Y.Array.each(photos, Y.bind(function(photo){
				df.append(this._renderThumbnail(photo).setData(photo));
			}, this));
			listNode.append(df);
		},
		
		_renderThumbnail : function (data) {
			
			var src = this.getPhotoURL(data, 's'),
				photo = Y.Markout().li({ 'class': this.getClassName('photo') });
				
			this.loadImg(src, Y.bind(function(){
				photo.img({ src: src, title: data.title, alt: data.title });
			}, this));
			
			return photo.getNode();
		},
		
		_renderPhoto : function (photo, size) {
			
			var df = Y.Markout(),
				data = photo.getData(),
				matte, largePhoto;
			
			df.h4().text(data.title);
			
			matte = df.div({ 'class': this.getClassName('photo', 'matte') });
			largePhoto = matte.div({ 'class': this.getClassName('photo', 'large') });
			largePhoto.img({ src: this.getPhotoURL(data, size), alt: data.title });
			if (photo.previous()) {
				largePhoto.span({ 'class': this.getClassName('prev'), title: 'Previous Photo' });
			}
			if (photo.next()) {
				largePhoto.span({ 'class': this.getClassName('next'), title: 'Next Photo' });
			}
			matte.p({ 'class': this.getClassName('flickr') }).small().a({ href: this.getPhotoPageURL(data) }).text('View on Flickr');	
			if (data.description) {
				df.p({ 'class': this.getClassName('photo', 'desc') }).text(data.description);
			}
			
			return df.getNode();
		},
		
		_onPhotoClick : function (e) {
			
			var photo = e.currentTarget;
				
			this.useComponent('photoOverlay', function(overlay){
				this.loadPhoto(photo.addClass('loading'), Y.bind(function(){
					overlay.set('photo', photo.removeClass('loading'));
				}, this));
			});
		}
		
	}, {
		
		// *** Static *** //
		
		ATTRS : {
			
			photosetId	: { validator: isString, initOnly: true },
			photoNodes	: { initOnly: true },
			listNode	: { initOnly: true },
			overlay		: { readOnly: true, getter : function(){
				return this.getComponent('overlay');
			}}
			
		},
		
		HTML_PARSER : {
			
			photoNodes	: [DOT +getCN('photoviewer', 'photo')],
			listNode	: DOT + getCN('photoviewer', 'photos')
			
		},
		
		COMPONENTS : {
			
			photoOverlay : {
				requires	: ['overlay', 'overlay-extras', 'widget-anim'],
				initializer	: '_initPhotoOverlay'
			}
			
		},
		
		CONTENT_TEMPLATE : null,
		
		
		PHOTO_YQL : 'SELECT * FROM flickr.photos.info WHERE photo_id="{id}"',
		PHOTOS_YQL : 'SELECT * FROM flickr.photosets.photos({start}, {num}) WHERE photoset_id="{id}"',
		PHOTO_URL_TEMPLATE : 'http://farm{farm}.static.flickr.com/{server}/{id}_{secret}{size}.jpg'
		
	});
	
	
	// *** App *** //
	
	Y.namespace('DBD').App = Y.Base.create('app', Y.Base, [Y.BaseComponentMgr], {
		
		// *** Prototype *** //
		
		// *** Lifecycle Methods *** //
		
		initializer : function (config) {
			
			var flickrPhotosetIds = this.get('flickrPhotosetIds'),
				tumblrUsername = this.get('tumblrUsername');
			
			if ( ! flickrPhotosetIds || ! tumblrUsername) {
				Y.error('App needs to be configured with a flickrPhotosetID and tumblrUsername');
			}
			
			this.on('initComponents', this._initComponents);
			this.after('activeNavItemChange', function(e){ this._uiSetActiveNavItem(e.newVal); });
			
			if ( ! Y.UA.mobile) {
				Y.one('win').on('scroll', Y.bind(this._onScroll, this));
				Y.one('body').plug(Y.Plugin.Scroller, { easing: Y.Easing.easeNone });
			}
		},
		
		// *** Public Methods *** //
		
		// *** Private Methods *** //
		
		_initComponents : function (e) {
		
			e.componentsToInit.push('announcements', 'photoSets', 'map');
		},
		
		_uiSetActiveNavItem : function (navItem) {
			
			this.get('navItems').removeClass('active');
			navItem.addClass('active');
		},
		
		_onScroll : function (e) {
		
			var navItems = this.get('navItems'),
				activeNavItem = this.get('activeNavItem'),
				inView = [],
				inViewNI = [],
				activeIndex = 0,
				i, a, c, n;
			
			navItems.each(function(ni){
				var section = Y.one(ni.get('hash'));
				if (section.inViewportRegion()) {
					inView.push(section);
					inViewNI.push(ni);
				}
			});
			
			for (i = 0; i < inView.length; i++) {
				a = inView[activeIndex];
				c = inView[i];
				n = inView[i+1];
					
				if (a.inViewportRegion(true)) {
					break;
				} else if (this._amountVisible(c) > this._amountVisible(a)) {
					if (c.inViewportRegion(true)) {
						activeIndex = i;
						break;
					} else if ( ! n) {
						activeIndex = i;
						break;
					} else if (this._amountVisible(n) >= this._amountVisible(c)) {
						activeIndex = i;
						break;
					}
				}
			}
			
			if (inViewNI[activeIndex] !== activeNavItem) {
				this.set('activeNavItem', inViewNI[activeIndex]);
			}
		},
		
		_initAnnouncements : function () {
			
			var announcements;
			
			announcements = new Announcements({
			
				srcNode		: '#announcements .yui3-announcements',
				boundingBox	: '#announcements .yui3-announcements',
				username	: this.get('tumblrUsername'),
				num			: 2
				
			}).getAnnouncements(function(a){
				
				var bb = announcements.get('boundingBox'),
					fromHeight, toHeight;
				
					fromHeight = bb.getComputedStyle('height');
					announcements.addAnnouncements(a).render();
					toHeight = bb.getComputedStyle('height');
					bb.setStyles({ opacity: '0', height: fromHeight });
					bb.morph({
						to			: { height: toHeight },
						duration	: 0.25,
						after		: {
							finish : function(e){
								bb.setStyle('height', 'auto');
							}
						}
					}).appear({ duration: 0.5 });
				
			});
			
			return announcements;
		},
		
		_initPhotoSets : function () {
			
			var photoSets = [];
			
			Y.Array.each(this.get('flickrPhotosetIds'), function(id, i){
			
				var container = Y.all('#photos .yui3-photoviewer').item(i),
					photoSet;
				
				photoSet = new PhotoViewer({
					
					srcNode		: container,
					boundingBox	: container,
					photosetId	: id
					
				}).getPhotos(1, 21, function(photos){
					
					var fromHeight, toHeight;
					
						fromHeight = container.getComputedStyle('height');
						photoSet.addPhotos(photos).render();
						toHeight = container.getComputedStyle('height');
						container.setStyles({ opacity: '0', height: fromHeight });
						container.morph({
							to			: { height: toHeight },
							duration	: 0.25,
							after		: {
								finish : function(e){
									container.setStyle('height', 'auto');
								}
							}
						}).appear({ duration: 0.5 });
					
				});
				
				photoSets.push(photoSets);
				
			});
			
			return photoSets;
		},
		
		_initMap : function () {
			
			var locations, map, infoWins, showInfoWin;
			
			locations = [
				
				{ latLng: new google.maps.LatLng(43.400839, -85.800095), content: '#map-store' },
				{ latLng: new google.maps.LatLng(43.384591, -85.800133), content: '#map-work' },
				{ latLng: new google.maps.LatLng(43.392076, -85.756874), content: '#map-home' }
				
			];
			
			map = new google.maps.Map(document.getElementById("map"), {
				zoom		: 10,
				center		: locations[0].latLng,
				mapTypeId	: google.maps.MapTypeId.ROADMAP,
				scrollwheel	: false
			});
			
			infoWins = [];
			
			showInfoWin = function (map, marker) {
				
				Y.Array.each(infoWins, function(infoWin){
					infoWin.close();
				});
				
				this.open(map, marker);
			};
			
			Y.each(locations, function(loc){
				
				var marker, infoWin, content, heading;
				
				heading = Y.one(loc.content).one('h5').set('title', 'Show on Map');
				content = Y.one(loc.content).cloneNode(true);
				content.all('.hidden').removeClass('hidden');
				
				if (content.test('#map-store')) {
					content.one('address').remove();
				}
				
				marker = new google.maps.Marker({
					map			: map,
					position	: loc.latLng,
					clickable	: true
				});
				
				infoWin = new google.maps.InfoWindow({
					content	: Y.Node.getDOMNode(content)
				});
				
				infoWins.push(infoWin);
				
				heading.on('click', Y.bind(showInfoWin, infoWin, map, marker));
				google.maps.event.addListener(marker, 'click', Y.bind(showInfoWin, infoWin, map, marker));
				
			});
			
			return map;
		},
		
		_amountVisible : function (node) {
			
			var nr = node.get('region'),
				vr = node.get('viewportRegion');
				
			if (nr.bottom >= vr.top && nr.bottom <= vr.bottom) {
				return nr.bottom - vr.top;
			} else {
				return vr.bottom - nr.top;
			}
		}
		
	}, {
		
		// *** Static *** //
		
		ATTRS : {
			
			tumblrUsername		: { validator: isString, initOnly: true },
			flickrPhotosetIds	: { validator: isArray, initOnly: true },
			navItems			: { value: Y.all('#nav .sections > li > a'), readOnly: true },
			activeNavItem		: {  }
			
		},
		
		COMPONENTS : {
			
			announcements	: { initializer: '_initAnnouncements' },
			photoSets		: { initializer: '_initPhotoSets' },
			map				: { initializer: '_initMap' }
			
		}
		
	});
	
}, '1.0.0', {
	requires	: [	'base', 'widget', 'widget-anim', 'substitute', 'node', 'event-resize', 'overlay',
					'anim', 'async-queue', 'collection', 'event-synthetic',
					'gallery-base-componentmgr', 'gallery-markout', 'gallery-yql', 'gallery-effects', 'gallery-outside-events', 'gallery-overlay-extras',
					'scroller-plugin'
				  ]
});
