/*
 * Copyright (c) 2015 NeoDoc SARL. All rights reserved.
 * 
 * Author: Fabián Mandelbaum
 * 
 * Based on Delete command which has Copyright (c) 2000-2013 by Pixware SARL,
 * which is part of the XMLmind XML Editor project.
 */
package com.calenco.xxe.ext;

import com.xmlmind.xml.doc.Element;
import com.xmlmind.xml.doc.Node;
import com.xmlmind.xml.doc.TextNode;
import com.xmlmind.xml.name.Name;
import com.xmlmind.xml.name.Namespace;
import com.xmlmind.xmledit.cmd.RecordableCommand;
import com.xmlmind.xmledit.edit.ElementEditor;
import com.xmlmind.xmledit.edit.Mark;
import com.xmlmind.xmledit.edit.MarkManager;
import com.xmlmind.xmledit.edit.NodeMark;
import com.xmlmind.xmledit.edit.TextEditor;
import com.xmlmind.xmledit.edit.TextLocation;
import com.xmlmind.xmledit.edit.XMLClipboard;
import com.xmlmind.xmledit.view.DocumentView;

public class CustomDelete extends RecordableCommand {
	private final boolean cut;
	private boolean tryingToDelElementWithKeyboard = false;

	// -----------------------------------------------------------------------

	public CustomDelete() {
		this(false);
	}

	protected CustomDelete(boolean cut) {
		this.cut = cut;
	}

	public boolean prepare(DocumentView docView, String parameter, int x, int y) {
		System.out.println(String.format("+++DBG: Parameter: '%s'", parameter));
		MarkManager markManager = docView.getMarkManager();
		if (markManager == null) {
			return false;
		}

		ElementEditor elementEditor = docView.getElementEditor();

		NodeMark selected = markManager.getSelected();
		if (selected != null) {
			//System.out.println("+++DBG: Looks like elements or blocks are selected...");
			Element editedElement = selected.getNode().getParentElement();
			if (editedElement != null) {
				//System.out.println("+++DBG: editing element <" + editedElement.getLocalName() + ">");
				NodeMark selected2 = markManager.getSelected2();
				if (selected2 == null) {
					selected2 = selected;
				}
				elementEditor.editElement(editedElement);
				Node n1 = selected.getNode();
				//System.out.println("+++DBG: Node1 (" + n1.getXPath() + ") is explicitly selected, good...");
				Node n2 = selected2.getNode();
				//System.out.println("+++DBG: Node2 (" + n2.getXPath() + ") is explicitly selected, good...");
				if ((isForce(parameter) && elementEditor.canEditElement() && Node
						.canReplace(n1, n2)) || elementEditor.canDelete(n1, n2)) {
					// Flag to move the cursor to the beginning/end of the element before deletion and set revisionflag=deleted
					if (n1 == n2 && n1.getType() == Node.Type.ELEMENT && isDelOrBackSpaceKey(parameter)) {
						// System.out.println("+++DBG: Trying to remove element with keyboard (del/backspace)");
						Element e = (Element) n1;
						e.putAttribute(Name.get(Namespace.NONE, "revisionflag"), "deleted");
						tryingToDelElementWithKeyboard = true;
					}
					return true;
				}
			}
		} else {
			//System.out.println("+++DBG: Looks like text is selected...");
			TextLocation dot = markManager.getDot();
			TextLocation mark = markManager.getMark();

			if (dot != null) {
				if (mark != null
						&& (mark.getTextNode() != dot.getTextNode() || mark
								.getOffset() != dot.getOffset())) {
					return docView.getTextEditor().canDelete(mark, dot);
				} else {
					//System.out.println("+++DBG: mark is null!");
					Node child = null;

					if (isImplicitElement(parameter)) {
						child = dot.getTextNode().getParentElement();
						//System.out.println("+++DBG: Got implicitElement child " + child.getXPath());
					} else if (isImplicitNode(parameter)) {
						child = dot.getTextNode();
						//System.out.println("+++DBG: Got implicitNode child " + child.getXPath());
					}

					if (child != null) {
						Element editedElement = child.getParentElement();
						if (editedElement != null) {
							elementEditor.editElement(editedElement);
							if ((isForce(parameter)
									&& elementEditor.canEditElement() && Node
										.canReplace(child, child))
									|| elementEditor.canDelete(child, child)) {
								return true;
							}
						}
//					} else {
//						System.out.println("+++DBG: child is null!");
					}
				}
//			} else {
//				System.out.println("+++DBG: dot is null!");
			}
		}

		return false;
	}

	private static final boolean isForce(String parameter) {
		return (parameter != null && parameter.startsWith("force"));
	}

	private static final boolean isImplicitNode(String parameter) {
		return (parameter != null && parameter.endsWith("[implicitNode]"));
	}

	private static final boolean isImplicitElement(String parameter) {
		return (parameter != null && parameter.endsWith("[implicitElement]"));
	}
	
	private static final boolean isDelOrBackSpaceKey(String parameter) {
		return (parameter != null && parameter.startsWith("keypress"));
	}
	
	private static final boolean isBackwards(String parameter) {
		return (parameter != null && parameter.contains("backwards"));
	}

	protected Object doExecute(DocumentView docView, String parameter, int x,
			int y) {
		ElementEditor elementEditor = docView.getElementEditor();
		String deleted = null;

		MarkManager markManager = docView.getMarkManager();
		markManager.beginMark();

		NodeMark selected = markManager.getSelected();
		if (selected != null) {
			NodeMark selected2 = markManager.getSelected2();
			if (selected2 == null)
				selected2 = selected;
			
			if (tryingToDelElementWithKeyboard) {
				docView.executeCommand("moveDotTo", isBackwards(parameter) ? "elementStart" : "elementEnd", x, y);
			}

			Element editedElement = selected.getNode().getParentElement();
			
			if (cut) {
				deleted = cut(elementEditor, selected.getNode(),
						selected2.getNode());
			} else {
				delete(elementEditor, selected.getNode(), selected2.getNode());
			}

			if (!tryingToDelElementWithKeyboard && editedElement != null && !editedElement.hasAttribute(Name.get(Namespace.NONE, "revisionflag"))) {
				//System.out.println("+++DBG: doExecute (element change) setting revisionflag=modified");
				editedElement.putAttribute(Name.get(Namespace.NONE, "revisionflag"), "modified");
			}
			
			markManager.remove(Mark.SELECTED);
			markManager.remove(Mark.SELECTED2);
		} else {
			TextLocation dot = markManager.getDot();
			TextLocation mark = markManager.getMark();

			if (mark != null
					&& (mark.getTextNode() != dot.getTextNode() || mark
							.getOffset() != dot.getOffset())) {
				Element editedElement = mark.getNode().getParentElement();
				if (cut) {
					deleted = cut(docView.getTextEditor(), mark, dot);
				} else {
					delete(docView.getTextEditor(), mark, dot);
				}
				if (editedElement != null && !editedElement.hasAttribute(Name.get(Namespace.NONE, "revisionflag"))) {
					//System.out.println("+++DBG: doExecute (text change) setting revisionflag=modified");
					editedElement.putAttribute(Name.get(Namespace.NONE, "revisionflag"), "modified");
				}
			} else if (isImplicitElement(parameter)) {
				Element child = dot.getTextNode().getParentElement();
				Element parent = child.getParentElement();
				deleted = cut(elementEditor, child, child);
				if (parent != null && !parent.hasAttribute(Name.get(Namespace.NONE, "revisionflag"))) {
					//System.out.println("+++DBG: doExecute (text change implicitElement) setting revisionflag=modified");
					parent.putAttribute(Name.get(Namespace.NONE, "revisionflag"), "modified");
				}
			} else if (isImplicitNode(parameter)) {
				TextNode textNode = dot.getTextNode();
				Element parent = textNode.getParentElement();
				deleted = cut(elementEditor, textNode, textNode);
				if (parent != null && !parent.hasAttribute(Name.get(Namespace.NONE, "revisionflag"))) {
					//System.out.println("+++DBG: doExecute (text change implicitNode) setting revisionflag=modified");
					parent.putAttribute(Name.get(Namespace.NONE, "revisionflag"), "modified");
				}
			}

			// Remove used or useless mark.
			markManager.remove(Mark.MARK);
		}
		docView.describeUndo(cut ? "Cut" : "Delete");//Msg.msg("D.cut") : Msg.msg("D.delete"));

		if (cut) {
			XMLClipboard.getInstance().set(deleted);
			// notifyContextChangeListeners not useful here.
		}

		markManager.endMark();

		return null;
	}

	protected String cut(ElementEditor elementEditor, Node node1, Node node2) {
		return elementEditor.cut(node1, node2);
	}

	protected String cut(TextEditor textEditor, TextLocation mark,
			TextLocation dot) {
		return textEditor.cut(mark, dot);
	}

	protected void delete(ElementEditor elementEditor, Node node1, Node node2) {
		elementEditor.delete(node1, node2);
	}

	protected void delete(TextEditor textEditor, TextLocation mark,
			TextLocation dot) {
		textEditor.delete(mark, dot);
	}
}
