package common.plugin.literal.popup.actions;

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

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.texteditor.ITextEditor;

/**
 * トグルリテラルアクション
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class ToggleLiteralAction implements IEditorActionDelegate {
	/** エディタパート */
	private IEditorPart targetPart = null;

	/**
	 * @see org.eclipse.ui.IEditorActionDelegate
	 * #setActiveEditor(org.eclipse.jface.action.IAction, org.eclipse.ui.IEditorPart)
	 */
	@Override
	public void setActiveEditor(final IAction action, final IEditorPart targetEditor) {
		this.targetPart = targetEditor;
	}

	/**
	 * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
	 */
	@Override
	public void run(final IAction action) {
		IWorkbenchPage wbp = this.targetPart.getEditorSite().getPage();
		ITextSelection sel = (ITextSelection)wbp.getSelection();
		String str = sel.getText();
		if (str.isEmpty()) {
			return;
		}

		int loc = -1;
		Boolean literal = null;
		List<TextEdit> list = new ArrayList<TextEdit>();
		MultiTextEdit mte = new MultiTextEdit();
		for (int i = 0; i < sel.getLength(); i++) {
			if (str.codePointAt(i) == '"') {
				if (literal == null) {
					int pos = position(i, loc);
					list.add(new DeleteEdit(sel.getOffset() + pos, i - pos + 1));

					loc = -1;
					literal = Boolean.FALSE;
				} else if (Boolean.TRUE.equals(literal)) {
					list.add(new InsertEdit(sel.getOffset() + i, "\\"));
				} else {
					mte.addChildren(list.toArray(new TextEdit[list.size()]));
					list.clear();
					loc = i;
				}
			} else if (str.codePointAt(i) == '+' || str.codePointAt(i) == ' ') {
				if (literal == null && loc < 0) {
					loc = i;
				}
			} else if (str.codePointAt(i) == '\\') {
				if (Boolean.TRUE.equals(literal)) {
					list.add(new InsertEdit(sel.getOffset() + i, "\\"));
				} else if (Boolean.FALSE.equals(literal)) {
					if (i + 1 < sel.getLength()) {
						if (str.codePointAt(i + 1) == '\\' || str.codePointAt(i + 1) == '"') {
							list.add(new DeleteEdit(sel.getOffset() + i, 1));
							i++;
						} else if (str.codePointAt(i + 1) == 'n') {
							list.add(new DeleteEdit(sel.getOffset() + i, 2));
							i++;
						}
					}
				}
			} else if (str.codePointAt(i) == '\n') {
				int pos = i;
				if (0 < i && str.codePointAt(i - 1) == '\r') {
					pos = i - 1;
				}

				if (Boolean.TRUE.equals(literal)) {
					list.add(new InsertEdit(sel.getOffset() + pos, "\\n\""));
				}

				eol(mte, list, literal, sel.getOffset(), pos, loc);

				loc = -1;
				literal = null;
			} else if (!Character.isWhitespace(str.codePointAt(i))) {
				if (literal == null) {
					if (0 < mte.getChildrenSize()) {
						list.add(new InsertEdit(sel.getOffset() + position(i, loc), "+ \""));
					} else {
						list.add(new InsertEdit(sel.getOffset() + position(i, loc), "\""));
					}

					loc = -1;
					literal = Boolean.TRUE;
				}
			}
		}

		eol(mte, list, literal, sel.getOffset(), sel.getLength(), loc);

		ITextEditor te = (ITextEditor)this.targetPart;
		IDocument doc = te.getDocumentProvider().getDocument(this.targetPart.getEditorInput());
		try {
			mte.apply(doc);
		} catch (BadLocationException ex) {
			MessageDialog.openInformation(
					this.targetPart.getEditorSite().getShell(), "", sel.getText());
		}
	}

	/**
	 * @see org.eclipse.ui.IActionDelegate
	 * #selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
	 */
	@Override
	public void selectionChanged(final IAction action, final ISelection selection) {
		// NOP
	}

	/**
	 * 位置設定
	 *
	 * @param cur 現在位置
	 * @param loc ロケーション
	 * @return 位置
	 */
	private int position(final int cur, final int loc) {
		if (0 <= loc) {
			return loc;
		}
		return cur;
	}

	/**
	 * 行末処理
	 *
	 * @param mte テキストエディット
	 * @param list リスト
	 * @param literal リテラルフラグ
	 * @param offset オフセット
	 * @param pos ポジション
	 * @param loc ロケーション
	 */
	private void eol(final MultiTextEdit mte, final List<TextEdit> list,
			final Boolean literal, final int offset, final int pos, final int loc) {

		if (Boolean.TRUE.equals(literal)) {
			mte.addChildren(list.toArray(new TextEdit[list.size()]));
		} else if (Boolean.FALSE.equals(literal)) {
			if (0 <= loc) {
				mte.addChild(new DeleteEdit(offset + loc, pos - loc));
			}
		}
		list.clear();
	}
}
