/* Copyright 2009 Palm, Inc.  All rights reserved. */


var Calculator = Class.create({
	initialize: function() {
		this.observers = new Hash();
		this.maxNumDigits = 11;	// Max number of digits that can be displayed including decimal point and commas
		this.setupMemDb();
		this.memory = this.getMemDbValue();	
		this.clear();
		this.decimalChar = Mojo.Format.getFormatHash().numberDecimal;
	},
	
	setupMemDb: function() {
		this.memCookie = new Mojo.Model.Cookie("com.palm.app.calculator");
	},
	
	getMemDbValue: function() {
		var data = this.memCookie.get();
		if (data) {
			return parseFloat(data.memory);
		}
		return 0.0;
	},
	
	setMemDbValue: function(value) {
		this.memCookie.put({memory: value});
	},
	
	setMaxDigits: function(max) {
		this.maxNumDigits = max;
	},
	
	results: function() {
		var maxDigits = this.maxNumDigits;
		if ((this.inputBuffer.indexOf('-') == 0) || (this.xReg < 0))
			maxDigits += 1;
		// need to check for max length - a MemRecall could cause
		// inputBuffer to exceed length because we just set inputBuffer to xReg...
		if ((this.inputBuffer.length) && (this.inputBuffer.length <= maxDigits))
			return this.formatDecimalChar(this.inputBuffer);
			
		if (isNaN(this.xReg) || !isFinite(this.xReg)) {
			// fix NOV-82805
			this.clear();
			return $L("Error");			
		}

		// Prevent rounding errors - calculate size of integer part of number so we can determine how
		// many places we can have after decimal...
		var numDecimal = maxDigits - 1 - Math.floor(this.xReg).toString().length;
		var adjustLength = 6;
		// Want to maximize number of digits we can display so we have to see how large the
		// exponential part is, if there is one
		if (this.xReg.toString().indexOf('e') >= 0) {
			var exponentialPosition = this.xReg.toString().indexOf('e');
			adjustLength = this.xReg.toString().substr(exponentialPosition).length + 2;
		}
		try {
			// numDecimal < -1 means that the integer portion of the number has exceeded
			// maximumum number of digits we can display so we have to represent it as 
			// an exponential number now.
			var resultStr = "";
			if (numDecimal < -1) {
				resultStr = this.xReg.toExponential(this.maxNumDigits-adjustLength);
			}
			else {
				resultStr = this.xReg.toFixed(numDecimal+1).substr(0, maxDigits);
				// if resultStr is zero but xReg is NOT that means the number is 0.000000000xxx...
				// so we need to convert to an exponential.
				if ((this.xReg != 0) && (resultStr == 0)){
					resultStr = this.xReg.toExponential(this.maxNumDigits-adjustLength);
				}			
			}	
			
			if (resultStr.indexOf('e') >= 0) {
				// get rid of any trailing zeroes that preceed the exponential... and lone decimal...
				resultStr = resultStr.replace(/0+e/, 'e').replace(/\.e/, 'e');
			}
			else 
				if (resultStr.indexOf('.') >= 0) {
					resultStr = resultStr.replace(/0+$/, "").replace(/\.$/, "");
				}
			if ((parseFloat(resultStr) == 0.0))
				return 0;
				
			return this.formatDecimalChar(resultStr);
		} 
		catch (ex) {
			return $L("Error");
		}
	},
	
	formatDecimalChar: function(resultStr) {
		if (this.decimalChar != '.')
			return resultStr.replace(/\./, this.decimalChar);
			
		return resultStr;
	},
	
	clear: function() {
		this.inputBuffer = '';
		this.xReg = 0;
		this.yReg = 0;
		this.op = Calculator.NOOP;
	},
	
	backspace: function() {
		if (this.inputBuffer.indexOf('e') >= 0) {
			// Don't allow digits in an exponential number to get deleted
			return;
		}
		if (this.inputBuffer.length) {
			if (this.inputBuffer.length == 1) {
				this.inputBuffer = '';
				this.xReg = 0;
			// Fix JIRA NOV-78230 - Check for a single digit negative number...
			} else if ((this.inputBuffer.indexOf('-') == 0) && (this.inputBuffer.length == 2)) {
				this.inputBuffer = '-0';			
				this.xReg = 0;
			} else {
				this.inputBuffer = this.inputBuffer.substr(0, this.inputBuffer.length - 1);
				this.xReg = parseFloat(this.inputBuffer);
			}
		}
	},
	
	enterDigit: function(digit) {
		if (this.inputBuffer.length >= this.maxNumDigits)
			return;
			
		// Prevent leading zeros
		if ((digit == 0) &&
			(this.xReg == 0) &&
			(this.inputBuffer.indexOf('.') < 0)) 
		{
			return;
		}
		
		// Fix JIRA NOV-78229, extra check to prevent leading zero from appearring	
		if ((parseFloat(this.inputBuffer) == 0) && (this.inputBuffer.indexOf('.') < 0)) {
			this.inputBuffer = this.inputBuffer.substr(0, this.inputBuffer.length - 1);
		}
		this.inputBuffer += digit;
		this.xReg = parseFloat(this.inputBuffer);
	},
	
	enterDecimal: function(digit) {
		if (this.inputBuffer.length >= this.maxNumDigits)
			return;
			
		if (this.inputBuffer.length == 0)
			this.inputBuffer = '0';
			
		if (this.inputBuffer.indexOf('.') < 0) {
			this.inputBuffer += '.';
		}
	},
	
	add: function() {
		this.setOp(Calculator.ADD);
	},
	
	subtract: function() {
		this.setOp(Calculator.SUBTRACT);
	},
	
	multiply: function() {
		this.setOp(Calculator.MULTIPLY);
	},
	
	divide: function() {
		this.setOp(Calculator.DIVIDE);
	},
	
	equals: function() {
		this.doOp();
		this.opComplete = true;
	},
	
	setOp: function(op) {
		this.clearCompeletedOp();
		if (this.inputBuffer.length)
			this.doOp();
		this.yReg = this.xReg;
		this.op = op;
	},
	
	doOp: function() {
		var result = this.xReg;
		switch(this.op) {
			case Calculator.ADD:
				result = this.yReg + this.xReg;
				break;
			case Calculator.SUBTRACT:
				if (this.inputBuffer.length)
					result = this.yReg - this.xReg;
				else
					result = this.xReg - this.yReg;
				break;
			case Calculator.MULTIPLY:
				result = this.yReg * this.xReg;
				break;
			case Calculator.DIVIDE:
				if (this.inputBuffer.length)
					result = this.yReg / this.xReg;
				else
					result = this.xReg / this.yReg;
				break;
		}
		
		if (this.inputBuffer.length) {
			this.yReg = this.xReg;
		}
		this.xReg = result;
		this.inputBuffer = '';
	},
	
	clearCompeletedOp: function() {
		if (this.opComplete)
			this.op = Calculator.NOOP;
		
		this.opComplete = false;
	},
	
	sqrt: function() {
		this.xReg = Math.sqrt(this.xReg);
		this.inputBuffer = '';
	},
	
	percent: function() {
		// Fix JIRA NOV-29819
		// operand1 + or - operand2 %
		// old way : operand1 + or - operand2/100
		// new way : operand1 + or - (operand1 * (operand2/100))
		if ((this.op == Calculator.ADD) || (this.op == Calculator.SUBTRACT)) {
			this.xReg = this.yReg * (this.xReg / 100);
		}
		else {
			this.xReg = this.xReg / 100;
		}
		this.inputBuffer = '' + this.xReg;
	},
	
	plusMinus: function() {
		this.xReg = -this.xReg;
		// Fix JIRA NOV-80683, allow for -0 and -0. values to be entered
		//
		// Case 1: clear key pressed or initial run of calculator, inputBuffer equals empty string and xReg = 0
		if ((this.inputBuffer.length == 0) && (this.xReg == 0)) {
			this.inputBuffer = '-' + this.xReg;
		}
		// Case 2: inputBuffer has text, check for whether we're making it neg. or pos.
		//	this takes care of making sure a number with a decimal portion doesn't
		//	get converted to a whole number, i.e. enter 9.00, press +/-, number would
		//	change to -9 if we just did inputBuffer = '' + this.xReg.
		else if ((this.inputBuffer.length) && (this.inputBuffer.indexOf('-') < 0)) {
			this.inputBuffer = '-' + this.inputBuffer;
		}
		else if ((this.inputBuffer.length) && (this.inputBuffer.indexOf('-') >= 0)) {
			this.inputBuffer = this.inputBuffer.substr(1);
		}
		// Case 3: When "=" is pressed, inputBuffer gets cleared and xReg stores the result
		//	this takes care of that case.
		else {
			this.inputBuffer = '' + this.xReg;
		}
	},
	
	memPlus: function() {
		this.memory += this.xReg;
		this.setMemDbValue(this.memory);
		this.inputBuffer = '';
	},
	
	memMinus: function() {
		this.memory -= this.xReg;
		this.setMemDbValue(this.memory);
		this.inputBuffer = '';
	},
	
	memRecall: function() {
		this.xReg = this.memory;
		this.inputBuffer = '' + this.memory;
	},
	
	memClear: function() {
		this.memory = 0.0;
		this.setMemDbValue('0.0');
	},
	
	getMemValue: function() {
		return this.memory;
	},
});

Calculator.NOOP = 0;
Calculator.ADD = 1;
Calculator.SUBTRACT = 2;
Calculator.MULTIPLY = 3;
Calculator.DIVIDE = 4;
