CLI = {
	stdin: '',
	stdin_enabled: true,
	history: [''],
	history_z: 0,
	buffer: [''],
	buffer_z: 0,
	canvas: null,
	line_height: 18,
	num_lines: 0,
	canvas_height: 0,
	prompt: {string:'p957# ', y_offset:6, padding:0},
	startpos: {x:4, y:4},
	x: 4,
	y: 4,
	font: {stdin:'bold 14px Courier', stdout:'14px Courier'},
	colors: {background:'#222', stdin:'#fff', stdout:'#fff', error:'#f06', cursor:'#ccc'},
	cursor: '&#x275a;',
	
	echo: function(s, color, log) {
		this.setFillStyle(color);
		this.ctx.fillText(s, this.x, this.y);
		this.x += this.ctx.measureText(s).width;
		this.cursor.css('left', this.x);
		if (log) {
			this.writeBuffer(s, color);
		}
		//TODO: add wrap text logic here based on this.x > this.canvas.width - (this.startpos.x * 2)
	},
	
	writeBuffer: function(str, color) {
		this.buffer[this.buffer_z++] = (color ? '^C['+color+'];' : '') + str;
	},
		
	read: function(str) {
		this.echo(str, this.colors.stdin);
		this.stdin += str;
	},
	
	setFont: function(font) {
		this.ctx.font = font;
	},
	
	setFillStyle: function(style) {
		this.ctx.fillStyle = style;
	},
	
	execute: function(str) {
		str = str.replace(/^\s+|\s+$/g, ''); //trim whitespace
		
		if (str.length == 0) {
			return this.executeFinish(false);
		}
		
		this.history[this.history_z++] = str;
	
		if (str == 'clear') {
			this.clearBuffer();
			this.buffer = [''];
			this.executeFinish(false);
		}
		else {
			try {
				this.stdin_enabled = false;
				this.writeBuffer(str);
				//eval(str);
				self = this;
				$.getJSON('./cli.php', {cmd:str}, function(data) {
					self.executeFinish(data);
				});
			}
			catch (ex) {
				str = 'Unhandled Exception: ' + ex;
				this.setFont(this.font.stdout);
				this.newLine(false);
				this.echo(str, this.colors.error, true);
				this.executeFinish(false);
			}
		}
	},
	
	executeFinish: function(response) {
		if (typeof response != 'undefined' && response) {
			this.setFont(this.font.stdout);
			if (response.errno == 0) {
				if (response.action == 'go') {
					window.location.href = response.data[0];
				}
				else {
					for (var i=0, len=response.data.length; i<len; i++) {
						this.newLine(false);
						this.echo(response.data[i], this.colors.stdout, true);
					}
				}
			}
			else {
				this.newLine(false);
				this.echo(response.error, this.colors.error, true);
			}
		}
		
		this.stdin_enabled = true;
		this.stdin = '';
		this.setFont(this.font.stdin);
		this.newLine(true);
	},
	
	newLine: function(has_prompt) {
		if (this.y + this.line_height >= this.canvas_height) {
			this.redrawBuffer(1);
			this.setFont(this.font.stdin);
		}
		
		this.x = this.startpos.x;
		this.y += this.line_height;
		
		if (has_prompt) {
			this.echo(this.prompt.string, this.colors.stdin);
			this.cursor.css('top', this.y - this.line_height + this.prompt.y_offset);
		}
	},
	
	redrawBuffer: function(dir) {
		//this.buffer_offset += dir || 0;
		offset = this.buffer_z - (this.num_lines-2);
		this.clearBuffer();
	
		for (i=offset; i<this.buffer_z; i++) {
			matches = this.buffer[i].match(/^\^C\[(#\w{3})\];(.*)/);
			if (matches) {
				this.setFont(this.font.stdout);
				this.newLine(false);
				this.echo(matches[2], matches[1]);
			}
			else {
				this.setFont(this.font.stdin);
				this.newLine(true);
				this.echo(this.buffer[i], this.colors.stdin);
			}
		}
	},
	
	clearLine: function() {
		this.setFillStyle(this.colors.background);
		this.x = this.startpos.x + this.prompt.padding;
		this.ctx.fillRect(this.x, this.y-this.line_height+4, this.canvas.width, this.y);
		this.stdin = '';
	},
	
	clearBuffer: function() {
		this.setFillStyle(this.colors.background);
		this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
		this.x = this.startpos.x;
		this.y = this.startpos.y;
	},
	
	delete: function(str) {
		this.clearLine();
		str ? this.read(str.slice(0, str.length-1)) : this.endOfBufferAlert();
	},
	
	keyPress: function(e) {
		if (this.stdin_enabled) {
			chr = e.charCode || e.keyCode;
			if (chr == 13) {
				this.execute(this.stdin);
			}
			else if (chr != 8) {
			 	this.read(String.fromCharCode(chr));
			}
		}
		return false;
	},
	keyDown: function(e) {
		if (!this.stdin_enabled) return false;
		
		switch (e.keyCode) {
			case 8:
				e.preventDefault();
				this.delete(this.stdin);
				break;
			case 38:
				e.preventDefault();
				this.clearLine();
				this.read(this.history[(this.history_z > 0 ? --this.history_z : 0)]);
				break;
			case 40:
				e.preventDefault();
				this.clearLine();
				str = ''
				if (typeof this.history[this.history_z+1] != 'undefined') { 
					str = this.history[++this.history_z];
				}
				else {
					this.history_z = this.history.length;
				}
				this.read(str);
				break;
		}
	},
	
	endOfBufferAlert: function() {
		$('<div>End</div>')
		.css({
			position:'absolute',
			bottom:'2px',
			left:'12px',
			padding:'0 20px',
			background:'#fff',
			opacity:'0.8',
			font:this.font.stdin,
			color:this.colors.background}
		)
		.appendTo('body')
		.delay(1000)
		.fadeOut(100, function() {
			$(this).remove();
		});
	},
	
	init: function() {
		var isCanvasSupported = function(){
			//check first for <canvas> support and then for canvas text support
			var supported = !!document.createElement('canvas').getContext;
			if (supported) {
				var tmp_canvas = document.createElement('canvas');
				var tmp_context = tmp_canvas.getContext('2d');
				supported = typeof tmp_context.fillText == 'function';
			}
			return supported;
		};	
		if (!isCanvasSupported()) {
			return window.location.replace('/unsupported.html');
		}	
		
		//document.title='';
		$(document.body).css({padding:0, margin:0});
		
		this.canvas = $('<canvas/>').appendTo('body').get(0);
		this.ctx = this.canvas.getContext('2d');
		this.canvas.width = $(window).width();
		this.canvas.height = $(window).height();
		this.canvas.style.backgroundColor = this.colors.background;
		
		// determine the max number of lines for the current canvas height and set the canvas_height variable 
		this.num_lines = Math.floor((this.canvas.height - this.startpos.y * 2) / this.line_height);
		this.canvas_height = this.num_lines * this.line_height;
		
		this.setFont(this.font.stdin);
		this.prompt.padding = this.ctx.measureText(this.prompt.string).width;
		
		self = this;
		
		document.onkeypress = function(e) {
			self.keyPress(e);
		}
		document.onkeydown = function(e) {
			self.keyDown(e);
		}
		
		// add the cursor and the blink function
		this.cursor = $('<div>'+this.cursor+'</div>').css({position:'absolute', color:this.colors.cursor, left:this.startpos.x + this.prompt.padding}).appendTo('body');
		setInterval(function() {
			self.cursor.toggle();
		}, 500);
		
		this.newLine(true);
	},
};

$(document).ready(function() {
	CLI.init();
});

