var Editor = new Class({

	Implements: [Events, Options],

	options: {
		toolbar: true,
		url: '/',
		css: null,
		buttons: 'bold,italic,underline,strikethrough,separator,justifyleft,justifyright,justifycenter,justifyfull,separator,insertunorderedlist,insertorderedlist,separator,createlink,unlink,separator,undo,redo,separator,toggleview',		
		text: {
			'bold': 'Bold',
			'italic': 'Italic',
			'underline': 'Underline',
			'strikethrough': 'Strikethrough',
			'justifyleft': 'Justify Left',
			'justifyright': 'Justify Right',
			'justifycenter': 'Justify Center',
			'justifyfull': 'Justify Full',
			'insertunorderedlist': 'Unordered List',
			'insertorderedlist': 'Ordered List',
			'undo': 'Undo',
			'redo': 'Redo',
			'createlink': 'Add Hyperlink',
			'unlink': 'Remove Hyperlink',
			'toggleview': 'Toggle View'
		}
	},
	
	initialize: function(el, options) {
		this.setOptions(options);
		this.textarea = el;
		this.build();
	},

	build: function() {
		// Build the container
		this.container = new Element('div', {
			'id': (this.textarea.id) ? this.textarea.id + '-container' : null,
			'class': 'editor-container',
			'styles': {
				'width': '100%',
				'margin': this.textarea.getStyle('margin')
			}
		});

		// Put textarea inside container
		this.container.wraps(this.textarea);

		// Fix IE bug, refer "IE/Win Inherited Margins on Form Elements" <http://positioniseverything.net/explorer/inherited_margin.html>
		if(Browser.Engine.trident) new Element('span').wraps(this.textarea);

		// Build the iframe
		var pads = this.textarea.getStyle('padding').split(' ');
		pads = pads.map(function(p){ return (p == 'auto') ? 0 : p.toInt(); });

		this.iframe = new IFrame({
			'class': 'editor-iframe',
			'styles': {
				/*'width': this.textarea.getStyle('width').toInt() + pads[1] + pads[3],*/
				'width': '99.8%',
				'height': this.textarea.getStyle('height').toInt() + pads[0] + pads[2]
				/*'border-color': this.textarea.getStyle('border-color'),
				'border-width': this.textarea.getStyle('border-width'),
				'border-style': this.textarea.getStyle('border-style')*/
			}
		});
		
		// For ie browser show incorrect box size 
		if (Browser.Engine.trident) this.iframe.setStyle('width', '99.7%');

		this.textarea.setStyles({
			'margin': 0,
			'display': 'none',
			'resize': 'none', // disable resizable textareas in Safari
			'outline': 'none' // disable focus ring in Safari
		});

		this.iframe.inject(this.container, 'top');

		// contentWindow and document references
		this.win = this.iframe.contentWindow;
		this.doc = this.win.document;

		// Build the content of iframe
		var css = (this.options.css !== null) ? '<link rel="stylesheet" type="text/css" href="' + this.options.css + '">' : '';
		var documentTemplate = '<html style="cursor: text; height: 100%">' + css + '<body id=\"editable\" style="border: 0; padding: 0px;">' + this.cleanup(this.textarea.value) + '</body></html>';
		this.doc.open();
		this.doc.write(documentTemplate);
		this.doc.close();

		// Turn on Design Mode
		this.doc.designMode = 'on';

		// In IE6, after designMode is on, it forgots what is this.doc. Weird.
		if(Browser.Engine.trident4) this.doc = this.win.document;

		// styleWithCSS, not supported in IE and Opera
//		if (!Browser.Engine.trident && !Browser.Engine.presto) this.execute('styleWithCSS', false, false);

		// Assign view mode
		this.mode = 'iframe';

		// Update the event for textarea's corresponding labels
		if (this.textarea.id && $$('label[for="' + this.textarea.id + '"]')) {
			$$('label[for="' + this.textarea.id + '"]').addEvent('click', function(e) {
				if (this.mode == 'iframe') {
					e = new Event(e).stop();
					this.win.focus();
				}
			}.bind(this));
		}

		// Update & cleanup content before submit
		this.form = this.textarea.getParent('form');
		if (this.form) this.form.addEvent('submit', function() {
			if (this.mode == 'iframe') this.updateContent();
		}.bind(this));

		if (this.options.toolbar) this.buildToolbar();
	},

	buildToolbar: function() {
		this.toolbar = new Element('div', { 'class': 'editor-toolbar' });
		this.toolbar.inject(this.iframe, 'before');

		var toolbarButtons = this.options.buttons.split(',');
		toolbarButtons.each(function(command, idx) {
			var element;
			var obj = this;
			
			if (command == 'separator') {
				element = new Element('img', { 'src': this.options.url + 'separator.png', 'alt': command, 'class': 'toolbar-separator' });
			} else {
				var img = new Element('img', { 'src': this.options.url + this.options.text[command].toLowerCase().replace(/ /gi, "-") + '.png', 'alt': this.options.text[command] });
				
				element = new Element('button', {
					'class': command + '-button toolbar-button',
					'style': 'cursor: pointer',
					'title': this.options.text[command],
					'events': {
						'click': function(e) {
							e.stop();
							if (!this.hasClass('disabled')) obj.action(command);
							obj.win.focus();
						},
						'mousedown': function(e) { e.stop(); }
					}
				});
				
				// add hover effect for IE6
				if (Browser.Engine.trident4) element.addEvents({
					'mouseenter': function(e) { this.addClass('hover'); },
					'mouseleave': function(e) { this.removeClass('hover'); }
				});
				
				img.inject(element);
			}
			
			element.inject(this.toolbar);
		}.bind(this));
	},

	action: function(command) {
		switch(command){
			case 'createlink':
				var selection = (this.doc.selection) ? this.doc.selection.createRange().text : this.win.getSelection();

				if (selection == '') {
					alert("Please select the text you wish to hyperlink.");
				} else {
					var url = prompt('Enter URL', 'http://');
					if (url) this.execute(command, false, url.trim());
				}
				break;
			case 'urlimage':
				var url = prompt('Enter Image URL', 'http://');
				if (url) this.execute('insertImage', false, url.trim());
				break;
			case 'toggleview':
				this.toggleView();
				break;
			default:			
				this.execute(command, false, '');			
		}
	},

	execute: function(command, param1, param2) {
	    if (!this.busy) {
			this.busy = true;
			this.doc.execCommand(command, param1, param2);
			//this.updateContent();
			this.busy = false;
		}
		
		return false;
	},

	toggleView: function() {
		if (this.mode == 'textarea') {
			this.mode = 'iframe';
			this.iframe.setStyle('display', '');
			
			(function() {
				this.doc.getElementById('editable').innerHTML = this.textarea.value;
			}).bind(this).delay(1); // dealing with Adobe AIR's webkit bug
			
			this.toolbar.getElements('.toolbar-button').each(function(item) {
				item.removeClass('disabled');
				item.setProperty('disabled', '');
				item.set('opacity', 1);
			});
			
			this.textarea.setStyle('display', 'none');
		} else {
			this.mode = 'textarea';
			this.textarea.addClass('editor');
			this.textarea.setStyle('display', '');

			// For ie browser show incorrect box size 
			this.textarea.setStyle('width', ((Browser.Engine.trident) ? '99.7%' : '99.8%'));

			this.updateContent();
			
			this.toolbar.getElements('.toolbar-button').each(function(item) {
				if (!item.hasClass('toggleview-button')) {
					item.addClass('disabled');
					item.setProperty('disabled', 'disabled');
					item.set('opacity', 0.4);					
				}
			});
			
			this.iframe.setStyle('display', 'none');
		}
	},

	updateContent: function() {
		this.textarea.value = this.cleanup(this.doc.getElementById('editable').innerHTML);
	},

	cleanup: function(source) {
		// Webkit cleanup
		source = source.replace(/<br class\="webkit-block-placeholder">/gi, "<br />");
		source = source.replace(/<span class="Apple-style-span">(.*)<\/span>/gi, '$1');
		source = source.replace(/ class="Apple-style-span"/gi, '');
		source = source.replace(/<span style="">/gi, '');

		// Remove padded paragraphs
		source = source.replace(/<p>\s*<br \/>\s*<\/p>/gi, '<p>\u00a0</p>');
		source = source.replace(/<p>(&nbsp;|\s)*<\/p>/gi, '<p>\u00a0</p>');
		source = source.replace(/\s*<br \/>\s*<\/p>/gi, '</p>');

		// Replace improper BRs
		source = source.replace(/<br>/gi, "<br />");

		// Remove leading and trailing BRs
		source = source.replace(/<br \/>$/gi, '');
		source = source.replace(/^<br \/>/gi, '');

		// Remove useless BRs
		source = source.replace(/><br \/>/gi, '>');

		// Remove BRs right before the end of blocks
		source = source.replace(/<br \/>\s*<\/(h1|h2|h3|h4|h5|h6|li|p)/gi, '</$1');

		// Remove empty tags
		source = source.replace(/(<[^\/]>|<[^\/][^>]*[^\/]>)\s*<\/[^>]*>/gi, '');

		// Semantic conversion
		source = source.replace(/<span style="font-weight: bold;">(.*)<\/span>/gi, '<strong>$1</strong>');
		source = source.replace(/<span style="font-style: italic;">(.*)<\/span>/gi, '<em>$1</em>');
		source = source.replace(/<b(\s+|>)/g, '<strong$1');
		source = source.replace(/<\/b(\s+|>)/g, '</strong$1');
		source = source.replace(/<i(\s+|>)/g, '<em$1');
		source = source.replace(/<\/i(\s+|>)/g, '</em$1');
		source = source.replace(/<u(\s+|>)/g, '<span style="text-decoration: underline;"$1');
		source = source.replace(/<\/u(\s+|>)/g, "</span$1");

		// Replace uppercase element names with lowercase
		source = source.replace(/<[^> ]*/g, function(match){return match.toLowerCase();});

		// Replace uppercase attribute names with lowercase
		source = source.replace(/<[^>]*>/g, function(match){
			match = match.replace(/ [^=]+=/g, function(match2){return match2.toLowerCase();});
			return match;
		});

		// Put quotes around unquoted attributes
		source = source.replace(/<[^>]*>/g, function(match){
			match = match.replace(/( [^=]+=)([^"][^ >]*)/g, "$1\"$2\"");
			return match;
		});

		// Final trim
		source = source.trim();

		return source;
	}
	
});