function CalculatorKernel() {
	this.calculatePriceForAdUnit = calculatePriceForAdUnit;
	this.getColorSurchargeName = function(inRegion, inColor, inPercent) {
		var csVar = inRegion;
		// standard ad unit
		if ( inColor == 'full_color' ) {
			csVar = 'full_color_' + csVar;
		} else if ( inColor == 'spot_color' ) {
			csVar = 'spot_color_' + csVar;
		}
		// page coverage
		if ( inPercent >= 100 ) {
			csVar = 'full_page_or_more_' + csVar;
		} else {
			csVar = 'smaller_than_full_page_' + csVar;
		}
		return csVar;
	};
	
	this.roundPerInchPricesForMultipleRegionDiscount = function(inPrice, discount) {
		inPrice = inPrice * discount / 4;
		return inPrice.round(2) * 4;
	};
}

function calculatePriceForAdUnit(inputObj) {
	/*
	 This function performs calculation using validated and normalized parameters from inputObj
	 properties of inputObj
	 - adUnit : the ad unit object obtained from server
	 - selectedRegions : array of ISO 2-char country code
	 - basePrices : property-value pairs for region code and base price
	 - discounts : all discount information obtained from server
	 - surcharge : color surcharge table and print guaranteed surcharge from server
	 - color : current color option (bw, spot_color, full_color)
	 - revenue : array index of the revenue contract
	 - frequency : frequency value
	 - category : boolean signaling if categrory discount should be applied or not
	 - isPrintGuaranteed : boolean signaling if it is a print guarranteed.
	 - splitCopy : number of creative supplied
	 
	 output object's property
	 - original : original price without any discount
	 _ discount : the discount (in exact amount, not percentage)
	 - total : the final price
	 - volumeDiscountTotal : volume discount, we will apply either volume or frequency discount (not both) to the ad unit.  In case you need to know the exact value of the volume discount, you can use this property
	 - frequencyDiscountTotal : refer to volumeDiscountTotal
	 - bonusAd : the number of bonus ad.  The frequency program applies to only selected ad units.  For those which don't apply to this scheme, the returned value is -1.  Otherwise, the value tells you how many bonus ad units the customer can get. (from 0 to 12 in year 2007)
	 
	 */
	if ( (inputObj.color == 'bw' && !inputObj.adUnit.allow_bw) || 
		 (inputObj.color == 'spot_color' && !inputObj.adUnit.allow_spot_color ) ||
		 (inputObj.color == 'full_color' && !inputObj.adUnit.allow_full_color )
		 ) {
		return {original:NaN, discount:NaN, total:NaN, volumeDiscountTotal:NaN, frequencyDiscountTotal:NaN, discountSelected:'', bonusAd:-1};
	}
	
	var mtRegionDiscountTable = inputObj.discounts.multiple_region_discounts_multiple
	var colorSurchargeTable = inputObj.surcharge.color_surcharge;
	var printGuaranteedSurcharge = inputObj.surcharge.print_guaranteed_surcharge;
	var categoryDiscountTable = inputObj.discounts.category_discount;
	var agencyDiscountTable = inputObj.discounts.agency_discount;
	var splitCopySurchargeTable = inputObj.surcharge.split_copy;
	// check which attributes have been set
	// inputObj.color surcharge and multiple region discounts are calculated here.
	var mtRegionDiscountValue = 1;
	var basePrice = 0;
	var matchedRegion = 0;
	var matchedGCN = 0;
	var colorSurcharge = 0;
	var frequencyDiscountTable;
	var myPageLeft;
	var n1, n2;
	switch ( inputObj.selectedRegions.length ) {
		case 1:
			// get base price
			basePrice = eval('inputObj.basePrices.' + inputObj.selectedRegions[0] + '.price');
			// determine inputObj.color surcharge
			csVar = '';
			if ( inputObj.selectedRegions[0] == 'asia' ) {
				// full asia has its own inputObj.color surcharge table
				csVar = 'asia';
				// ======== color option only. for print guaranteed surcharge, refer to the print guaranteed section.
				if ( inputObj.color == 'bw' ) {
					colorSurcharge = 0;
				} else if ( inputObj.adUnit.type == 'creative' ) {
					// creative ad unit
					if ( inputObj.adUnit.no_gutter ) {
						// if no gutter (i.e. spread), use the most expensive setting
						colorSurcharge = eval('colorSurchargeTable.full_page_or_more_' + inputObj.color + '_asia');
					} else {
						// page coverage - need to check the left and right side separately
						if ( inputObj.adUnit.page_percentage_right > 0 ) {
							// check if the position option is "Print Guaranteed - Center Spread"
							if ( inputObj.isPrintGuaranteed == 'print_guaranteed' ) {
								// only apply color surcharge once
								colorSurcharge = eval('colorSurchargeTable.' + this.getColorSurchargeName('asia', inputObj.color, inputObj.adUnit.page_percentage));
							} else {
								// need to check left and right side separately
								myPageLeft = inputObj.adUnit.page_percentage - inputObj.adUnit.page_percentage_right;
								n1 = this.getColorSurchargeName('asia', inputObj.color, myPageLeft);
								n2 = this.getColorSurchargeName('asia', inputObj.color, inputObj.adUnit.page_percentage_right);
								colorSurcharge = eval('colorSurchargeTable.' + n1) + eval('colorSurchargeTable.' + n2)
							}
						} else {
							// just apply 1 color surcharge
							colorSurcharge = eval('colorSurchargeTable.' + this.getColorSurchargeName('asia', inputObj.color, inputObj.adUnit.page_percentage));
						}
					}
				} else {
					// standard ad unit
					if ( inputObj.color == 'full_color' ) {
						csVar = 'full_color_' + csVar;
					} else if ( inputObj.color == 'spot_color' ) {
						csVar = 'spot_color_' + csVar;
					}
					// page coverage
					if ( inputObj.adUnit.page_percentage >= 100 ) {
						csVar = 'full_page_or_more_' + csVar;
					} else {
						csVar = 'smaller_than_full_page_' + csVar;
					}
					
					colorSurcharge = eval('colorSurchargeTable.' + csVar);
					
					// Color surcharge hack by Laurens 11/02/2010 for 2/3 Page Dominant
					if (inputObj.adUnit.id == 57) {
						 
						if ( inputObj.color == 'full_color' ) {
							colorSurcharge = 8034;
						}
					}
					
					
				}
				// set discount table
				volumeDiscountTable = eval('inputObj.discounts.volume_discounts_asia');
				
				frequencyDiscountTable = eval('inputObj.discounts.frequency_discounts_asia');
			} else {
				// no difference no matter it's spot or full inputObj.color
				if ( inputObj.color != 'bw' ) colorSurcharge = colorSurchargeTable.any_1;
				else colorSurcharge = 0;
				// set discont table
				
				volumeDiscountTable = eval('inputObj.discounts.volume_discounts_multiple');
				frequencyDiscountTable = eval('inputObj.discounts.frequency_discounts_multiple');
			}
			break;
		case 2:
		case 3:
		case 4:
			for ( i = 0; i < inputObj.selectedRegions.length; i++ ) {
				if ( inputObj.selectedRegions[i] == 'sg' || inputObj.selectedRegions[i] == 'hk' ) {
					matchedRegion++;
				}
				if ( inputObj.selectedRegions[i] == 'hk' || inputObj.selectedRegions[i] == 'tw' ) {
					matchedGCN++;
				}
				// sum up the base price
				basePrice += eval('inputObj.basePrices.' + inputObj.selectedRegions[i] + '.price');
			}
			// determine multiple region discount
			if ( matchedRegion == 2 ) {
				mtRegionDiscountValue = eval('mtRegionDiscountTable.hk_sg_' + inputObj.selectedRegions.length);
			} else if ( matchedGCN == inputObj.selectedRegions.length) {
				mtRegionDiscountValue = eval('mtRegionDiscountTable.greater_china_' + inputObj.selectedRegions.length);
			} else {
				mtRegionDiscountValue = eval('mtRegionDiscountTable.any_' + inputObj.selectedRegions.length);
			}
			if ( inputObj.color != 'bw' ) {
				if ( matchedGCN == inputObj.selectedRegions.length ) {
					colorSurcharge = colorSurchargeTable.greater_china;
				} else {
					colorSurcharge = eval('colorSurchargeTable.any_' + inputObj.selectedRegions.length);
				}
			}
			else colorSurcharge = 0;
			// set discont table
			volumeDiscountTable = eval('inputObj.discounts.volume_discounts_multiple');
			frequencyDiscountTable = eval('inputObj.discounts.frequency_discounts_multiple');
			break;
		case 5:
		default:
			// check if it's southeast asia 
			for ( i = 0; i < inputObj.selectedRegions.length; i++ ) {
				if ( inputObj.selectedRegions[i] == 'sg' || inputObj.selectedRegions[i] == 'my' || inputObj.selectedRegions[i] == 'th' || inputObj.selectedRegions[i] == 'ph' || inputObj.selectedRegions[i] == 'id' )
					matchedRegion++;
				// sum up the base price
				basePrice += eval('inputObj.basePrices.' + inputObj.selectedRegions[i] + '.price');
			}
			// determine if we should apply southeast_asia discount
			if ( matchedRegion == 5 ) {
				mtRegionDiscountValue = mtRegionDiscountTable.southeast_asia_5;
				if ( inputObj.color != 'bw' ) colorSurcharge = colorSurchargeTable.southeast_asia;
				else colorSurcharge = 0;
			} else {
				mtRegionDiscountValue = mtRegionDiscountTable.any_5
				if ( inputObj.color != 'bw' ) colorSurcharge = colorSurchargeTable.any_5;
				else colorSurcharge = 0;
			}
			// set discont table
			volumeDiscountTable = eval('inputObj.discounts.volume_discounts_multiple');
			frequencyDiscountTable = eval('inputObj.discounts.frequency_discounts_multiple');
			
			break;
	}
	
	// create result object
	var resultObj = {
		original:0.0, 
		discount:0.0, 
		total:0.0,
		volumeDiscountTotal:0.0,
		frequencyDiscountTotal:0.0,
		discountSelected:'',
		bonusAd:-1
	}
	
	// =========== frequency discount ===========
	var frequencyDiscountedTotal = 0;
	var frequencyDiscountValue;
	for (var i = 0; i < inputObj.selectedRegions.length; i++) {
		frequencyDiscountValue = 1;
		// check the frequency discount table format
		if ( eval('frequencyDiscountTable.' + inputObj.selectedRegions[i]) !== undefined ) {
			if ( inputObj.frequency >= eval('frequencyDiscountTable.' + inputObj.selectedRegions[i] + '.keys[0]') ) {
				var keyLength = eval('frequencyDiscountTable.' + inputObj.selectedRegions[i] + '.keys.length');
				for ( var j = keyLength - 1; j >=0; j-- ) {
					if ( inputObj.frequency >= eval('frequencyDiscountTable.' + inputObj.selectedRegions[i] + '.keys[j]') ) {
						frequencyDiscountValue = eval('frequencyDiscountTable.' + inputObj.selectedRegions[i] + '.values[j]');
						break;
					}
				}
			}
		} else {
			if ( inputObj.frequency >= frequencyDiscountTable.other.keys[0] ) {
				for ( j = frequencyDiscountTable.other.keys.length - 1; j >=0; j-- ) {
					if ( inputObj.frequency >= frequencyDiscountTable.other.keys[j] ) {
						frequencyDiscountValue = frequencyDiscountTable.other.values[j];
						break;
					}
				}
			}
		}
		frequencyDiscountedTotal += eval('inputObj.basePrices.' + inputObj.selectedRegions[i] + '.price') * frequencyDiscountValue;
	}
	resultObj.frequencyDiscountTotal = frequencyDiscountedTotal;
	
	// ============= frquency program =======
	// this is a buy x get n free scheme.  It doesn't not affect the grand total nor the discount value
	if ( inputObj.discounts.frequency_program ) {
		resultObj.bonusAd = 0;
		var freqProgDiscountTable = inputObj.discounts.frequency_program;
		for (var j = freqProgDiscountTable.keys.length - 1; j >= 0; j--) {
			if ( inputObj.frequency >= freqProgDiscountTable.keys[j] ) {
				resultObj.bonusAd = freqProgDiscountTable.values[j];
				break;
			}
		}		
	}
	
	// get the paid colume inch
	var inchFactor = eval('inputObj.basePrices.' + inputObj.selectedRegions[0] + '.price_type') == 'per inch' ? inputObj.adUnit.paid_column_inch : 1;
	// =========== volume discount ==============
	/*
	 Here's the tricky thing...
	 
	 The "Volume" discount actually consists of 2 different discounts: volume discount and contract discount.  The contract discount is applicable to only to Full Asia case.  For local edition, use the volume discount.  We do not separate this because we were told they are the same originally.  And, now (5/10/07), it's too late because the database and PHP logic are both implemented in the originally way.
	 */
	var volumeDiscountValue = 1;
	var volumeDiscountedTotal;
	var subtotal;
	if ( inputObj.selectedRegions[0] == 'asia' ) {
		// Editted and hacked by Laurens 10/02/2010. The line below was original.
		//volumeDiscountValue = ( inputObj.revenue == -1 ) ? 1 : volumeDiscountTable.values[inputObj.revenue];
		if (inputObj.revenue == -1) {
			volumeDiscountValue = 1;
		}
		else {
			if (($('category').selectedIndex > 0) && ($('revenue_contract').selectedIndex > 0)) {
				var new_revenue_discount = parseInt($('revenue_contract').options[$('revenue_contract').selectedIndex].value);
				new_revenue_discount = (100 - new_revenue_discount) / 100;
				volumeDiscountValue = new_revenue_discount;
			}
			else {
				volumeDiscountValue = volumeDiscountTable.values[inputObj.revenue];
			}
		}
		// End hack.

	} else {
		// if it's not Full Asia case, determine the discount automatically.
		var tmpTotal = inchFactor * basePrice * inputObj.frequency;
		for (var j = volumeDiscountTable.keys.length - 1; j >= 0; j--) {
			if ( tmpTotal >= volumeDiscountTable.keys[j] ) {
				volumeDiscountValue = volumeDiscountTable.values[j];
				break;
			}
		}		
	}
	if ( volumeDiscountValue != 1 ) volumeDiscountedTotal = this.roundPerInchPricesForMultipleRegionDiscount(basePrice, volumeDiscountValue);
	else volumeDiscountedTotal = basePrice;
	resultObj.volumeDiscountTotal = volumeDiscountedTotal;
	// check which one should use
	if ( frequencyDiscountedTotal > volumeDiscountedTotal ) {
		// use volume discount
		subtotal = volumeDiscountedTotal;
		resultObj.discountSelected = 'volume';
	} else {
		subtotal = frequencyDiscountedTotal;
		resultObj.discountSelected = 'frequency';
	}
	
	// =========== print guaranteed surcharge ==============
	if ( inputObj.isPrintGuaranteed != 'run_of_page' ) {
		basePrice *= printGuaranteedSurcharge;
		subtotal *= printGuaranteedSurcharge;
		resultObj.frequencyDiscountTotal *= printGuaranteedSurcharge;
		resultObj.volumeDiscountTotal *= printGuaranteedSurcharge;
	}
	// =========== apply multiple region discount ===========
	if ( mtRegionDiscountValue < 1 ) {
		// apply the multiple region discount to the price where frequency discount has been applied
		if ( inputObj.adUnit.price_type == 'per inch' ) {
			subtotal = this.roundPerInchPricesForMultipleRegionDiscount(subtotal, mtRegionDiscountValue);
		} else {
			subtotal *= mtRegionDiscountValue;
		}
	}
	
	// =========== category discount ============
	if ( inputObj.category != -1 ) {
		categoryDiscountValue = categoryDiscountTable.values[inputObj.category];
	} else {
		categoryDiscountValue = 1;
	}
	
	// =========== agency discount ============
	if ( inputObj.agency ) {
		agencyDiscountValue = agencyDiscountTable.values[0];
	} else {
		agencyDiscountValue = 1;
	}
	
	// ============ split copy ==============
	var splitCopySurcharge = 0;
	if ( inputObj.splitCopy > 1 ) {
		splitCopySurcharge = eval('splitCopySurchargeTable.' + inputObj.color) * inputObj.splitCopy;
	}
	
	// remember we can't apply volume and frequency discount at the same time.
	
	resultObj.frequencyDiscountTotal = (basePrice - resultObj.frequencyDiscountTotal) * inchFactor * inputObj.frequency;
	resultObj.volumeDiscountTotal = (basePrice - resultObj.volumeDiscountTotal) * inchFactor * inputObj.frequency;
	resultObj.original = (basePrice * inchFactor + colorSurcharge + splitCopySurcharge) * inputObj.frequency;
	
	
	
	//resultObj.total = (subtotal * inchFactor * categoryDiscountValue  + colorSurcharge + splitCopySurcharge) * inputObj.frequency * agencyDiscountValue;
	if (categoryDiscountValue != 1) {
		// With category discount (There should not be a discount on the color surcharge)
		resultObj.total = ((subtotal * inchFactor * categoryDiscountValue  + splitCopySurcharge) + colorSurcharge) * inputObj.frequency * agencyDiscountValue;
	}
	else {
		// Without category discount
		resultObj.total = (subtotal * inchFactor * categoryDiscountValue  + colorSurcharge + splitCopySurcharge) * inputObj.frequency * agencyDiscountValue;
	}
	resultObj.discount = resultObj.original - resultObj.total;

	return resultObj;
}
