
CodeMirror.defineMode("pascal", function() {
	function words(str) {
		var obj = {}, words = str.split(" ");
		for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
		return obj;
	}
	var keywords = words("and array begin case const div do downto else end file for forward integer " +
		"boolean char function goto if in label mod nil not of or packed procedure " +
		"program record repeat set string then to type until var while with try except finally TStringList");
	var atoms = {"null": true};

	var isOperatorChar = /[+\-*&%=<>!?|\/]/;

	function tokenBase(stream, state) {
		var ch = stream.next();
		if (ch == "#" && state.startOfLine) {
			stream.skipToEnd();
			return "meta";
		}
		if (ch == '"' || ch == "'") {
			state.tokenize = tokenString(ch);
			return state.tokenize(stream, state);
		}
		if (ch == "(" && stream.eat("*")) {
			state.tokenize = tokenComment;
			return tokenComment(stream, state);
		}
		if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
			return null;
		}
		if (/\d/.test(ch)) {
			stream.eatWhile(/[\w\.]/);
			return "number";
		}
		if (ch == "/") {
			if (stream.eat("/")) {
				stream.skipToEnd();
				return "comment";
			}
		}
		if (isOperatorChar.test(ch)) {
			stream.eatWhile(isOperatorChar);
			return "operator";
		}
		stream.eatWhile(/[\w\$_]/);
		var cur = stream.current();
		if (keywords.propertyIsEnumerable(cur)) return "keyword";
		if (atoms.propertyIsEnumerable(cur)) return "atom";
		return "variable";
	}

	function tokenString(quote) {
		return function(stream, state) {
			var escaped = false, next, end = false;
			while ((next = stream.next()) != null) {
				if (next == quote && !escaped) {end = true; break;}
				escaped = !escaped && next == "\\";
			}
			if (end || !escaped) state.tokenize = null;
			return "string";
		};
	}

	function tokenComment(stream, state) {
		var maybeEnd = false, ch;
		while (ch = stream.next()) {
			if (ch == ")" && maybeEnd) {
				state.tokenize = null;
				break;
			}
			maybeEnd = (ch == "*");
		}
		return "comment";
	}

	// Interface

	return {
		startState: function() {
			return {tokenize: null};
		},

		token: function(stream, state) {
			if (stream.eatSpace()) return null;
			var style = (state.tokenize || tokenBase)(stream, state);
			if (style == "comment" || style == "meta") return style;
			return style;
		},

		electricChars: "{}"
	};
});

CodeMirror.defineMIME("text/x-pascal2", "pascal2");

webix.protoUI({
	name:"codemirror-editor",
	defaults:{
		mode:"text/x-pascal",
		lineNumbers:true,
		matchBrackets:true,
		theme:"neat"
	},
	$init:function(config){
		this.$view.innerHTML = "<textarea style='width:100%;height:100%;'></textarea>";
		this.$ready.push(this._render_when_ready);
	},

	// _render_cm_editor:function(){ // загрузку сделаю сам
	//
	// 	webix.require("pascalHints.js", this._render_when_ready, this);
	// },

	_render_when_ready:function(){
		webix.require.disabled = true;

		var extraPascalHints = window.extraPascalHints || {};

		this.editor = CodeMirror.fromTextArea(this.$view.firstChild, {
			mode         : this.config.mode,
			lineNumbers  : this.config.lineNumbers,
			matchBrackets: this.config.matchBrackets,
			theme        : this.config.theme,
			hintOptions  : {
				completeSingle: false
			}
		});


		var server = new CodeMirror.TernServer({
			defs: [extraPascalHints],
			queryOptions: {
				completions: {
					caseInsensitive: true
				}
			},
			hintDelay: 15000
		});

		this.editor.setOption("extraKeys", {
			"Ctrl-Space": function(cm) { server.complete(cm); },
			"Ctrl-U"    : function(cm) { server.showType(cm); },
			"Ctrl-O"    : function(cm) { server.showDocs(cm); },
			"Alt-."     : function(cm) { server.jumpToDef(cm); },
			"Alt-,"     : function(cm) { server.jumpBack(cm); },
			"Ctrl-Q"    : function(cm) { server.rename(cm); },
			"Ctrl-."    : function(cm) { server.selectName(cm); },
			"Tab"       : function(cm) { cm.replaceSelection(Array(cm.getOption("indentUnit") + 1).join(" ")); }
		});

		// this.editor.on("cursorActivity", function(cm) {
		// 	server.updateArgHints(cm);
		// });

		this.setValue(this.config.value);

		if (this._focus_await)
			this.focus();

		this.callEvent("onReady", []);


	},

	_set_inner_size:function(){
		if (!this.editor || !this.$width) return;

		this._updateScrollSize();
		this.editor.scrollTo(0,0); //force repaint, mandatory for IE
	},
	_updateScrollSize:function(){
		var box = this.editor.getWrapperElement();
		var height = (this.$height || 0) + "px";

		box.style.height = height;
		box.style.width = (this.$width || 0) + "px";

		var scroll = this.editor.getScrollerElement();
		if (scroll.style.height != height){
			scroll.style.height = height;
			this.editor.refresh();
		}
	},
	$setSize:function(x,y){
		if (webix.ui.view.prototype.$setSize.call(this, x, y)){
			this._set_inner_size();
		}
	},

	setValue:function(value){
		if(!value && value !== 0)
			value = "";

		this.config.value = value;
		if(this.editor){
			this.editor.setValue(value);
			//by default - clear editor's undo history when setting new value
			if(!this.config.preserveUndoHistory)
				this.editor.clearHistory();
			this._updateScrollSize();
		}
	},

	getValue:function(){
		return this.editor?this.editor.getValue():this.config.value;
	},

	focus:function(){
		this._focus_await = true;
		if (this.editor)
			this.editor.focus();
	},

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


	//undo, redo, etc
	undo:function(){
		this.editor.undo();
	},
	redo:function(){
		this.editor.redo();
	},
	undoLength:function(){
		return this.editor.historySize().undo;
	}
}, webix.ui.view, webix.EventSystem);