Ext.define("Ext.ux.util.ace.Editor", {

	mixins: [
		"Ext.mixin.Mashup"
	],

	requiredScripts: [
		"/portfolio/app/ux/util/ace/src/ace.js"
	],

	editorOptions: [
		"selectionStyle",
		"highlightActiveLine",
		"highlightSelectedWord",
		"readOnly",
		"cursorStyle",
		"mergeUndoDeltas",
		"behavioursEnabled",
		"wrapBehavioursEnabled",
		"autoScrollEditorIntoView",

		"hScrollBarAlwaysVisible",
		"vScrollBarAlwaysVisible",
		"highlightGutterLine",
		"animatedScroll",
		"showInvisibles",
		"showPrintMargin",
		"printMarginColumn",
		"printMargin",
		"fadeFoldWidgets",
		"showFoldWidgets",
		"showLineNumbers",
		"showGutter",
		"displayIndentGuides",
		"fontSize",
		"fontFamily",
		"maxLines",
		"minLines",
		"scrollPastEnd",
		"fixedWidthGutter",
		"theme",

		"scrollSpeed",
		"dragDelay",
		"dragEnabled",
		"focusTimout",
		"tooltipFollowsMouse",

		"firstLineNumber",
		"overwrite",
		"newLineMode",
		"useWorker",
		"useSoftTabs",
		"tabSize",
		"wrap",
		"foldStyle",
		"mode"
	],
	editorOptionsPrefixes: {
		"theme": "ace/theme/",
		"mode": "ace/mode/"
	},

	// Editor options
	selectionStyle: "line",//"line"|"text"
	highlightActiveLine: true,
	highlightSelectedWord: true,
	readOnly: false,
	cursorStyle: "ace", //"ace"|"slim"|"smooth"|"wide"
	mergeUndoDeltas: false, //false true "always"
	behavioursEnabled: false,
	wrapBehavioursEnabled: true,

	// autoScrollEditorIntoView: , // this is needed if editor is inside scrollable page

	// Renderer options
	hScrollBarAlwaysVisible: false,
	vScrollBarAlwaysVisible: false,
	highlightGutterLine: true,
	animatedScroll: false,
	showInvisibles: false,
	showPrintMargin: true,
	printMarginColumn: 80,
	printMargin: 80,
	fadeFoldWidgets: false,
	showFoldWidgets: true,
	showLineNumbers: true,
	showGutter: true,
	displayIndentGuides: true,
	fontSize: 12, //number or css font-size string
	// fontFamily: '', //css
	// maxLines: ,
	// minLines: false,
	scrollPastEnd: 0,

	// fixedWidthGutter: false,
	theme: "textmate", //path to a theme textmate|

	// Mousehandler options
	scrollSpeed: 2, //number
	dragDelay: 0, //number
	dragEnabled: true,
	focusTimout: 0, // number
	tooltipFollowsMouse: true,

	// Session options
	firstLineNumber: 1, //number
	overwrite: false,
	newLineMode: "auto", // auto|?
	useWorker: true,
	useSoftTabs: true,
	tabSize: 4, //number
	wrap: false,

	// foldStyle: true,
	mode: "text", //path to a mode text|json|

	// Properties
	_editor: null,
	onAceLoaded: Ext.EmptyFn,

	initAce: function () {

		var i_cmp = this;
		i_cmp.fireEvent("aceloaded", i_cmp, window.ace);
	},

	initEditor: function (s_id) {

		var i_cmp = this;
		var i_editor;
		var i_ace = window.ace;

		i_editor = i_ace.edit(s_id);
		i_cmp._editor = i_editor;

		i_cmp.initEditorOptions();

		i_cmp.on({
			resize: {
				fn: function() {
					setTimeout(function () {
						i_cmp.getEditor().resize();
					}, 100);
				}
			},
			activate: {
				fn: function() {
					i_cmp.getEditor().focus();
				}
			},
			focus: {
				fn: function () {
					i_cmp.getEditor().focus();
				}
			}
		});

		var value = i_cmp.getValue();
		if (Ext.isObject(value)) {
			value = JSON.stringify(value, null, "\t");
		}
		if (Ext.isArray(value)) {
			value = value.join("\n");
		}

		if (!Ext.isDefined(value)) {
			value = "";
		}

		i_cmp.setEditorValue(value);

		i_editor.session.on("change", function() {
			i_cmp.fireEvent("editorchange", i_cmp, i_editor, i_cmp.getEditorValue());
		});
		i_editor.session.on("changeAnnotation", function() {
			i_cmp.fireEvent("editorvalidate", i_cmp);
		});

		i_cmp.fireEvent("editorready", i_cmp, i_editor);

		return i_editor;
	},

	initEditorOptions: function () {

		var i_cmp = this;
		var a_pick = i_cmp.editorOptions;
		var o_options = {};
		var o_config = i_cmp.initialConfig;

		Ext.Array.each(a_pick, function(s_key) {
			if (o_config.hasOwnProperty(s_key)) {
				o_options[s_key] = o_config[s_key];
			}
		});

		// Run the options through the prefix map
		var o_pf = i_cmp.editorOptionsPrefixes;
		Ext.Object.each(o_options, function (s_key, value) {
			if (o_pf[s_key]) {
				o_options[s_key] = o_pf[s_key] + value;
			}
		});

		i_cmp.setEditorOptions(o_options);
	},
	setEditorOptions: function (o_options) {

		var i_cmp = this;
		var i_editor = i_cmp.getEditor();

		if (i_editor) {
			i_editor.setOptions(o_options);
		}
	},
	isEditorValid: function () {

		var i_cmp = this;
		var i_session = i_cmp.doSession();
		var b_valid = true;

		if (i_session) {

			var a_list = i_session.getAnnotations();

			Ext.Array.some(a_list, function (o_annotation) {

				if (o_annotation.type == "error") {
					b_valid = false;
					return true;
				}
			});
		}

		return b_valid;
	},

	getEditorErrors: function () {

		var i_cmp = this;
		var i_session = i_cmp.doSession();
		var a_errors = [];

		if (i_session) {

			var a_list = i_session.getAnnotations();

			Ext.Array.each(a_list, function (o_annotation) {

				if (o_annotation.type == "error") {
					a_errors.push([
						o_annotation.text,
						"on line",
						o_annotation.row++,
						"column",
						o_annotation.column
					].join(" "));
				}
			});
		}

		return a_errors;
	},

	/**
	 * This method returns the editor object created by AceEditor used by this UX
	 */
	getEditor: function() {

		return this._editor;
	},

	getEditorId: function() {

		return this.editorId;
	},

	/**
	 * This method returns the current session of the editor
	 */
	doSession: function() {

		var i_cmp = this;
		var i_editor = i_cmp._editor;
		var i_session;

		if (i_editor) {
			i_session = i_editor.getSession();
		}

		return i_session;
	},

	/**
	 * This handler gets the value of the editor, by value is meant the text/code/script
	 */
	getEditorValue: function() {

		var i_cmp = this;
		var i_session;
		var i_editor = i_cmp.getEditor();
		var value;

		if (i_editor) {
			i_session = i_editor.getSession();
		}

		if (i_session) {
			value = i_session.getValue();
		}

		return value;
	},

	/**
	 * This handler sets the value of the editor, by value is meant the text/code/script
	 * @param {string} value The value/code to insert into the editor
	 */
	setEditorValue: function(value) {

		var i_cmp = this;
		var i_editor = i_cmp._editor;
		var i_session;
		if (i_editor) {
			i_session = i_editor.getSession();
		}

		if (i_session) {
			i_session.setValue(value);
		}

		return this;
	},

	gotoLine: function(line) {
		this.getEditor().gotoLine(line);
		return this;
	},

	/**
	 * This handler undoes the last change in the editor and returns the editor object for chainability
	 */
	undo: function() {
		this.getEditor().undo();
		return this;
	},

	/**
	 * This handler redoes the last undone change in the editor and returns the editor object for chainability
	 */
	redo: function() {
		this.getEditor().redo();
		return this;
	},

	/**
	 * This handler replaces the next occurence of "toReplace" with "replaceWith"
	 * @param {string} toReplace The string to look for and replace
	 * @param {string} replaceWith The string to replace found occurence of "toReplace" with
	 * @returns this
	 */
	replace: function(toReplace, replaceWith) {
		var editor = this.getEditor();

		editor.find(toReplace);
		editor.replace(replaceWith);

		return this;
	},

	/**
	 * This handler replaces all found occurences of "toReplace" with "replaceWith"
	 * @param {string} toReplace
	 * @param {string} replaceWith
	 * @returns this
	 */
	replaceAll: function(toReplace, replaceWith) {
		var editor = this.getEditor();

		editor.find(toReplace);
		editor.replaceAll(replaceWith);

		return this;
	}
});
