package com.oim.test.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.html.HTMLDocument;
import org.w3c.dom.html.HTMLElement;

import com.oim.common.util.EmojiUtil;
import com.sun.javafx.webkit.Accessor;
import com.sun.javafx.webkit.InputMethodClientImpl;
import com.sun.javafx.webkit.KeyCodeMap;
import com.sun.webkit.WebPage;
import com.sun.webkit.dom.HTMLBodyElementImpl;
import com.sun.webkit.event.WCInputMethodEvent;
import com.sun.webkit.event.WCKeyEvent;
import com.sun.webkit.event.WCMouseEvent;

import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.event.EventHandler;
import javafx.scene.effect.BlendMode;
import javafx.scene.input.DragEvent;
import javafx.scene.input.InputMethodEvent;
import javafx.scene.input.InputMethodHighlight;
import javafx.scene.input.InputMethodTextRun;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;

public class WritePane extends VBox {
	private static final Map<Object, Integer> idMap = new HashMap<Object, Integer>();
	private String fontName = "Microsoft YaHei";
	private int fontSize = 12;
	private Color color = Color.BLACK;
	private boolean bold = false;
	private boolean underline = false;
	private boolean italic = false;

	WebView webView = new WebView();
	WebPage webPage;
	WebEngine webEngine;
	static {
		idMap.put(MouseButton.NONE, WCMouseEvent.NOBUTTON);
		idMap.put(MouseButton.PRIMARY, WCMouseEvent.BUTTON1);
		idMap.put(MouseButton.MIDDLE, WCMouseEvent.BUTTON2);
		idMap.put(MouseButton.SECONDARY, WCMouseEvent.BUTTON3);

		idMap.put(MouseEvent.MOUSE_PRESSED, WCMouseEvent.MOUSE_PRESSED);
		idMap.put(MouseEvent.MOUSE_RELEASED, WCMouseEvent.MOUSE_RELEASED);
		idMap.put(MouseEvent.MOUSE_MOVED, WCMouseEvent.MOUSE_MOVED);
		idMap.put(MouseEvent.MOUSE_DRAGGED, WCMouseEvent.MOUSE_DRAGGED);

		idMap.put(KeyEvent.KEY_PRESSED, WCKeyEvent.KEY_PRESSED);
		idMap.put(KeyEvent.KEY_RELEASED, WCKeyEvent.KEY_RELEASED);
		idMap.put(KeyEvent.KEY_TYPED, WCKeyEvent.KEY_TYPED);
	}

	public WritePane() {
		initComponent();
		iniEvent();
	}

	private void initComponent() {
		this.getChildren().add(webView);
		webEngine = webView.getEngine();
		webPage = Accessor.getPageFor(webEngine);
		webPage.setEditable(true);
		webView.setFocusTraversable(true);
		// webView.getEngine().setUserStyleSheetLocation(getClass().getResource("/resources/common/css/webview.css").toExternalForm());
		// webPage.setBackgroundColor(255);
		webView.setPrefWidth(300);
		// webView.setOpacity(0.92);
		webView.setBlendMode(BlendMode.DARKEN);
		webView.setOnDragDropped(a -> {

		});

		initializeHtml();
	}

	private void iniEvent() {
		// TODO Auto-generated method stub

	}

	public void setWebOnDragDropped(EventHandler<? super DragEvent> value) {
		webView.setOnDragDropped(value);
	}

	private void updateStyle() {
		Document doc = webPage.getDocument(webPage.getMainFrame());
		if (doc instanceof HTMLDocument) {
			HTMLDocument htmlDocument = (HTMLDocument) doc;
			HTMLElement htmlDocumentElement = (HTMLElement) htmlDocument.getDocumentElement();
			HTMLElement htmlBodyElement = (HTMLElement) htmlDocumentElement.getElementsByTagName("body").item(0);
			htmlBodyElement.setAttribute("style", createStyleValue().toString());
		}
	}

	private StringBuilder createStyle() {
		StringBuilder style = new StringBuilder();
		style.append("style=\"");
		style.append(createStyleValue());
		style.append("\"");
		return style;
	}

	private StringBuilder createStyleValue() {
		StringBuilder style = new StringBuilder();

		// style.append("background-color:rgba(0,152,50,0.7);");
		style.append("word-wrap:break-word;");// word-wrap: break-word;
		style.append("font-family:").append(fontName).append(";");
		style.append("font-size:").append(fontSize).append("px;");
		if (underline) {
			style.append("margin-top:0;text-decoration:underline;");
		} else {
			style.append("margin-top:0;");
		}
		if (italic) {
			style.append("font-style:italic;");
		}
		// if (italic) {
		// style.append("font-style:oblique;");
		// }
		if (bold) {
			style.append("font-weight:bold;");
		}
		if (null != color) {
			String c = colorValueToHex(color);
			style.append("color:#");
			style.append(c);
			style.append(";");
		}
		return style;
	}

	private String colorValueToHex(Color c) {
		return String.format((Locale) null, "%02x%02x%02x", Math.round(c.getRed() * 255),
				Math.round(c.getGreen() * 255), Math.round(c.getBlue() * 255));
	}

	public void initializeHtml() {
		// initializeHtml(true);
		webEngine.load(this.getClass().getResource("/resources/chat/html/write.html").toString());

		webEngine.getLoadWorker().stateProperty().addListener((ObservableValue<? extends State> ov, State oldState, State newState) -> {

			if (newState == State.SUCCEEDED) {
				// System.out.println(webEngine.getDocument());
			}
		});
		webView.setOnInputMethodTextChanged(new EventHandler<InputMethodEvent>() {

			@Override
			public void handle(InputMethodEvent e) {
				processInputMethodEvent2(e);
			}
		});

		webView.setOnKeyPressed(new EventHandler<KeyEvent>() {

			@Override
			public void handle(KeyEvent e) {
				processKeyEvent(e);
			}
		});
	}

	private void processInputMethodEvent2(InputMethodEvent ie) {
		if (webPage == null) {
			return;
		}
		String text = ie.getCommitted();
		boolean isEmoji = EmojiUtil.isEmoji(text);
		if (isEmoji) {
			//String path = "Resources/Images/Face/emoji/23x23/";
			//text = FaceUtil.toEmojiImage(text, path, ".png");
			if (null != text && !"".equals(text)) {
				insertSelectionHtml(text);
			}
		} else {
			InputMethodEvent e = new InputMethodEvent(
					ie.getSource(),
					ie.getTarget(),
					ie.getEventType(),
					ie.getComposed(),
					text,
					ie.getCaretPosition());

			WCInputMethodEvent imEvent = InputMethodClientImpl.convertToWCInputMethodEvent(e);
			if (webPage.dispatchInputMethodEvent(imEvent)) {
				ie.consume();
				return;
			}
		}
	}

	void processInputMethodEvent(InputMethodEvent ie) {
		if (webPage == null) {
			return;
		}
		String text = ie.getCommitted();
		// String path = "Resources/Images/Face/emoji/23x23/";
		// text = FaceUtil.toEmojiImage(text, path, ".png");
		// System.out.println(text);
		if (null != text && !"".equals(text)) {
			// insertSelectionHtml( text) ;
		}

		// String t="&lt;img &nbsp;value=\"0x1f604.png\"
		// src=\"file:/E:/Workspaces/Own/oim/svn/oim-fx/Resources/Images/Face/emoji/23x23/0x1f604.png\"
		// /&gt;";
		InputMethodEvent e = new InputMethodEvent(
				ie.getSource(),
				ie.getTarget(),
				ie.getEventType(),
				ie.getComposed(),
				text,
				ie.getCaretPosition());
		// if (!webView.getInputMethodClient().getInputMethodState()) {
		// ie.consume();
		// return;
		// }

		WCInputMethodEvent imEvent = convertToWCInputMethodEvent(e);
		if (webPage.dispatchInputMethodEvent(imEvent)) {
			ie.consume();
			return;
		}
	}

	public static WCInputMethodEvent convertToWCInputMethodEvent(InputMethodEvent ie) {
		List<Integer> underlines = new ArrayList<Integer>();
		StringBuilder composed = new StringBuilder();
		int pos = 0;

		// Scan the given composedText to find input method highlight attribute
		// runs.
		for (InputMethodTextRun run : ie.getComposed()) {
			String rawText = run.getText();
			System.out.println("rawText:" + rawText);
			// Convert highlight information of the attribute run into a
			// CompositionUnderline.
			InputMethodHighlight imh = run.getHighlight();
			underlines.add(pos);
			underlines.add(pos + rawText.length());
			// WebKit CompostionUnderline supports only two kinds of
			// highlighting
			// attributes, thin and thick underlines. The SELECTED_CONVERTED
			// and SELECTED_RAW attributes of JavaFX are mapped to the thick
			// one.
			underlines.add((imh == InputMethodHighlight.SELECTED_CONVERTED ||
					imh == InputMethodHighlight.SELECTED_RAW) ? 1 : 0);
			pos += rawText.length();
			composed.append(rawText);
		}

		int size = underlines.size();
		// In case there's no highlight information, create an underline element
		// for the entire text
		if (size == 0) {
			underlines.add(0);
			underlines.add(pos);
			underlines.add(0); // thin underline
			size = underlines.size();
		}
		int[] attributes = new int[size];
		for (int i = 0; i < size; i++) {
			attributes[i] = underlines.get(i);
		}
		System.out.println(composed);
		return new WCInputMethodEvent(ie.getCommitted(), composed.toString(),
				attributes, ie.getCaretPosition());
	}

	private void processKeyEvent(KeyEvent ev) {
		if (webPage == null)
			return;

		String text = null;
		String keyIdentifier = null;
		int windowsVirtualKeyCode = 0;
		if (ev.getEventType() == KeyEvent.KEY_TYPED) {
			text = ev.getCharacter();
		} else {
			KeyCodeMap.Entry keyCodeEntry = KeyCodeMap.lookup(ev.getCode());
			keyIdentifier = keyCodeEntry.getKeyIdentifier();
			windowsVirtualKeyCode = keyCodeEntry.getWindowsVirtualKeyCode();
		}
		System.out.println(ev.getText());
		WCKeyEvent keyEvent = new WCKeyEvent(
				idMap.get(ev.getEventType()),
				text,
				keyIdentifier,
				windowsVirtualKeyCode,
				ev.isShiftDown(), ev.isControlDown(),
				ev.isAltDown(), ev.isMetaDown(), System.currentTimeMillis());
		if (webPage.dispatchKeyEvent(keyEvent)) {
			ev.consume();
		}
	}

	public void initializeHtml(boolean createStyle) {
		StringBuilder html = new StringBuilder();

		html.append("<!doctype html>");
		html.append("<html>");
		html.append("	<head>");
		html.append("		<meta charset=\"UTF-8\">");
		html.append("		<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
		html.append("		<title></title>");
		html.append("	</head>");
		html.append("	<body contenteditable=\"true\" ").append((createStyle ? createStyle() : "")).append("></body>");
		html.append("</html>");
		webPage.load(webPage.getMainFrame(), html.toString(), "text/html");
	}

	public String getHtml() {
		String html = webPage.getHtml(webPage.getMainFrame());
		return html;
	}

	public void insert(String text) {
		Document doc = webPage.getDocument(webPage.getMainFrame());
		if (doc instanceof HTMLDocument) {
			HTMLDocument htmlDocument = (HTMLDocument) doc;
			HTMLElement htmlDocumentElement = (HTMLElement) htmlDocument.getDocumentElement();
			HTMLElement htmlBodyElement = (HTMLElement) htmlDocumentElement.getElementsByTagName("body").item(0);

			if (htmlBodyElement instanceof HTMLBodyElementImpl) {
				// HTMLBodyElementImpl htmlBodyElementImpl =
				// (HTMLBodyElementImpl) htmlBodyElement;
				// Element e = htmlBodyElementImpl.getOffsetParent();
				// htmlBodyElementImpl.insertBefore(newChild, refChild)
				// htmlDocument.
			}

			Element e = htmlDocument.createElement("div");
			e.setTextContent(text);

			int positionOffset = webPage.getClientInsertPositionOffset();
			int index = (positionOffset + 1);
			NodeList nodeList = htmlBodyElement.getChildNodes();
			int length = nodeList.getLength();
			if (index < 0) {
				htmlBodyElement.appendChild(e);
			} else if (index < length) {
				org.w3c.dom.Node node = nodeList.item(index);
				// htmlBodyElement.
				htmlBodyElement.insertBefore(e, node);
			} else {
				htmlBodyElement.appendChild(e);
			}

			webPage.getClientInsertPositionOffset();

			// webView.set
			// System.out.println(htmlDocument.);

			//
			// org.w3c.dom.Node n = htmlBodyElement.getLastChild();
			//
			//
			//
			// webPage.getClientInsertPositionOffset();
			// // htmlBodyElement.ge
			// // hd.appendChild(e);
			// htmlDocument.getp
			//
		}
	}

	public String getFontName() {
		return fontName;
	}

	public void setFontName(String fontName) {
		this.fontName = fontName;
		updateStyle();
	}

	public int getFontSize() {
		return fontSize;
	}

	public void setFontSize(int fontSize) {
		this.fontSize = fontSize;
		updateStyle();
	}

	public Color getColor() {
		return color;
	}

	public String getWebColor() {
		return colorValueToHex(color);
	}

	public void setColor(Color color) {
		this.color = color;
		updateStyle();
	}

	public boolean isBold() {
		return bold;
	}

	public void setBold(boolean bold) {
		this.bold = bold;
		updateStyle();
	}

	public boolean isUnderline() {
		return underline;
	}

	public void setUnderline(boolean underline) {
		this.underline = underline;
		updateStyle();
	}

	public boolean isItalic() {
		return italic;
	}

	public void setItalic(boolean italic) {
		this.italic = italic;
		updateStyle();
	}

	public void insertSelectionHtml(String html) {
		// String js=getJavaScript(escapeJavaStyleString(html, true, true));
		String js = createSelectionJavaScript(html);
		webEngine.executeScript(js);
	}

	public void insertLastHtml(String html) {
		// String js=getJavaScript(escapeJavaStyleString(html, true, true));
		String js = createLastJavaScript(html);
		webEngine.executeScript(js);
	}

	private String createSelectionJavaScript(String html) {
		StringBuilder js = new StringBuilder();
		js.append("function insertHtmlAtCursor(html) {\n");
		js.append("    var range, node;\n");
		js.append("    var hasSelection = window.getSelection;\n");
		js.append("    var selection = window.getSelection();\n");
		js.append("    if (hasSelection &&selection.rangeCount>0&& selection.getRangeAt)  {\n");
		js.append("        range = window.getSelection().getRangeAt(0);\n");
//		js.append("		   var element = document.createElement(\"div\");\n");
//		js.append("		   element.innerHTML = html;\n");
		// js.append(" node = range.createContextualFragment(html);\n");

//		js.append("		   var nodes = element.childNodes;");
//		js.append("		   var l = nodes.length;");
//		js.append("		   for (var i = 0; i < l; i++) {");
//		js.append("		       node = nodes[i];");
//		js.append("		       range.insertNode(node);");
//		js.append("		   }");
		// js.append(" range.setStartAfter(node);");
		// js.append(" range.insertNode(node);\n");
//		js.append("        if(node){\n");
//		js.append("        		range.setStartAfter(node);\n");
//		js.append("        }\n");
		// js.append(" range.setStartAfter(node);\n");

		 js.append(" range = selection.getRangeAt(0);\n");
		 js.append(" range.deleteContents();\n");
		// Range.createContextualFragment() would be useful here but is
		// non-standard and not supported in all browsers (IE9, for one)
		 js.append(" var element = document.createElement(\"div\");\n");
		 js.append(" element.innerHTML = html;\n");
		 js.append(" var frag = document.createDocumentFragment(), node,lastNode;\n");
		 js.append(" while ((node = element.firstChild)) {\n");
		 js.append(" lastNode = frag.appendChild(node);\n");
		 js.append(" }\n");
		 js.append(" range.insertNode(frag);\n");
		
		 js.append(" if (lastNode) {// Preserve the selection\n");
		 js.append(" range = range.cloneRange();\n");
		 js.append(" range.setStartAfter(lastNode);\n");
		 js.append(" range.collapse(true);\n");
		 //js.append(" selection.remorangeveAllRanges();\n");
		 js.append(" selection.addRange(range);\n");
		 js.append(" }\n");

		js.append("    } else if (document.selection && document.selection.createRange) {\n");
		js.append("        document.selection.createRange().pasteHTML(html);\n");
		js.append("    } else {\n");
		js.append("        var element = document.createElement('div');\n");
		js.append("        element.innerHTML = html;");
		js.append("        node = element.childNodes[0];");
		js.append("        document.body.appendChild(node);\n");
		js.append("    }");
		js.append("}");
		js.append("insertHtmlAtCursor('");
		js.append(html);
		js.append("');");
		String jsInsertHtml = js.toString();
		return jsInsertHtml;
	}

	private String createLastJavaScript(String html) {
		StringBuilder js = new StringBuilder();
		js.append("function insertHtmlAtCursor(html) {\n");
		js.append("    var range, node;\n");
		js.append("    var element = document.createElement('div');\n");
		js.append("    element.innerHTML = html;");
		js.append("    node = element.childNodes[0];");
		js.append("    document.body.appendChild(node);\n");
		js.append("}");
		js.append("insertHtmlAtCursor('");
		js.append(html);
		js.append("');");
		String jsInsertHtml = js.toString();
		return jsInsertHtml;
	}

	private String hex(int i) {
		return Integer.toHexString(i);
	}

	public String escapeJavaStyleString(String str, boolean escapeSingleQuote, boolean escapeForwardSlash) {
		StringBuilder out = new StringBuilder("");
		if (str == null) {
			return null;
		}
		int sz;
		sz = str.length();
		for (int i = 0; i < sz; i++) {
			char ch = str.charAt(i);

			// handle unicode
			if (ch > 0xfff) {
				out.append("\\u").append(hex(ch));
			} else if (ch > 0xff) {
				out.append("\\u0").append(hex(ch));
			} else if (ch > 0x7f) {
				out.append("\\u00").append(hex(ch));
			} else if (ch < 32) {
				switch (ch) {
				case '\b':
					out.append('\\');
					out.append('b');
					break;
				case '\n':
					out.append('\\');
					out.append('n');
					break;
				case '\t':
					out.append('\\');
					out.append('t');
					break;
				case '\f':
					out.append('\\');
					out.append('f');
					break;
				case '\r':
					out.append('\\');
					out.append('r');
					break;
				default:
					if (ch > 0xf) {
						out.append("\\u00").append(hex(ch));
					} else {
						out.append("\\u000").append(hex(ch));
					}
					break;
				}
			} else {
				switch (ch) {
				case '\'':
					if (escapeSingleQuote) {
						out.append('\\');
					}
					out.append('\'');
					break;
				case '"':
					out.append('\\');
					out.append('"');
					break;
				case '\\':
					out.append('\\');
					out.append('\\');
					break;
				case '/':
					if (escapeForwardSlash) {
						out.append('\\');
					}
					out.append('/');
					break;
				default:
					out.append(ch);
					break;
				}
			}
		}
		return out.toString();
	}
}