/*jslint devel: true, browser: true, sloppy: true, eqeq: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */

if (typeof(ID) === 'undefined') { throw "The ID class is required for ProduktOversigt.js"; }
if (typeof(Effect) === 'undefined') { throw "The Effect class (Scriptaculous) is required for ProduktOversigt.js"; }

Effect.SmoothSliderSetValue = Class.create(Effect.Base, {

	initialize: function(element, options) {
		this.element = element;
		if (!this.element) { throw Effect._elementDoesNotExistError; }
		var options = Object.extend({
			from: 0,
			to: 0,
			duration: 1.0
		}, options || { });
		this.factor = 1/(options.to-options.from);
		this.start(options);
	},

	update: function(position) {
		this.element.setValue(position);
	}
});


if (ID === undefined) { var ID = {}; }

//-------------------------------------------------------------------------------------------------------------

ID.ProduktOversigt = Class.create(ID,{
	_identity: 'ID.ProduktOversigt',

	initialize: function(elementId,itemArr,options) {
		this.containerElement = $(elementId).up('.produktoversigt');

		this.options = Object.extend({
			'itemContainer': { },
			'menubar': {
				'buttons': {
					'filter': ['none','onoffer','shopable']
				}
			}
		},options || {});

		this.menubar	= new ID.ProduktOversigtMenubar('produktoversigt_menubar',this._onMenuBarChanged.bind(this));
		this.items		= new ID.ItemContainer(itemArr,this._onItemsLoaded.bind(this),this.options.itemContainer);
		this.views		= new ID.ProduktOversigtViews(elementId, {'menubar': this.menubar});
		
		this.containerElement.observe('click',this._onClick.bindAsEventListener(this));

		this._info('Initialized');
	},

	_onItemsLoaded: function() {
		$('produktoversigt_dataloading').hide();
		
		//this._info('Items loaded');

		// Add filter buttons
		this.options.menubar.buttons.filter.each(function(filter){
			var count = this.items.count(filter);
			if (count) {
				this.menubar.addButton('filter', filter, '('+count+')');
			}
		},this);
		
		//this._info('Filters added');
		
		try {
			this.menubar.pickDefaults();
		}
		catch(ex){
			this._error(ex);
		}
	},

	_onMenuBarChanged: function() {
		//this._info('Menu bar changed');
		this._render();
	},

	_render: function(show) {
		if (show===undefined) { show=true; }
		var viewName = this._renderView(
			this.menubar.getSelectedButton('filter'),
			'vertical'
			/* this.menubar.getSelectedButton('mode') */
		);
		if (show) {
			this.views.show(viewName);
		}
	},

	_renderView: function(filter, mode) {
		var viewName = filter+'_'+mode;
		if (!this.views.has(viewName)) {
			var view = this.views.add(viewName, mode);
			var filteredItems = this.items.filter(filter);
			view.setItems(filteredItems);
		}
		// TODO: Consider returning the view - not the name of it
		return viewName;
	},

	_onClick: function(evt) {
		var element = Event.element(evt);
		if (!element.hasClassName('action')) {
			if (!(element = element.up('.action'))) {
				this._debug('Ignoring click on non-action element');
				return false;
			}
		}
		var parmsArr = element.identify().split('_');
		if (this._handleAction(element,parmsArr)) {
			Event.stop(evt);
		} else {
			this._warn('Unhandled or failed action');
		}
	},

	_handleAction: function(element,parmsArr) {
		this._info('Action: '+element.identify());
		switch (parmsArr[0]) {
			/* case 'produktoversigtmode': */
			case 'produktoversigtfilter':
				return this.menubar.handleAction(element,parmsArr);

			case 'produktoversigtprint':
				window.print();
				return true;

			case 'produktoversigtitem':
				return this.items.handleAction(element,parmsArr);

			case 'sliderbutton':
				return this.views.handleAction(element,parmsArr);
		}
		return false;
	}
});

//-------------------------------------------------------------------------------------------------------------

ID.ItemContainer = Class.create(ID,{
	_identity: 'ID.ItemContainer',

	initialize: function(itemsArr,onItemsLoadedCallback, options) {
		this.onItemsLoadedCallback = onItemsLoadedCallback;
		this.options = Object.extend({
			forceProfile:	undefined,
			defaultProfile:	'wide',
			profileGroups: {
			},
			itemProfiles: {
				'tall': {
					metaImageWidth:		112,
					metaImageHeight:	112,
					metaElementWidth:	120,
					metaElementHeight:	174,
					metaElementSlack:	11,
					metaMaxShortTitleLength:	14,
					metaTitleShortening:		'…' // &ellipsis;
				},
				'wide': {
					metaImageWidth:		224,
					metaImageHeight:	112,
					metaElementWidth:	232,
					metaElementHeight:	174,
					metaElementSlack:	39,
					metaMaxShortTitleLength:	30,
					metaTitleShortening:		'…' // &ellipsis;
				}
			},
			templates: {
				'imageURL': new Template('/files/produkter/#{group}/#{metaImageWidth}x#{metaImageHeight}/#{id}-001.jpg'),
				'errorImageURL': new Template('/files/system/gfx/fotopaavej/fotopaavej-#{metaImageWidth}.jpg')
			}
		},options || {});

		// Weed out unwanted items
		this.itemsArr = [];
		itemsArr.each(function(item){
			if ((item.id === undefined)) {
				this._warn('Quietly ignoring item without id: '+item.inspect());
				return;
			}

			// Extend with profile information
			item = this._getProfiledItem(item);

			// Do NOT add metaIndex here since we might be weeding out the array later on
			this.itemsArr.push(item);
		},this);

		this._log('All items extended');

		this._loadItemImages((function(items) {

			this.itemsArr = items;

			if ( this.options.informedItems === undefined || this.options.informedItems === false )
			{
				// Call service
				var ids = this.itemsArr.pluck('id').sort().uniq();
				if (ids.length > 0) {
					var idStr = ids.join(',');
					this._info('Requesting async metadata for: '+idStr);
					dk.ide.webservice.RelatedProducts.GetProductInformation(idStr,this._onItemsLoaded.bind(this));
				} else {
					// Make sure callback gets called, even is no data is available
					this._onItemsLoaded.bind(this).defer([]);
				}
			}
			else
			{
				this._info('Items are already populated with metadata');
				this._onItemsLoaded.bind(this).defer(this.itemsArr);
			}

		}).bind(this));

	},

	_loadItemImages: function(callback) {
		var itemsWithImages = [];

		var imagesToGo = this.itemsArr.size();
		this._log('Attempting to load images ('+imagesToGo+')');

		var checkFinished = (function() {
			if (imagesToGo <= 0) {
				this._log('Image load finished');
				callback(itemsWithImages);
			} else {
				this._log(imagesToGo+' images to go');
			}
		}).bind(this);

		this.itemsArr.each(function(item) {
			//item.image = new Element('img', {'src': item.metaImageURL})
			item.image = new Element('img')
				.observe('abort', (function(evt) {
					this._log('Image load aborted');
					imagesToGo--;
					checkFinished();
				}).bind(this))
				.observe('load', (function(evt) {
					this._log('Image loaded');
					itemsWithImages.push(item);
					imagesToGo--;
					checkFinished();
				}).bind(this))
				.observe('error', (function(evt) {
					this._log('Image load failed');
					(function() {
						dk.ide.webservice.ImageErrorLogging.Log(item.metaImageURL, document.location.href, false);	
					}).delay(5);
					imagesToGo--;
					checkFinished();
				}).bind(this));

			// Events are observed - set the src attribute now:
			item.image.src = item.metaImageURL;
				
		}, this);

/*
		// Periodical checker
		new PeriodicalExecuter((function(pe) {
			var toGo = this.itemsArr.size();
			this.itemsArr.each(function(item) {
				// IE:
				this._log(item.image.complete);

			}, this);
			if (!toGo) {
				this._log('Periodical executer determined that image load is completed');
			}

			pe.stop();
		}).bind(this), 0);
*/
	},

	_escapeHTML: function(str) {
		if (str===undefined) { return 'undefined'; }
		return str
			.replace('&','&amp;')
			.replace('<','&lt;')
			.replace('>','&gt;');
	},

	_getProfiledItem: function(item) {
		// Find profile for item
		item.metaType = undefined;
		if (item.group === undefined) {
			this._error('Item #'+item.id+' has no "group" property');
			item.metaType = this.options.defaultProfile;
		} else {
			// Iterate through defined profileGroups
			$H(this.options.profileGroups).each(function(pair){
				if ((item.metaType === undefined) && (pair.value.indexOf(item.group) !== -1)) {
					item.metaType = pair.key;
					//this._debug('Item ['+item.id+'/'+item.group+'] was listed in the group "'+item.metaType+'"');
				}
			},this);
			if (item.metaType === undefined) {
				item.metaType = this.options.defaultProfile;
				this._debug('Item ['+item.id+'/'+item.group+'] was not listed in any group - defaulting to "'+item.metaType+'"');
			}
		}

		if ( item.pageurl.indexOf('/') != 0 ) 
		{
			item.pageurl = '/' + item.pageurl; 
		}

		// Merge with profile data (overwrite)
		item = Object.extend(item, this.options.itemProfiles[item.metaType]);

		// Get image URLs
		item.metaImageURL = this.options.templates.imageURL.evaluate(item);
		item.metaErrorImageURL = this.options.templates.errorImageURL.evaluate(item);

		return item;
	},

	_getEnrichedItem: function(item, data) {
		// Merge with data (now overwrite)
		item = Object.extend(Object.clone(data),item);

		// Provide raw and formatted prices no matter what format amounts arrive in
		item.metaPrice = this._getPriceFromString(item.price);
		item.metaSavings = this._getPriceFromString(item.savings);
		item.metaFormattedPrice = this._getFormattedPrice(item.metaPrice);
		item.metaFormattedSavings = this._getFormattedPrice(item.metaSavings);

		// Add custom data
		item.metaClassNames = 'productblock_'+item.metaType;
		if (item.onoffer) { item.metaClassNames += ' productblock_onoffer'; }
		if (item.shopable) { item.metaClassNames += ' productblock_shopable'; }
		if (item.outlet) { item.metaClassNames += ' productblock_outlet'; }
		if (item.metaSavings > 0) { item.metaClassNames += ' productblock_discounted'; }

		item.metaEnriched = true;

		// Add titles
		item.metaTitle = item.name;
		item.metaTitleEscaped = escape(item.metaTitle);
		item.metaTitleHTML = this._escapeHTML(item.metaTitle);

		item.metaTitleShort = item.metaTitle;
		if (item.metaTitleShort.length > item.metaMaxShortTitleLength) {
			item.metaTitleShort = item.metaTitleShort.substr(0, item.metaMaxShortTitleLength - item.metaTitleShortening.length)+item.metaTitleShortening;
		}
		item.metaTitleShortEscaped = escape(item.metaTitleShort);
		item.metaTitleShortHTML = this._escapeHTML(item.metaTitleShort);

		return item;
	},

	_getFormattedPrice: function(price) {
		var priceStr = price.toString();
		var re = new RegExp(/(\d)(\d{3})(\.|$)/);
		while (re.exec(priceStr) !== null) { priceStr = priceStr.replace(re,'$1.$2$3'); }
		// TODO: Make this less stupid:
		if (priceStr.match(/,/)===null) {
			priceStr += ',-';
		}
		return priceStr;
	},

	_getPriceFromString: function(priceStr) {
		var price = 0;
		if (priceStr != '') {
			if (priceStr.match(/^\d+(\.\d+)?$/) !== null) {
				price = parseFloat(priceStr);
			} else if (priceStr.match(/^[\d+\.]+(|\,-|\,\d+)$/)) {
				price = parseFloat(priceStr
					.replace(/\./,'')
					.replace(/,-$/,'')
					.replace(/,/,'.')
				);
			} else {
				this._warn('Unparsable price string: "'+priceStr+'"');
			}
		}
		return price;
	},

	_onItemsLoaded: function(data) {
		// Index received data by id
		var dataArr = {};
		data.each(function(row){
			dataArr[row.id] = row;
		},this);

		// Iterate through existing items (and possibly enrich)
		var mergedItems = [];
		this.itemsArr.each(function(item){
			if (dataArr[item.id] === undefined) {
				this._warn('No async meta data received for #'+item.id);
				return;
			}
			
			// Ignore items with zero price to avoid displaying invalid amounts
			// Case 26035
			if (dataArr[item.id].price === '0,-') {
				this._warn('Ignoring item #'+item.id+' with zero price');
				return;
			}
			
			mergedItems.push(this._getEnrichedItem(item, dataArr[item.id]));
		},this);

		// Overwrite itemsArr with newly generated array
		this.itemsArr = mergedItems;

		// Iterate through new (final) array and add metaIndex
		var i = 0;
		this.itemsArr.each(function(item){
			item.metaIndex = i++;
		},this);

		// Do callback
		this.onItemsLoadedCallback();
	},

	count: function(filter) {
		var count = 0;
		this.itemsArr.each(function(item){
			if ((filter === 'none') || (item[filter] === true)) { count++; }
		},this);
		return count;
	},

	filter: function(filter) {
		// Build a fresh array
		var items = [];
		this.itemsArr.each(function(item){
			if ((filter === 'none') || (item[filter] === true)) {
				items.push(item);
			}
		},this);
		return items;
	},

	handleAction: function(element,parmsArr) {
		switch (parmsArr[0]) {
			case 'produktoversigtitem':
				// Ex.: "produktoversigtitem_none_horizontal_2"
				return this._handleItemClick(parmsArr[3]);
		}
		return false;
	},

	_handleItemClick: function(itemIndex) {
		var item = this.itemsArr[itemIndex];
		if ((item === undefined) || (item === null)) {
			this._warn('Clicked on non-existing item (itemIndex='+itemIndex+')');
			return false;
		}
		if ((item.pageurl === undefined) || (item.pageurl === null) || (item.pageurl == '')) {
			this._warn('Clicked on item without pageurl');
			return false;
		}
		this._log('Setting location to '+item.pageurl);
		document.location.href = item.pageurl;
		return true;
	}
});

//-------------------------------------------------------------------------------------------------------------

ID.ProduktOversigtMenubar = Class.create(ID,{
	_identity: 'ID.ProduktOversigtMenubar',

	initialize: function(elementId,onChangeCallback) {
		this.onChangeCallback = onChangeCallback;
		this.element = $(elementId);
		this.cookieExpiry = 1; // Days
		this.buttons = {};
	},

	pickDefaults: function() {
		Object.keys(this.buttons).each(function(type){

			// Special case triggered by adding #tilbud to url:
			if ((type=='filter') && (window.location.hash.match('#tilbud'))) {
				this._pickButton(type, 'onoffer', false); // Do NOT set cookie
			}
			// Try cookie and fall back to first available key
			else if (!this._pickButton(type, CodeCompany.Cookie.read('produktoversigt_'+type))) {
				var keys = Object.keys(this.buttons[type].available);
				this._pickButton(type, keys[0]);
			}
		},this);
		this._callback();
	},

	_callback: function() {
		this.onChangeCallback();
	},

	addButton: function(type, name, annotationStr) {
		// Look for element
		var elementId = 'produktoversigt'+type+'_'+name;
		var element = $(elementId);
		if (element === null) {
			this._error('Button element not found: '+elementId);
			return false;
		}

		// Make sure type entry exists
		if (this.buttons[type] === undefined) {
			this.buttons[type] = {
				'value':		undefined,
				'available':	{}
			};
		}

		// Add button
		this.buttons[type].available[name] = {
			'element': element,
			'listelement': element.up('li')
		};

		// Mark it enabled
		this.buttons[type].available[name].listelement.addClassName('enabled');

		// Annotate?
		if (annotationStr !== undefined) {
			this.annotateButton(type, name, annotationStr);
		}

		//this._info('Added button '+type+'/'+name);
	},

	hasButton: function(type,name) {
		return (this.buttons[type] !== undefined) && (this.buttons[type].available[name] !== undefined);
	},

	annotateButton: function(type, name, str) {
		if (!this.hasButton(type, name)) {
			throw 'Tried to annotate non-existing button';
		}
		var button = this.buttons[type].available[name];
		if (button.originalLabel === undefined) {
			button.originalLabel = button.element.innerHTML;
		}
		button.element.update(button.originalLabel+' '+str);
	},

	clickButton: function(type,name,setCookie) {
		this._pickButton(type,name,setCookie);
		this._callback();
	},

	_pickButton: function(type, name, setCookie) {
		if (setCookie === undefined) {
			setCookie = true;
		}

		if (!this.hasButton(type,name)) {
			this._warn('Unable to pick unlisted button: "'+type+'"/"'+name+'"');
			return false;
		}

		// Mark button element selected (and unselect others)
		var li = this.buttons[type].available[name].listelement;
		li.siblings().invoke('removeClassName','selected');
		li.addClassName('selected');

		// Set cookie
		if (setCookie) {
			try { CodeCompany.Cookie.write('produktoversigt_'+type,name,this.cookieExpiry); }
			catch (e) { this._error('Unable to set mode cookie'); }
		}

		this.buttons[type].value = name;

		return true;
	},

	getSelectedButton: function(filter) {
		if (this.buttons[filter] === undefined) {
			return undefined;
		}
		return this.buttons[filter].value;
	},

	handleAction: function(element,parmsArr) {
		switch (parmsArr[0]) {
			case 'produktoversigtfilter':
				if (this._pickButton('filter',parmsArr[1])) {
					this._callback();
				}
				return true;
		}
		return false;
	}
});

//-------------------------------------------------------------------------------------------------------------

ID.ProduktOversigtViews = Class.create(ID,{
	_identity: 'ID.ProduktOversigtViews',

	initialize: function(elementId, options) {
		this._options = Object.extend({
		}, options || {});

		this.element = $(elementId);
		this.views = new Hash();
		this.currentView = undefined;
		
		this.loadingContainer = $('produktoversigt_dataloading');
		this.loadingContainer.show();
	},

	has: function(viewName) {
		return (this.views.get(viewName) !== undefined);
	},

	show: function(viewName) {
		this.loadingContainer.hide();
		this.currentView = this.views.get(viewName);
		this.views.each(function(pair){
			if (pair.key == viewName) {
				pair.value.show();
			}
			else { pair.value.hide(); }
		},this);
	},

	add: function(viewName,mode) {
		this._info('Adding view "'+viewName+'"');
		var element = new Element('div');
		element.addClassName('produktoversigt_view');

		var view;
		switch (mode) {
			case 'vertical':
				view = new ID.ProduktOversigtViewVertical(element,viewName);
				break;

			default:
				this._error('Unknown view mode "'+mode+'"');
				return undefined;
		}

		this.views.set(viewName,view);
		this.element.insert({'bottom':element});

		this._info('Added view "'+viewName+'"');

		return view;
	},

	handleAction: function(element,parmsArr) {
		if (this.currentView) {
			return this.currentView.handleAction(element,parmsArr);
		}
		return false;
	}
});

//-------------------------------------------------------------------------------------------------------------

ID.ProduktOversigtView = Class.create(ID,{
	toString: function() { return 'ID.ProduktOversigtView('+this.mode+')'; },

	initialize: function(element,viewName,mode) {
		this.element = element;
		this.viewName = viewName;
		this.rendered = false;
		this.mode = mode;

		this.classNames = this.mode;

		this.itemsMarkup = '';
		this.blockStyle = '';
		this.containerStyle = '';
		this.bottomMarkup = '';

		this.options = {
			'itemsPerColumn':	3
		};

		this.templates = {
			'container':	new Template(
								'<div class="productoverview #{classNames}">'+
									'<div class="productblockcontainer" style="#{containerStyle}">'+
										'<div class="productblocks" style="#{blockStyle}">'+
											'#{itemsMarkup}'+
											'<div class="clear"></div>'+
										'</div>'+
										'<div class="clear"></div>'+
									'</div>'+
									'#{bottomMarkup}'+
								'</div>'
							),
			'item':			new Template(
								'<div class="productblockwrapper action" id="produktoversigtitem_#{metaViewName}_#{metaIndex}">'+
									'<div class="productblock #{metaClassNames}">'+
										'<div class="productblocklayers">'+
											// On-hover frame
											'<div class="framelayer">'+
												'<div class="text">'+
													'<a href="#{pageurl}">Se mere her</a>'+
												'</div>'+
											'</div>'+
											// Actual content (image+text)
											'<div class="contentlayer">'+
												'<div class="tilbud"></div>'+
												'<div class="outlet"></div>'+
												'<div class="shopable">Køb online</div>'+
												'<div class="image">'+
													'<a href="#{pageurl}">'+
														'<img '+
															'src="#{metaImageURL}" '+
															'onerror="this.src=\'#{metaErrorImageURL}\'" '+
															'style="width:#{metaImageWidth}px;height:#{metaImageHeight}px;" '+
															'alt="#{metaTitleHTML} '+
															'[#{group}/#{id}]" '+
														'/>'+
													'</a>'+
												'</div>'+
												'<div class="text">'+
													'<h2 class="passivecoloredlink">'+
														'<a href="#">&raquo; #{metaTitleShortHTML}</a>'+
													'</h2>'+
													'<div class="price">'+
														'<a href="#{pageurl}">'+
															'<span class="savings savings_label">Nu </span>'+
															'<span class="price">Kr. #{metaFormattedPrice}</span><br/>'+
															'<span class="savings">'+
																'<span class="savings_label">SPAR</span> '+
																'<span class="savings_amount">#{metaFormattedSavings}</span>'+
																'<br/>'+
															'</span>'+
															'<span class="maal">#{maal}</span>'+
														'</a>'+	
													'</div>'+
												'</div>'+
											'</div>'+
										'</div>'+
									'</div>'+
								'</div>'
							)
		};		
	},

	setItems: function(itemsArr) {
		this.itemsArr = itemsArr;

		// Iterate through items and determine what types we have
		this.itemTypes = [];
		this.itemsArr.each(function(item){
			if (this.itemTypes.indexOf(item.metaType)==-1) { this.itemTypes.push(item.metaType); }
		},this);
		this._info('This view contains '+this.itemsArr.size()+' items of these types: '+this.itemTypes.join(', '));

		this.itemsType = 'mixed';
		if (this.itemTypes.size()==1) {
			this.itemsType = this.itemTypes[0];
		}

		this.classNames += ' productoverview_'+this.itemsType;
	},

	setMode: function(mode) {
		this.mode = mode;
	},

	hide: function() {
		this.element.hide();
	},

	_render: function() {
		this.element.update(this.templates.container.evaluate(this));
		this.rendered = true;
		this._postRender.bind(this).defer();
	},

	_renderEmpty: function() {
		this.element.update('Der er ingen produkter der matcher din søgning.');
		this.rendered = true;
		this._postRender.bind(this).defer();
	},
	
	_postRender: function() {
		new HoverSiblings([this.element.identify()],{siblingElementSelector:".productblock"});
		document.fire("custom:contentrefresh");
		document.fire('custom:systemfontreplace');
		this._info('Rendered');
	},
	
	show: function() {
		if (!this.rendered) {
			if (this.itemsArr.size()) {
				this._render();
			} else {
				this._renderEmpty();
			}
		}
		this.element.show();
		this._postShow.bind(this).defer();
	},

	_postShow: function() {
		document.fire("custom:contentrefresh");
	}
});

//-------------------------------------------------------------------------------------------------------------

ID.ProduktOversigtViewVertical = Class.create(ID.ProduktOversigtView,{
	_identity: 'ID.ProduktOversigtViewVertical',

	initialize: function($super,element,viewName) {
		$super(element,viewName,'vertical');
	},

	_render: function($super) {
		this.itemsArr.each(function(item){
			item.metaViewName = this.viewName;
			this.itemsMarkup += this.templates.item.evaluate(item);
		},this);

		/* NOT setting this breaks view completely for IE7 (WTF?) */
		this.blockStyle = 'width:850px';

		$super();
	}
});

//-------------------------------------------------------------------------------------------------------------

// Anvendes i udvalgt produktoversigt:
ID.ProduktOversigtPreOp = new Class.create(ID,{
	_identity: 'ID.ProduktOversigtPreOp',
	
	initialize: function(elementId,itemIds,options)
	{
		this.elementId = elementId;
		this.options = options;
		
		// Lookup extended product information
		dk.ide.webservice.RelatedProducts.GetProductInformationExtended(servicetoken, itemIds, -1, this.onItemsReceived.bind(this));
	},
	
	onItemsReceived: function(data)
	{
		this._log('Received extended information');
		new ID.ProduktOversigt(this.elementId, data, this.options);
	}
});


