/********************************************************************
	Obsessing.org : obsessing_ide.js
	Copyright 2008 Aubrey Anderson / Particle Programmatica, Inc
	http://particlewebsite.com | aubrey [at] particularplace [dot] com
	please be cool.
********************************************************************/

// dojo's lovely key code array.
var keys = {
	BACKSPACE: 8,
	TAB: 9,
	CLEAR: 12,
	ENTER: 13,
	SHIFT: 16,
	CTRL: 17,
	ALT: 18,
	PAUSE: 19,
	CAPS_LOCK: 20,
	ESCAPE: 27,
	SPACE: 32,
	PAGE_UP: 33,
	PAGE_DOWN: 34,
	END: 35,
	HOME: 36,
	LEFT_ARROW: 37,
	UP_ARROW: 38,
	RIGHT_ARROW: 39,
	DOWN_ARROW: 40,
	INSERT: 45,
	DELETE: 46,
	HELP: 47,
	LEFT_WINDOW: 91,
	RIGHT_WINDOW: 92,
	SELECT: 93,
	NUMPAD_0: 96,
	NUMPAD_1: 97,
	NUMPAD_2: 98,
	NUMPAD_3: 99,
	NUMPAD_4: 100,
	NUMPAD_5: 101,
	NUMPAD_6: 102,
	NUMPAD_7: 103,
	NUMPAD_8: 104,
	NUMPAD_9: 105,
	NUMPAD_MULTIPLY: 106,
	NUMPAD_PLUS: 107,
	NUMPAD_ENTER: 108,
	NUMPAD_MINUS: 109,
	NUMPAD_PERIOD: 110,
	NUMPAD_DIVIDE: 111,
	F1: 112,
	F2: 113,
	F3: 114,
	F4: 115,
	F5: 116,
	F6: 117,
	F7: 118,
	F8: 119,
	F9: 120,
	F10: 121,
	F11: 122,
	F12: 123,
	F13: 124,
	F14: 125,
	F15: 126,
	NUM_LOCK: 144,
	SCROLL_LOCK: 145
};

var Sketch = function() {
	this.name = "Unnamed Sketch";
	this.modDate = new Date().getTime();
	this.editorText = "";
};	

var ObsessingIDE = function() {
  	
   	this.cursorLine = 1;
		this.cursorPosition = 0;
		this.cursorLeftOffset = 54;
		this.cursorTopOffset = -11;
		this.fontWidth = 7.2;
		this.fontHeight = 16;
		this.tabSpacing = 2;
		this.editorText = "";
		this.insertPoint = 0;
		this.cursorLine = 0;
		this.renderEngine = "canvas";
		
		this.sketches = new Array(new Sketch());
		this.currentSketch = 0;
		
		var ob = this;
		this.cursorAnimation = false;

		//this.updateStatus("hi there");
		
		__ObsessingEnvironment = "development";
		
		//this.relocateCursor();
		
		this.captureKeys = true;
		
		var ideRef = this;	
		
		
		      

  
  this.renderDefaultText = function() {

		
		this.updateEditor("\n");
		this.updateEditor("/*");
		this.updateEditor("\n");
		this.updateEditor("Welcome to Obsessing!");
		this.updateEditor("\n");
		this.updateEditor("Obsessing is all about bringing the Processing API/experience into the browser.");
		this.updateEditor("\n");
		this.updateEditor("Obsessing is VERY ALPHA and activly being developed, a lot of things are still seriously rough.");
		this.updateEditor("\n");
		this.updateEditor("Click that little info button in the top right for more....");
		this.updateEditor("\n");
		this.updateEditor("\n");
		this.updateEditor("to make anything happen you'll need to implement");
		this.updateEditor("\n");
		this.updateEditor("void draw()");
		this.updateEditor("\n");
		this.updateEditor("You can also implement");
		this.updateEditor("\n");
		this.updateEditor("void setup()");
		this.updateEditor("\n");
		this.updateEditor("for anything you need before animation starts....");
		this.updateEditor("\n");
		this.updateEditor("Here's a little test example:");
		this.updateEditor("\n");
		this.updateEditor("*/");
		this.updateEditor("\n");
		this.updateEditor("\n"); 
		
		this.updateEditor("PGraphics pg;"); 
		this.updateEditor("\n");
		this.updateEditor("\n");
		this.updateEditor("void setup() {"); 
		this.updateEditor("\n");
  	this.updateEditor("  size(200, 200);"); 
  	this.updateEditor("\n");
  	this.updateEditor("  pg = createGraphics(80, 80, P3D);"); 
  	this.updateEditor("\n");
		this.updateEditor("}"); 
		this.updateEditor("\n");
		this.updateEditor("\n");
		this.updateEditor("void draw() {"); 
		this.updateEditor("\n");
  	this.updateEditor("  fill(0, 12);"); 
  	this.updateEditor("\n");
  	this.updateEditor("  rect(0, 0, width, height);"); 
  	this.updateEditor("\n");
  	this.updateEditor("  fill(255);"); 
  	this.updateEditor("\n");
  	this.updateEditor("  noStroke();"); 
  	this.updateEditor("\n");
  	this.updateEditor("  ellipse(mouseX, mouseY, 60, 60);"); 
  	this.updateEditor("\n");
  	this.updateEditor("\n");
  	this.updateEditor("  pg.beginDraw();"); 
  	this.updateEditor("\n");
  	this.updateEditor("  pg.background(102);"); 
  	this.updateEditor("\n");
  	this.updateEditor("  pg.noFill();"); 
  	this.updateEditor("\n");
  	this.updateEditor("  pg.stroke(255);"); 
  	this.updateEditor("\n");
  	this.updateEditor("  pg.ellipse(mouseX-60, mouseY-60, 60, 60);"); 
  	this.updateEditor("\n");
  	this.updateEditor("  pg.endDraw();"); 
  	this.updateEditor("\n");
  	this.updateEditor("\n");
  
  	this.updateEditor("  image(pg, 60, 60); "); 
  	this.updateEditor("\n");
		this.updateEditor("}"); 
		this.updateEditor("\n");
		this.updateEditor("\n");
		
		
  
  };
  

	this.shouldEval = false;
	this.CODE_OK = 1;
	this.CODE_ERROR = 2;
	this.CODE_WARN = 3;
	this.criteriaString = "";
	this.editorText = "";
	this.defaultText = false;

	this.cursorIntervalCallback = function() {
		$('#cursor').toggle();
		
		// here's a test script eval on interval
		// should pop a warning if you leave an error for
		// more than 1000 milis
		if (this.shouldEval && ! this.codeCompleteIsShowing)
		{
			try
			{
				eval(this.editorText);
				this.updateStatus(this.CODE_OK);
				
			}
			catch(evalError)
			{
				this.updateStatus(this.CODE_ERROR, evalError);
			}
			
			this.shouldEval = false;
		}
		else this.shouldEval = true;
		
		if (! this.defaultText) {
			//this.renderDefaultText();
			//this.updateEditor("LOADING....");
			this.defaultText = true;
		}
		
	};

	this.relocateCursor = function() {
		$('#cursor').css("left", (this.cursorPosition * this.fontWidth) + this.cursorLeftOffset);
		$('#cursor').css("top", (this.cursorLine * this.fontHeight) + this.cursorTopOffset);
		
		$('#lines').empty();
	
		var lines = this.editorText.split("\n");
		for(var i=0; i<lines.length; i++)
		{
			$('#lines').append((i+1) + "<br />");
		}
		
		$('#line_number').empty();
		$('#line_number').append(this.cursorLine);
		
		$('#column_number').empty();
		$('#column_number').append(this.cursorPosition + 1);
			
	};
	
	this.clearEditor = function() {
		
		window[0].document.body.innerHTML = "";
		
	};

	// we'll get either the key or the char or a String for k
	// should probably branch on typeof just to be clear
	this.updateEditor = function(k, specialKey) {
	
		
		if (k.length == 0) k = "&nbsp;";
		k = k.replace(/\s/g, "&nbsp;");
		//console.log("|" + k + "|");
		window[0].document.body.innerHTML += "<div class='brush: java; html-script: true'>"+k+"</div>";



		
	};
	
	this.previewSketch = function() {
		
		var exportPage = window.open("/build", "sketchExportWindow", 
			"toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=1,width=650,height=650,left=200,top=100");
		//var exportPage = window.open("export.html", "ref", "toolbar=1");
		
		var t = "";
		$(window[0].document.body).find("div").each(function() {
			console.log($(this).text());
			
			var text = $(this).text().replace(/\s/g, " ").replace(/\+/g, "%2B");
			
			t += escape(text) + "\n";
		});
		
		
		
//		var t = $(window[0].document.body).text()
//			.replace(/\&nbsp\;/g, " ")
//			.replace(/\;/g, ";\n")
//			.replace(/\}/g, "}\n")
//			.replace(/\{/g, "{\n");
		$("#exportSketchCode").attr("value", t);
		$("#exportSketchForm").submit();
		exportPage.focus();
		
	};


	// TO DO: convert these next 2 to regex
	this.nextIsNewLine = function() {
		var nextChar = this.editorText.substring(this.insertPoint, this.insertPoint + 1);
		if (nextChar == "\n") return true;
		else return false;
	};

	this.lastIsNewLine = function() {
		var lastChar = this.editorText.substring(this.insertPoint-1, this.insertPoint);
		if (lastChar == "\n") return true;
		else return false;
	};

	this.insertText = function(txt) {
		var t1 = this.editorText.substring(0, this.insertPoint);
		var t2 = this.editorText.substring(this.insertPoint, this.editorText.length);
		this.editorText = t1 + txt + t2;
		
	};

	this.deleteText = function()	{
		var t1 = this.editorText.substring(0, this.insertPoint-1);
		var t2 = this.editorText.substring(this.insertPoint, this.editorText.length);
		this.editorText = t1 + t2;
	};

	this.codeCompleteIsShowing = false;
  this.selectedCCItem = 0;
	this.methodArray = new Array();

	this.codeComplete = function(objectName) {
		trace("cc getting called");
		
		// we ideally need a way to evaluate written code on the page
		// as we go, probably wrap that up into a live interpreter
		// which is going at an interval?
	
		this.codeCompleteIsShowing = true;
		$('#codeComplete').empty();
	
		var objectRef = null;
		try { 
			objectRef = eval(objectName); 
			//trace(typeof(objectRef));
			//trace(objectRef);	
		}
		//try { objectRef = objectName; }
		catch(er) { 
			// shouldn't default to string though
			objectRef = ""; 
			trace("couldn't eval for code complete");
		}  
	
		this.methodArray = new Array();
	
		// this seems to be looping and or evaling down for each
		// object... curious
		//this.methodArray.push("AAAcompleting for: " + objectRef);
	
		for (x in objectRef) {
			var cc = x;
			var cct = typeof(objectRef[x]);
	
			if (cct == "function") cc += "()";
			else cc += " - " + cct;
			
			if (! cc.indexOf("_") == 0)
			this.methodArray.push(cc);
		}
	
		this.methodArray.sort();
	
		var ideRef = this;
		
		for (i=0; i<this.methodArray.length; i++) {
			var d = document.createElement('div');
			d.className = "ccOption";
			d.id = this.methodArray[i];
			d.innerHTML = this.methodArray[i];
			d.onclick = function(evt) { 
				
				var textToAdd = this.innerHTML.split(" - ")[0];
				
				//this.cursorPosition += textToAdd.length - 1;
				//updateEditor("");
				$('#codeComplete').fadeOut(300, function() { ideRef.updateEditor(textToAdd); });
				ideRef.codeCompleteIsShowing = false;
				
			};
			
			if (d) $('#codeComplete').append(d);
		
		}
		
		// position the completion box
		$('#codeComplete').css("left", (this.cursorPosition * this.fontWidth) + this.cursorLeftOffset);
		$('#codeComplete').css("top", (this.cursorLine * this.fontHeight) + this.cursorTopOffset);
		
		$('#codeComplete').css("visibility", "visible");
		$('#codeComplete').fadeIn(300);
		
	};

	this.codeCompleteCriteria = function(criteria) {
		$('#codeComplete').empty();
		
		var ideRef = this;
		
		for (i=0; i<this.methodArray.length; i++)
		{
			if (this.methodArray[i].indexOf(criteria) == 0)
			{
				var d = document.createElement('div');
				d.className = "ccOption";
				d.id = this.methodArray[i];
				d.innerHTML = this.methodArray[i];
				d.onclick = function(evt) { 
				
					var textToAdd = this.innerHTML.split(" - ")[0];
	
					//this.cursorPosition += textToAdd.length - 1;
					//updateEditor("");
					textToAdd = textToAdd.substring(criteria.length);
					
					$('#codeComplete').fadeOut(300, function() { ideRef.updateEditor(textToAdd); });
					ideRef.codeCompleteIsShowing = false;
				
				};
			
				if (d) $('#codeComplete').append(d);
			}
		}
	};

	this.updateStatus = function(status, err) {		
		if (status == this.CODE_OK) {
			$(".code-ok").show();
			$(".code-error").hide();
			$(".status-txt").empty().append("Code Ok");
		}
		else if (status == this.CODE_ERROR) {
			$(".code-ok").hide();
			$(".code-error").show();
			$(".status-txt").empty().append(err.message);
		}
	};

	// potential button handlers
	this.importContent = function() {
		var impContainer = document.createElement("div");
		$(impContainer).css("position","absolute");
		$(impContainer).css("width","430px");
		$(impContainer).css("height","460px");
		$(impContainer).css("left","150px");
		$(impContainer).css("top","100px");
		$(impContainer).css("background","#505973");
		
		var imp = document.createElement("textarea");
		imp.rows = "40";
		imp.cols = "70";
		
		$(imp).css("position","absolute");
		$(imp).css("width","400px");
		$(imp).css("height","400px");
		$(imp).css("left","10px");
		$(imp).css("top","10px");
		
		var ok = document.createElement("div");
		ok.innerHTML = "import";
		ok.onclick = function() {
			//this.editorText = imp.value;
			$(impContainer).remove();	
			this.updateEditor(imp.value);
			
			this.updateEditor("");
			
		}
		$(ok).css("position","relative");
		$(ok).css("left","10px");
		$(ok).css("top","430px");
		$(ok).css("width","40px");
		$(ok).addClass("genericButton");
		
		var canc = document.createElement("div");
		canc.innerHTML = "cancel";
		canc.onclick = function() {
			$(impContainer).remove();
		}
		$(canc).css("position","relative");
		$(canc).css("left","70px");
		$(canc).css("top","412px");
		$(canc).css("width","40px");
		$(canc).addClass("genericButton");
		
		$(impContainer).append(imp);
		$(impContainer).append(ok);
		$(impContainer).append(canc);
		$("#editorContainer").append(impContainer);
		imp.focus();
		
		
	};

	this.saveSketch = function() {
		
		try {
			this.updateStatus("SAVED");
		}
		catch(exp) {
			trace(exp);
		}
		
		// some UI fedback
		
	};
	
	this.getCode = function() {
		return this.editorText;
	};

	this.saveSkatchAs = function() {
		this.updateStatus("Save As");
	};

	this.newSketch = function() {
		this.updateStatus("NEW");
	};

	this.openSketch = function(code) {
		//this.updateStatus("OPEN");
		this.clearEditor()
		var self = this;
		var lines = code.split(/\n/);
		
		
		$(lines).each(function() {
			
			self.updateEditor(this);
			
			//if (this.length == 0) self.updateEditor("&nbsp;");
			//else self.updateEditor(this.replace(" ", "&nbsp;"));
			
		});
		
		
	};

	
  

};



