tx_mdcalculator_pi1 = {};

tx_mdcalculator_pi1.Calculator = Class.create({
	
	config: {},
		
	totalprice: 0,
	savings: 0,
	
	// hold ALL nodes, formats and attributes with a unique key to access them all time 
	nodes: {},
	formats: {},
	attributes: {},
	
	priceconfig: {
		format: null,
		attributes: [],
		quantity: 1,
		pages: null,
		// default to Germany
		// TODO: Set via Typo3
		shippingzone: 4
	},	
	
	initialize: function(ident, config) {
			
		this.ident = ident;
		this.config = config;
		
		//console.log(this.config);
		
		this.config.currencySymbol = '&#8364';
		
		this.container = $(ident + '_main');
		this.calculator = $(ident + '_calculator');
		this.resultCheap = $(ident + '_cheapprice');
		this.resultBase = $(ident + '_baseprice');
		this.savingRow = $(ident + '_savingrow');
		this.resultSaving = $(ident + '_saving');
		
		var quantitySelector = this.renderQuantitySelector(this.config.minQuantity, this.config.maxQuantity);
		var countrySelector = this.renderCountrySelector();
		
		this.calculator.insert(this.renderSelector(config, 0));
		
		//if(this.config.quantityselector) {
			this.calculator.insert(quantitySelector);
		//}
		if(this.config.countries) {
			this.calculator.insert(countrySelector);
		}
		
		if(!config.childNodes && config.formats.size() == 1) {
			this.onFormatSelect(config.formats[0], config);
		}
		
		this.priceconfig.shippingzone = 6;
		this.updatePrice();
		
	},
	
	// calculate price for the selected configuration
	updatePrice: function() {

		// reset prices
		this.savings = 0;
		this.totalprice = 0;
		
		if(this.priceconfig.format) {
			
			// add formats base, offer or block price
			var price = this.getFormatPrice(this.priceconfig.format, this.priceconfig.quantity);		
			this.addToPrice(price);

			// add price for additional pages
			if(this.priceconfig.format.variablepages && this.priceconfig.format.pageSelect) {
				this.totalprice += ((parseInt(this.priceconfig.format.pageSelect.value) - this.priceconfig.format.minpages) / this.priceconfig.format.steppages) * this.priceconfig.format.priceperstep; 
			}
			
			// add price for each attribute
			this.priceconfig.attributes.each(function(ident) {
				var price = this.getAttributePrice(ident, (this.priceconfig.format.pageSelect ? this.priceconfig.format.pageSelect.value : null));
				this.addToPrice(price);
			}.bind(this));
			
			// multiply price with quantitity
			this.totalprice *= this.priceconfig.quantity;
			this.savings *= this.priceconfig.quantity;
			
			if(this.priceconfig.quantity > 1) {
				this.quantityItemBaseprice.show();
				this.quantityItemBaseprice.update(' x ' + this.priceconfig.quantity + ' = ' + this.formatNumber(this.totalprice, 2, true) + ' ' + this.config.currencySymbol);
			} else {
				this.quantityItemBaseprice.hide();
			}
			
			
			if(this.priceconfig.format.shipping) {
				var shipping = this.priceconfig.format.shipping.find(function(shipping) {
					return (shipping.zone_uid == parseInt(this.priceconfig.shippingzone)) ? true : false;
				}.bind(this));
				this.shippingItemBaseprice.show();
				this.shippingItemBaseprice.update('+ ' + this.formatNumber(parseFloat(shipping.amount), 2, true) + ' ' + this.config.currencySymbol);
				if(shipping.free_shipping && this.totalprice >= parseFloat(shipping.free_shipping_min_amount)) {
					this.priceconfig.shipping = 0;
					this.savings += shipping ? parseFloat(shipping.amount) : 0;
					this.shippingItemBaseprice.addClassName('strike');
					this.shippingItemCheapprice.show();
					this.shippingItemCheapprice.update('+ ' + this.formatNumber(0, 2, true) + ' ' + this.config.currencySymbol);
				} else {
					this.priceconfig.shipping = parseFloat(shipping.amount);
					this.shippingItemCheapprice.hide();
					this.shippingItemBaseprice.removeClassName('strike');
					this.totalprice += shipping ? parseFloat(shipping.amount) : 0;
				} 
			} else {
				this.shippingItemBaseprice.hide();
			}
			
			this.resultBase.update(this.formatAsPrice(this.totalprice));
			this.resultBase.show();
			if(this.savings > 0) {
				this.savingRow.show();
				this.resultSaving.update(this.formatAsPrice(this.savings));				
				var baseprice = this.totalprice + this.savings;
				this.resultBase.update(this.formatAsPrice(baseprice))
				this.resultCheap.show();
				this.resultCheap.update(this.formatAsPrice(this.totalprice));
				this.resultBase.addClassName('strike');
			} else {
				this.savingRow.hide();
				this.resultCheap.hide();
				this.resultBase.removeClassName('strike');				
			}
							
		} else {
			this.shippingItemBaseprice.hide();
			this.shippingItemCheapprice.hide();
			this.quantityItemBaseprice.hide();
			this.resultCheap.hide();
			this.resultBase.hide();
			this.savingRow.hide();
		}
	},
	
	addToPrice: function(price) {
		if(!isNaN(price.cheap)) {
			this.totalprice += price.cheap;
			this.savings += (price.base - price.cheap);
		} else {
			this.totalprice += price.base;
		}		
	},
	
	getAttributePrice: function(ident, pages) {
		var price = {};
		
		var attribute = this.attributes[ident];
		
		if(attribute.isFree) {
			price.cheap = 0;
			price.base = parseFloat(attribute.price); 
		} else if(attribute.offeractive) {
			price.cheap = parseFloat(attribute.offerprice);
			price.base = parseFloat(attribute.price); 
		} else {
			price.base = parseFloat(attribute.price);
		}
		
		if(attribute.page_multiply && pages) {
			price.base *= pages;
			if(price.cheap) {
				price.cheap *= pages;
				attribute.itemCheapprice.update('+ ' + this.formatNumber(parseFloat(price.cheap), 2, true) + ' ' + this.config.currencySymbol);
			}
			attribute.itemBaseprice.update('+ ' + this.formatNumber(parseFloat(price.base), 2, true) + ' ' + this.config.currencySymbol);
		}
		return price;
	},
	
	getFormatPrice: function(format, quantity) {
		var price = {};
		price.base = parseFloat(format.baseprice);
		if(format.offeractive) {
			var cheap = parseFloat(format.offerprice);
		} 
		if(format.blockpricing) {
			var bprice = this.getBlockPrice(format.blockpricing, quantity);
			if(bprice <= 0) {
				var cheap = price.base + bprice;
			} else {
				var cheap = bprice;
			}
		}
		if(cheap && cheap < price.base) {
			price.cheap = cheap;
		}
		return price;
	},
	
	getBlockPrice: function(blockpricing, quantity) {
		var price;
		Object.keys(blockpricing).each(function(rangeString) {
			var range = rangeString.split(':');
			var min = parseInt(range[0]);
			var max = parseInt(range[1]);
			if(min != Number.NaN && max != Number.NaN && parseInt(quantity) >= min && parseInt(quantity) <= max) {
				price = blockpricing[rangeString];
			} else if(min != Number.NaN && !max && parseInt(quantity) >= min) {
				price = blockpricing[rangeString];
			}
		}.bind(this));
		return parseFloat(price);		
	},
	
	// TODO: still very long function - make it shorter
	renderSelector: function(node, depth) {

		// add node to global node object
		this.nodes['childNode_' + node.uid] = node;
		node.container = new Element('div', {'class': 'row'});
		
		if(depth > 0) {	node.container.hide(); }
		
		// add label
		node.container.insert(this.getSelectLabel(depth));	

		// render price items if node contains formats (because EVERY format has a price)
		if(node.formats) {
			node.itemCheapprice = new Element('span', {'class': 'item_cheapprice'}).hide();
			node.container.insert(node.itemCheapprice);			
			node.itemBaseprice = new Element('span', {'class': 'item_baseprice'}).hide();
			node.container.insert(node.itemBaseprice);
		}
		
		if(node.childNodes || node.formats.size() > 1) {
			// add select element
			var select = new Element('select');
			node.container.insert(select);
			node.container.select = select;
			select.observe('change', this.onNodeOrFormatChange.bind(this, node));
			
			// insert options into select element
			var options = this.getNodeOptions(node);
			Object.keys(options).each(function(key) {
				select.insert(options[key]);
			}.bind(this));
		} else {
			var select = new Element('span', {'class': 'format'}).update(node.formats[0].title);
			node.container.insert(select);
		}
		
		// add format stuff like attributes and pageselect
		if(node.formats) {			
			node.formats.each(function(format) {
				this.formats['format_' + format.uid] = format;
				format.container = new Element('div', {'class': 'format'}).hide();
				format.parentNode = node;
				node.container.insert(format.container);
				
				if(format.images) {
					format.container.insert(this.renderImages(format.images));
				}
				
				// add page selector
				if(format.variablepages) {
					format.container.insert(this.renderPageSelect(format));
				}
				
				// add attributes
				if(format.attributes) {
					var attributes = this.renderAttributes(format.attributes, format.pageSelect, format.uid);
					format.container.insert(attributes);
				}
			}.bind(this));			
		}		
		
		// add child nodes recursively
		if(node.childNodes) {
			node.childNodes.each(function(childNode) {
				node.container.insert(this.renderSelector(childNode, depth + 1));
			}.bind(this));
		}
		
		this.onNodeOrFormatChange(node, {target: select});

		return node.container;		
	},
	
	getNodeOptions: function(node) {
		var options = {};

		// insert blank option
		//options['novalue'] = new Element('option', {'value': 'novalue'});;
		//options['novalue'].update('-');
		
		// add formats
		if(node.formats) {
			node.formats.each(function(format) {
				options['format_' + format.uid] = new Element('option', {'value': 'format_' + format.uid});
				options['format_' + format.uid].update(format.title);				
			}.bind(this));
		}
		
		// add additional child nodes recursively
		if(node.childNodes) {
			node.childNodes.each(function(childNode) {
				options['childNode_' + childNode.uid] = new Element('option', {'value': 'childNode_' +  childNode.uid});
				options['childNode_' + childNode.uid].update(childNode.title);
			}.bind(this));
		}	
		
		return options;
	},
	
	// handle changing of category or format
	onNodeOrFormatChange: function(parentNode, event) {
		// hide other nodes on the same level
		if(parentNode.childNodes) {
			parentNode.childNodes.each(function(childNode) { childNode.container.hide(); }.bind(this));
		}
		if(parentNode.formats) {
			parentNode.formats.each(function(format) { format.container.hide() });
		}
		
		// clear possibly selected attributes;
		this.priceconfig.attributes.clear();
		
		// set node's value (required for walking through nodes to a possibly selected format);
		parentNode.value = event.target.value;
		
		// a node was selected, there must be formats in a deeper level
		if(this.nodes[event.target.value]) {
			this.onNodeSelect(this.nodes[event.target.value], parentNode);
		}
		// a format was selected, there can't be sub nodes
		else if(this.formats[event.target.value]) {
			var format = this.formats[event.target.value];
			this.onFormatSelect(format, parentNode);
		}
		// nothing or dummy was selected, so hide price items
		else {
			parentNode.value = null;
			if(parentNode.formats) {
				parentNode.itemBaseprice.hide();
				parentNode.itemCheapprice.hide();
			}
		}
		
		// always a good idea ;-)
		this.updatePrice();
	},
	
	// a node was selected
	onNodeSelect: function (node, parentNode) {
		if(parentNode.formats) {
			parentNode.itemBaseprice.hide();
			parentNode.itemCheapprice.hide();
		}
		node.container.show();
		this.priceconfig.format = null;
		this.walkNodesForFormat(parentNode);		
	},
	
	// a format was selected
	onFormatSelect: function(format, parentNode) {
		
		// show format's attributes and page select and hide the others
		format.container.show();
		
		this.priceconfig.format = format;
		var price = this.getFormatPrice(format, this.quantitySelector.value);
		
		parentNode.itemBaseprice.show();
		parentNode.itemBaseprice.update(this.formatAsPrice(price.base));
		if(price.cheap) {
			parentNode.itemCheapprice.show();
			parentNode.itemCheapprice.update(this.formatAsPrice(price.cheap));
			parentNode.itemBaseprice.addClassName('strike');			
		} else {
			parentNode.itemCheapprice.hide();
			parentNode.itemBaseprice.removeClassName('strike');
		}
		
		// check if there are still selected attributes
		if(format.attributes) {
			format.attributes.each(function(attribute) {
				if(attribute.checkbox.checked) {
					this.enableAttribute(attribute.ident);
				}
			}.bind(this));
		}
		this.updatePrice();
	},
	
	// walk recursively through all nodes to find a slected format if available
	walkNodesForFormat: function(node) {
		if(node.value) {	
			if(node.value.substr(0,6) == 'format' && this.formats[node.value]) {
				this.onFormatSelect(this.formats[node.value], node);
			}
	
			if(node.value.substr(0,9) == 'childNode' && this.nodes[node.value]) {	
				var childNode = this.nodes[node.value];
				this.walkNodesForFormat(childNode);
			}
		}
	},
	
	renderImages: function(images) {
		var container = new Element('div', {'class': 'images'});
		images.each(function(image) {
			container.insert(image);
		}.bind(this));
		
		return container;
	},
	
	renderAttributes: function(attributes, pageSelect, formatUID) {
		
		
		var container = new Element('div', {'class': 'attributes'});
		
		attributes.each(function(attribute) {
			attribute.ident = 'format_' + formatUID + '_attribute_' + attribute.uid;
			var excludes = [];
			if(attribute.exclude) {
				attribute.exclude.each(function(exclude) {
					excludes.push('format_' + formatUID + '_attribute_' + exclude);
				}.bind(this));
				attribute.exclude = excludes.join(',');
			}
			this.attributes[attribute.ident] = attribute; 
			container.insert(this.renderSingleAttribute(attribute.ident, pageSelect));
		}.bind(this));

		return container;
	},
	
	renderSingleAttribute: function(ident, pageSelect) {
		var attribute = this.attributes[ident];
		// rendering	
		//var ident = this.ident + '_format_' + formatUID + '_attribute_' + attribute.uid;
		var label = new Element('label', {'for': attribute.ident});
		attribute.free = new Element('span', {'class': 'free'}).update('(GRATIS!)').hide();
		label.insert(attribute.title + ' ');
		label.insert(attribute.free);
		attribute.checkbox = new Element('input', {'type': 'checkbox', 'class': 'checkbox', 'id': attribute.ident, 'name': 'attribute', value: attribute.uid});
		var container = new Element('div', {'class': 'row'});
		
		attribute.itemCheapprice = new Element('span', {'class': 'item_cheapprice'}).hide();
		attribute.itemBaseprice = new Element('span', {'class': 'item_baseprice'}).hide();
		attribute.itemBaseprice.update('+ ' + this.formatNumber(parseFloat(attribute.price), 2, true) + ' ' + this.config.currencySymbol);
		container.insert(attribute.itemCheapprice);
		container.insert(attribute.itemBaseprice);
		container.insert(attribute.checkbox);
		container.insert(label);
		
		// handle checkbox checking
		attribute.checkbox.observe('click', this.handleCheckboxForAttribute.bind(this, ident));
		
		// handle changing number of pages
		if(pageSelect && attribute.freeselector == 'pages') {
			pageSelect.observe('change', this.handlePageSelectForAttribute.bind(this, ident));
		}

		return container;
	},
	
	// add attribute to price configuration
	enableAttribute: function(ident) {
		this.priceconfig.attributes.push(ident);
		var attribute = this.attributes[ident];
		// disable all attributes that can't go together with the selected one
		if(attribute.exclude) {
			var excludes = attribute.exclude.split(',');
			excludes.each(function(excludeIdent) {
				this.disableAttribute(excludeIdent);
			}.bind(this));
		}
		this.updateAttribute(ident);
		this.updatePrice();
	},
	
	// remove attribute from price configuration
	disableAttribute: function(ident) {
		//var ident = this.ident + '_format_' + formatUID + '_attribute_' + attribute.uid;
		this.priceconfig.attributes = this.priceconfig.attributes.without(ident);
		this.attributes[ident].checkbox.checked = false;
		this.updateAttribute(ident);
		this.updatePrice();
	},
	
	// handle attribute's checkbox event
	handleCheckboxForAttribute: function(ident, event) {
		if(event.target.checked) {
			this.enableAttribute(ident);
		} else {
			this.disableAttribute(ident);
		}
	},
	
	// handle page select event
	handlePageSelectForAttribute: function(ident, event) {
		this.attributes[ident].isFree = (parseInt(event.target.value) >= parseInt(this.attributes[ident].freeselectorminvalue)) ? true : false;
		this.updateAttribute(ident);
		this.updatePrice();
	},
	
	// Re-render attribute (switch between offer, free or non-free)
	updateAttribute: function(ident) {
		var attribute = this.attributes[ident];
		if(attribute.checkbox.checked) {
			attribute.itemBaseprice.show();
			if(attribute.isFree) {
				attribute.itemCheapprice.update('+ 0,00 ' + this.config.currencySymbol);
				attribute.itemCheapprice.show();
				attribute.itemBaseprice.addClassName('strike');
			} else if(attribute.offeractive) {
				attribute.itemCheapprice.update('+ ' + this.formatNumber(parseFloat(attribute.offerprice), 2, true) + ' ' + this.config.currencySymbol);
				attribute.itemCheapprice.show();
				attribute.itemBaseprice.addClassName('strike');
			} else {
				attribute.itemCheapprice.hide();
				attribute.itemBaseprice.removeClassName('strike');
			}
		} else {
			attribute.itemCheapprice.hide();
			attribute.itemBaseprice.hide();
		}
		if(attribute.isFree) {
			attribute.free.show();
		} else {
			attribute.free.hide();
		}
	},
	
	// render page select individually for each format
	renderPageSelect: function(format) {
		var container = new Element('div', {'class': 'row'});
		format.pageSelect = new Element('select', {'name': 'pages'});
		container.insert(new Element('label').update('Seitenzahl:'));
		var itemBaseprice = new Element('span', {'class': 'item_baseprice'}).hide();
		container.insert(itemBaseprice);		
			
		format.pageSelect.observe('change', function(event) {
			var pages = parseInt(event.target.value);
			this.updatePrice();
			
			if(pages && pages > this.priceconfig.format.minpages) {
				var additional = ((event.target.value - format.minpages) / format.steppages) * format.priceperstep;
				itemBaseprice.show();
				itemBaseprice.update('+ ' + this.formatNumber(additional, 2, true) + ' ' + this.config.currencySymbol);
			} else {
				itemBaseprice.hide();
			}			
		}.bind(this));

		for(var i = parseInt(format.minpages) - 1; i < parseInt(format.maxpages); i += parseInt(format.steppages)) {
			var option = new Element('option', {'value': i + 1});
			option.update((i + 1)  + ' Seiten');
			format.pageSelect.insert(option);
		}
		container.insert(format.pageSelect)
		return container;
	},
	
	// render quantity selector and update price config according to value;
	renderQuantitySelector: function(min, max) {
		if(!min) {
			min = 1;
		}
		if(!max) {
			max = 10;
		}
		var container = new Element('div', {'class': 'row quantity'});

		// add label
		container.insert(new Element('label').update('Anzahl:'));		
		
		this.quantityItemBaseprice = new Element('span', {'class': 'item_baseprice quantity'}).hide();
		container.insert(this.quantityItemBaseprice);		
		
		this.quantitySelector = new Element('select');
		container.insert(this.quantitySelector);
		this.quantitySelector.observe('change', function(event) {
			this.priceconfig.quantity = parseInt(event.target.value);
			this.priceconfig.attributes.clear();
			this.onFormatSelect(this.priceconfig.format, this.priceconfig.format.parentNode);
			this.updatePrice();
		}.bind(this));
		
		for(var i = min; i <= max; i++) {
			this.quantitySelector.insert(new Element('option', {'value': i}).update(i));
		}
		
		this.priceconfig.quantity = min;
		
		return container;
	},
	
	// render shipping country selector and update price config according to value;
	renderCountrySelector: function() {
		var container = new Element('div', {'class': 'row'});

		// add label
		container.insert(new Element('label').update('Versand:'));		

		this.shippingItemCheapprice = new Element('span', {'class': 'item_cheapprice'}).hide();
		container.insert(this.shippingItemCheapprice);			
		this.shippingItemBaseprice = new Element('span', {'class': 'item_baseprice'});
		container.insert(this.shippingItemBaseprice);
		
		var select = new Element('select');
		container.insert(select);
		select.observe('change', function(event) {
			var shippingcountry = this.config.countries.find(function(country) {
				return country.uid == event.target.value ? true : false;
			}.bind(this));
			this.priceconfig.shippingzone = shippingcountry ? parseInt(shippingcountry.tx_mdcalculator_zone_uid) : null;
			this.updatePrice();
		}.bind(this));
		
		this.config.countries.each(function(country) {
			select.insert(new Element('option', {'value': country.uid}).update(country.cn_short_de));
			// TODO: add configuration possibility to TYPO3
			if(country.uid == 41) {
				select.insert(new Element('option', {'value': ''}).update('--'));
			}
		}.bind(this));
		
		return container;		
	},	
	
	// get label
	getSelectLabel: function(depth) {	
		if(this.config.labels && this.config.labels[depth]) {
			var label = new Element('label');
			label.update(this.config.labels[depth]);
		} else {
			var label = new Element('span', {'class': 'dummy'})
		}
		return label;	
	},	
	
	// from http://javascript.jstruebig.de/javascript/37/
	formatNumber: function(zahl, k, fix)
	{
	    if(!k) k = 0;
	    var neu = '';
	    // Runden
	    var f = Math.pow(10, k);
	    zahl = '' + parseInt( zahl * f + (.5 * (zahl > 0 ? 1 : -1)) ) / f ;
	    // Komma ermittlen
	    var idx = zahl.indexOf('.');
	    // fehlende Nullen einf?gen
	    if(fix)
	    {
	         zahl += (idx == -1 ? '.' : '' )
	         + f.toString().substring(1);
	    }
	    // Nachkommastellen ermittlen
	    idx = zahl.indexOf('.');
	    if( idx == -1) idx = zahl.length;
	    else neu = ',' + zahl.substr(idx + 1, k);
	
	    // Tausendertrennzeichen
	    while(idx > 0)
	    {
	        if(idx - 3 > 0)
	        neu = '.' + zahl.substring( idx - 3, idx) + neu;
	        else
	        neu = zahl.substring(0, idx) + neu;
	        idx -= 3;
	    }
	
	    return neu;
	},
	
	formatAsPrice: function(value, addIcon) {
		var content = '';
		if(addIcon) {
			content += '+ ';
		}
		content += this.formatNumber(parseFloat(value), 2, true) + ' ' + this.config.currencySymbol;
		return content;
	}
});

