package com.taobao.zeus.web.platform.client.lib.codemirror;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.NotStrict;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.MouseListener;
import com.google.gwt.user.client.ui.Widget;

public class CodeMirror extends Widget{

	public static String theme="default"; //default
	static{
		CodeMirrorCSS css=GWT.create(CodeMirrorCSS.class);
		css.style().ensureInjected();
	}
	private JavaScriptObject cm;
	
	public static interface OnChangeListener{
		void onChange();
	}
	
	private List<OnChangeListener> listeners=new ArrayList<OnChangeListener>();
	private List<MouseUpHandler> mouseListeners=new ArrayList<MouseUpHandler>();
	public CodeMirror(final CodeMirrorConfig config){
		setElement(DOM.createDiv());
		addAttachHandler(new Handler() {
			public void onAttachOrDetach(AttachEvent event) {
				if(event.isAttached()){
					cm=create(getElement(), config.toJsObject(CodeMirror.this));
				}
			}
		});
		sinkEvents(Event.MOUSEEVENTS|Event.ONCLICK);
	}
	
	public void addChangeListener(OnChangeListener listener){
		listeners.add(listener);
	}
	public void removeChangeListener(OnChangeListener listener){
		listeners.remove(listener);
	}
	public void addMouseUpHandler(MouseUpHandler handler){
		mouseListeners.add(handler);
	}
	public void removeMouseUpHandler(MouseUpHandler handler){
		mouseListeners.remove(handler);
	}
	@Override
	public void onBrowserEvent(Event event) {
		switch(DOM.eventGetType(event)){
		case Event.ONMOUSEUP:
		case Event.ONMOUSEOVER:
		case Event.ONCLICK:
			for(MouseUpHandler h:mouseListeners){
				h.onMouseUp(null);
			}
		}
	}
	
	public native void autoLoadMode(String type)/*-{
		var [email protected]Mirror::cm;
		$wnd.CodeMirror.autoLoadMode(cm,type);
	}-*/;
	
	public native String getValue()/*-{
		return [email protected]ror::cm.getValue();
	}-*/;
	
	public native void setValue(String value)/*-{
		if(value==null){
			value=" ";
		}
		[email protected]ror::cm.setValue(value);
	}-*/;
	
	public native String getSelection()/*-{
		return [email protected]ror::cm.getSelection();
	}-*/;
	
	public native void replaceSelection(String value)/*-{
		[email protected]ror::cm.replaceSelection(value);
	}-*/;
	
	public native void setSize(String width,String height)/*-{
		[email protected]ror::cm.setSize(width,height);
	}-*/;

	public native void focus()/*-{
		[email protected]ror::cm.focus();
	}-*/;
	
	public native void scrollTo(int x,int y)/*-{
		[email protected]ror::cm.scrollTo(x,y);
	}-*/;
	
	public native void setOption(String option,String value)/*-{
		[email protected]ror::cm.setOption(option,value);
	}-*/;
	
	public native String getOption(String option)/*-{
		return [email protected]ror::cm.getOption(option);
	}-*/;
	
	public native void undo()/*-{
		[email protected]ror::cm.undo();
	}-*/;
	
	public native void redo()/*-{
		[email protected]ror::cm.redo();
	}-*/;
	
	public native int lineCount()/*-{
		return [email protected]ror::cm.lineCount();
	}-*/; 
	
	public native void refresh()/*-{
		[email protected]ror::cm.refresh();
	}-*/;
	
	public native void format()/*-{
		var [email protected]CodeMirror::cm;
		$wnd.CodeMirror.commands['selectAll'](editor);
		editor.autoFormatRange(editor.getCursor(true),editor.getCursor(false));
	}-*/;
	
	private static native JavaScriptObject create(Element el,JavaScriptObject config)/*-{
		var editor=$wnd.CodeMirror(el,config);
		editor.setOption('theme',@com.taobao.zeus.web.platform.client.lib.codemirror.CodeMirror::theme);
		return editor;
	}-*/;
	
	public static class CodeMirrorConfig{
		public String value="";
		public String mode;
		public String theme;
		public int indentUnit=2;
		public boolean smartIndent;
		public int tabSize=4;
		public boolean indentWithTabs;
		public boolean electricChars=true;
		public boolean autoClearEmptyLines;
		public String keyMap;
		public Object extraKeys;
		public boolean lineWrapping;
		public boolean lineNumbers=true;
		public int firstLineNumber=1;
		public boolean gutter;
		public boolean fixedGutter;
		public boolean readOnly=true;
		
		public native JavaScriptObject toJsObject(CodeMirror cm)/*-{
			var js={};
			if([email protected]ror.CodeMirrorConfig::value!=null){
				[email protected]r.CodeMirror.CodeMirrorConfig::value;
			}
			if([email protected]ror.CodeMirrorConfig::mode!=null){
				[email protected].CodeMirror.CodeMirrorConfig::mode;
			}
			if([email protected]ror.CodeMirrorConfig::theme!=null){
				[email protected]r.CodeMirror.CodeMirrorConfig::theme;
			}
			[email protected]mirror.CodeMirror.CodeMirrorConfig::indentUnit;
			[email protected]emirror.CodeMirror.CodeMirrorConfig::smartIndent;
			[email protected]ror.CodeMirror.CodeMirrorConfig::tabSize;
			[email protected]codemirror.CodeMirror.CodeMirrorConfig::indentWithTabs;
			[email protected]odemirror.CodeMirror.CodeMirrorConfig::electricChars;
			[email protected].lib.codemirror.CodeMirror.CodeMirrorConfig::autoClearEmptyLines;
			if([email protected]ror.CodeMirrorConfig::keyMap!=null){
				[email protected]or.CodeMirror.CodeMirrorConfig::keyMap;
			}
			if([email protected]ror.CodeMirrorConfig::extraKeys!=null){
				[email protected]irror.CodeMirror.CodeMirrorConfig::extraKeys;
			}
			[email protected]demirror.CodeMirror.CodeMirrorConfig::lineWrapping;
			[email protected]emirror.CodeMirror.CodeMirrorConfig::lineNumbers;
			[email protected].codemirror.CodeMirror.CodeMirrorConfig::firstLineNumber;
			[email protected]or.CodeMirror.CodeMirrorConfig::gutter;
			[email protected]emirror.CodeMirror.CodeMirrorConfig::fixedGutter;
			[email protected]t.lib.codemirror.CodeMirror.CodeMirrorConfig::readOnly;
			
			if(cm!=null){
				js.onChange=function(){
					[email protected]r::change(Ljava/lang/String;)('');
				}
				js.onCursorActivity=function(){
					var [email protected]deMirror::cm;
					if($wnd.hlLine!=null){
						editor.setLineClass($wnd.hlLine,null,null);
					}
					$wnd.hlLine=editor.setLineClass(editor.getCursor().line,null,"activeline");
				}
			}
			return js;
		}-*/;
	}
	
	public void change(String msg){
		for(OnChangeListener lis:listeners){
			lis.onChange();
		}
	}
	
	public interface CodeMirrorCSS extends ClientBundle{
		@Source("CodeMirror.css")
		@NotStrict
		CssResource style();
	}

}