var CatalogueViewer = Class.create({
	params: {
		id: {
			container: 'CatalogueViewer',
			bookTitle: 'CVBookTitle',
			bookDescription: 'CVBookDescription',
			pageList: 'CVPageList',
			listWrapper: 'CVCatalogueWrapper',
			list: 'CVCatalogue',
			pageControllerLeftMask: 'CVCataloguePageControllerLeftMask',
			pageControllerRightMask: 'CVCataloguePageControllerRightMask',
			pageControllerLeft: 'CVCataloguePageControllerLeft',
			pageControllerRight: 'CVCataloguePageControllerRight',
			loadIndicator: 'CVCatalogueLoadIndicator',
			thumbnails: 'CVThumbnails'
		},
		bookPath: 'books',
		imagePath: 'preview',
		largeImagePath: 'large',
		thumbnailImagePath: 'thumbnails',
		bookLogoImageExt: '.png',
		listDataFilename: 'list.json',
		logoFilename: 'logos/logo-large.png',
		idPrefix: 'CVCanvas',
		listLeftsideFirstOffset: 800,
		listLeftsideOffset: 150,
		listItemWidth: 520
	},
	vars: {},
	initialize: function( book, appendInto ){
		this.vars.book = book;
		this.vars.step = 0;
		// Build nodes
		if( ! $( this.params.id.container )){
			this.nodes = {};
			( $( appendInto )|| document.body ).appendChild( this.nodes.container = Builder.node( 'div', { id: this.params.id.container },[
				( this.nodes.bookTitle = Builder.node( 'h3', { id: this.params.id.bookTitle })),
				( this.nodes.bookDescription = Builder.node( 'p', { id: this.params.id.bookDescription })),
				( this.nodes.pageList = Builder.node( 'ul', { id: this.params.id.pageList })),
				Builder.node( 'div', { id: this.params.id.listWrapper }, [
					( this.nodes.list = Builder.node( 'ul', { id: this.params.id.list })),
					Builder.node( 'div', { id: this.params.id.pageControllerLeftMask }),
					Builder.node( 'div', { id: this.params.id.pageControllerRightMask }),
					( this.nodes.pageControllerLeft = Builder.node( 'div', { id: this.params.id.pageControllerLeft }).hide()),
					( this.nodes.pageControllerRight = Builder.node( 'div', { id: this.params.id.pageControllerRight }).hide()),
					( this.nodes.loadIndicator = Builder.node( 'div', { id: this.params.id.loadIndicator }, 'Loading...' ).hide())
				]),
				( this.nodes.thumbnails = Builder.node( 'ul', { id: this.params.id.thumbnails }))
			]).hide());
		// Or find nodes
		} else {
			this.nodes = {
				container: $( this.params.id.container ),
				bookTitle: $( this.params.id.bookTitle ),
				bookDescription: $( this.params.id.bookDescription ),
				pageList: $( this.params.id.pageList ),
				list: $( this.params.id.list ),
				pageControllerLeft: $( this.params.id.pageControllerLeft ),
				pageControllerRight: $( this.params.id.pageControllerRight ),
				loadIndicator: $( this.params.id.loadIndicator ),
				thumbnails: $( this.params.id.thumbnails )
			};
		}
		// Observe event into page switchers
		Event.observe( this.nodes.pageControllerLeft, 'click', this.stepBackward.bind( this ));
		Event.observe( this.nodes.pageControllerRight, 'click', this.stepForward.bind( this ));
		// Show book logo
		this.nodes.bookTitle.style.backgroundImage = 'url(\"'+ this.params.bookPath +'/'+ this.vars.book +'/'+ this.params.logoFilename +'\")';
		// Download JSON datafile
		var datasource = this.params.bookPath +'/'+ this.vars.book +'/'+ this.params.listDataFilename;
		this.nodes.loadIndicator.show();
		new Ajax.Request( datasource, {
			method: 'get',
			onSuccess: function( request ){
				var data = eval( '('+ request.responseText +')' );
				this.vars.bookDescription = data.description;
				this.nodes.bookDescription.update(( data.description || '' ) + ( data.released ? ' (' + data.released + decodeURIComponent('%E7%99%BA%E5%A3%B2') +')' : '' ));
				this.listup( data.list );
				this.nodes.loadIndicator.hide();
			}.bind( this ),
			onFailure: function( request ){
				this.nodes.loadIndicator.update( '<strong>Warning!</strong><br />Failed to load database files.<br />HTTP Status Code: '+ request.status );
			}.bind( this )
		});
		this.nodes.list.style.left = this.params.listLeftsideFirstOffset +'px';
		this.nodes.container.show();
	},
	destroy: function(){
		// Clear previewer
		if( this.vars.preview ) this.vars.preview.destroy();
		// Clear data
		this.listdata = [];
		// Clear contents
		this.nodes.bookTitle.style.backgroundImage = '';
		this.nodes.list.update();
		this.nodes.thumbnails.update();
		this.nodes.pageList.update();
		this.nodes.bookDescription.update();
		this.nodes.container.hide();
		// Hide nodes
		this.nodes.pageControllerLeft.hide();
		this.nodes.pageControllerRight.hide();
	},
	listup: function( listdata ){
		// Initialize
		if( listdata ) this.listdata = listdata;
		this.nodes.thumbnails.hide();
		this.nodes.listItems = {};
		this.nodes.listItemIndicator = {};
		this.nodes.thumbnailItems = {};
		this.nodes.pageListItems = {};
		// List up
		this.listdata.each( function( e, i ){
			this.nodes.list.appendChild( this.nodes.listItems[ i ] = Builder.node( 'li', {}, [
				Builder.node( 'img', { id: this.params.idPrefix + i, src: this.params.bookPath +'/'+ this.vars.book +'/'+ this.params.imagePath +'/'+ e.uri }),
				( this.nodes.listItemIndicator[ i ] = Builder.node( 'div', { className: 'loadIndicator' }, Builder.node( 'div', {})).hide())]).setOpacity( 0.2 ));
			this.nodes.thumbnails.appendChild( this.nodes.thumbnailItems[ i ] = Builder.node( 'li', {}, [
				Builder.node( 'img', { src: this.params.bookPath +'/'+ this.vars.book +'/'+ this.params.thumbnailImagePath +'/'+ e.uri }),
				Builder.node( 'p', {}, decodeURIComponent( '%E3%83%9A%E3%83%BC%E3%82%B8%20' )+ e.title )]));
			this.nodes.pageList.appendChild( this.nodes.pageListItems[ i ] = Builder.node( 'li', {}, e.title ));
			Event.observe( this.nodes.thumbnailItems[ i ], 'click', function(){ this.step( i )}.bind( this ));
			Event.observe( this.nodes.pageListItems[ i ], 'click', function(){ this.step( i )}.bind( this ));
		}.bind( this ));
		// Change step
		this.step( this.vars.step || 0 );
		if( this.vars.step > 0 ){
			new Effect.Appear( this.nodes.pageControllerLeft, { duration: 0.5 });
		} else if( this.vars.step < this.listdata.length - 1 ){
			new Effect.Appear( this.nodes.pageControllerRight, { duration: 0.5 });
		}
		new Effect.BlindDown( this.nodes.thumbnails, { duration: 0.5 });
	},
	step: function( step ){
		if( this.vars.preview ) this.vars.preview.destroy();
		// Preload image
		this.vars.previewImage = new Image();
		this.nodes.listItemIndicator[ step ].fadeEffect = new Effect.Appear( this.nodes.listItemIndicator[ step ], { duration: 0.2 });
		// Initiate previewer when finished loading
		Event.observe( this.vars.previewImage, 'load', function(){
			this.vars.preview = new this.Preview( this.params.idPrefix + step, this.vars.previewImage.src );
			if( this.nodes.listItemIndicator[ step ].fadeEffect ) this.nodes.listItemIndicator[ step ].fadeEffect.cancel();
			this.nodes.listItemIndicator[ step ].fadeEffect = new Effect.Fade( this.nodes.listItemIndicator[ step ], { duration: 0.5 });
		}.bind( this ));
		this.vars.previewImage.src = this.params.bookPath +'/'+ this.vars.book +'/'+ this.params.largeImagePath +'/'+ this.listdata[ step ].uri;
		// Slide images
		new Effect.Morph( this.nodes.list, { style: { left: ( this.params.listLeftsideOffset - ( this.params.listItemWidth * step )) +'px' }, duration: 0.5 });
		// Show/hide page switchers
		if( step <= 0 ){
			new Effect.Fade( this.nodes.pageControllerLeft, { duration: 0.5 });
		} else if( step >= this.listdata.length - 1 ){
			new Effect.Fade( this.nodes.pageControllerRight, { duration: 0.5 });
		}
		if( this.vars.step <= 0 && step > 0 ){
			new Effect.Appear( this.nodes.pageControllerLeft, { duration: 0.5 });
		} else if( this.vars.step >= this.listdata.length - 1 && step < this.listdata.length - 1 ){
			new Effect.Appear( this.nodes.pageControllerRight, { duration: 0.5 });
		}
		// Set effects
		if( step != this.vars.step ) new Effect.Opacity( this.nodes.listItems[ this.vars.step ], { duration: 0.5, to: 0.2 });
		new Effect.Appear( this.nodes.listItems[ step ], { duration: 0.5 });
		// Toggle classname of thumbnails/page list
		this.nodes.thumbnailItems[ step ].addClassName( 'active' );
		this.nodes.pageListItems[ step ].addClassName( 'active' );
		if( this.vars.step != step ){
			this.nodes.thumbnailItems[ this.vars.step ].removeClassName( 'active' );
			this.nodes.pageListItems[ this.vars.step ].removeClassName( 'active' );
		}
		this.vars.step = step;
	},
	stepBackward: function(){
		if( this.vars.step <= 0 ) return false;
		this.step( this.vars.step - 1 );
	},
	stepForward: function(){
		if( this.vars.step >= this.listdata.length - 1 ) return false;
		this.step( this.vars.step + 1 );
	},
	Preview: Class.create({
		params: {
			id: { canvas: 'CVCanvas', previewer: 'CVPreviewer', preview: 'CVPreview' },
			previewerOffset: { x: 10, y: 10 },
			previewerSize: { x: 320, y: 320 },
			uri: ''
		},
		vars: {
			previewerPosition: { left: false, top: false }
		},
		initialize: function( canvas, uri, params ){
			if( params ) Object.extend( this.params, params );
			if( uri ) this.params.uri = uri;
			if( canvas ) this.params.id.canvas = canvas;
			// Find/Build nodes
			this.nodes = {
				canvas: $( this.params.id.canvas ),
				previewer: ( $( this.params.id.previewer ) && $( this.params.id.previewer ).hide()) || ( function(){
					document.body.appendChild( previewer = Builder.node( 'div', { id: this.params.id.previewer }, Builder.node( 'div', {}, Builder.node( 'div', {}, Builder.node( 'img', { id: this.params.id.preview })))).hide());
					return previewer;
				}.bind( this ))()
			};
			this.nodes.preview = $( this.params.id.preview );
			Event.observe( this.nodes.canvas, 'mousemove', this.update.bind( this ));
			Event.observe( this.nodes.canvas, 'mouseover', this.show.bind( this ));
			Event.observe( this.nodes.canvas, 'mouseout', this.hide.bind( this ));
			this.nodes.preview.src = this.params.uri;
		},
		destroy: function(){
			this.nodes.previewer.hide();
			Event.stopObserving( this.nodes.canvas, 'mousemove', this.update );
			Event.stopObserving( this.nodes.canvas, 'mouseover', this.show );
			Event.stopObserving( this.nodes.canvas, 'mouseout', this.hide );
		},
		update: function( e ){
			if( ! e ) e = window.event || event;
			// Calculate previewer position
			if( ! this.vars.previewerPosition.left &&( e.offsetX || e.layerX )/ this.nodes.canvas.width > 0.6 ) this.vars.previewerPosition.left = true;
			if( ! this.vars.previewerPosition.top &&( e.offsetY || e.layerY )/ this.nodes.canvas.height > 0.6 ) this.vars.previewerPosition.top = true;
			if( this.vars.previewerPosition.left &&( e.offsetX || e.layerX )/ this.nodes.canvas.width < 0.4 ) this.vars.previewerPosition.left = false;
			if( this.vars.previewerPosition.top &&( e.offsetY || e.layerY )/ this.nodes.canvas.height < 0.4 ) this.vars.previewerPosition.top = false;
			// if( this.nodes.previewer.morphEffect ) this.nodes.previewer.morphEffect.cancel();
			// this.nodes.previewer.morphEffect = new Effect.Morph( this.nodes.previewer, { style: {
			// Set previewer position
			this.nodes.previewer.setStyle({
				left: ( this.vars.previewerPosition.left ?
					Event.pointerX( e ) - this.nodes.previewer.offsetWidth - this.params.previewerOffset.x :
					Event.pointerX( e ) + this.params.previewerOffset.x ) +'px',
				top : ( this.vars.previewerPosition.top ?
					Event.pointerY( e ) - this.nodes.previewer.offsetHeight - this.params.previewerOffset.y :
					Event.pointerY( e ) + this.params.previewerOffset.y ) +'px'
			});
			// }, duration: 0.1 });
			// Move preview
			this.nodes.preview.setStyle({
				left: 0 - Math.floor(( e.offsetX || e.layerX ) * this.nodes.preview.width / this.nodes.canvas.width - this.params.previewerSize.x / 2 )+'px',
				top : 0 - Math.floor(( e.offsetY || e.layerY ) * this.nodes.preview.height / this.nodes.canvas.height - this.params.previewerSize.y / 2 )+'px'
			});
			this.nodes.previewer.className = 'position-'+( this.vars.previewerPosition.left ? 'left' : 'right' )+'-'+( this.vars.previewerPosition.top ? 'top' : 'bottom' );
		},
		show: function(){
			if( this.nodes.previewer.fadeEffect ) this.nodes.previewer.fadeEffect.cancel();
			this.nodes.previewer.fadeEffect = new Effect.Appear( this.nodes.previewer, { duration: 0.2 });
			if( this.nodes.canvas.fadeEffect ) this.nodes.canvas.fadeEffect.cancel();
			this.nodes.canvas.fadeEffect = new Effect.Fade( this.nodes.canvas, { duration: 0.5, to: 0.8 });
		},
		hide: function(){
			if( this.nodes.previewer.fadeEffect ) this.nodes.previewer.fadeEffect.cancel();
			this.nodes.previewer.fadeEffect = new Effect.Fade( this.nodes.previewer, { duration: 0.5 });
			if( this.nodes.canvas.fadeEffect ) this.nodes.canvas.fadeEffect.cancel();
			this.nodes.canvas.fadeEffect = new Effect.Appear( this.nodes.canvas, { duration: 0.5 });
		}
	})
});
