package hiro.yoshioka.sql.resource.view;

import hiro.yoshioka.sdh.DatabaseType;
import hiro.yoshioka.sql.IRequestListener;
import hiro.yoshioka.sql.ITransactionSQL;
import hiro.yoshioka.sql.ResourceSorter;
import hiro.yoshioka.sql.engine.GettingResourceRequest;
import hiro.yoshioka.sql.engine.HsqlServerManager;
import hiro.yoshioka.sql.engine.Request;
import hiro.yoshioka.sql.engine.SQLOperationType;
import hiro.yoshioka.sql.engine.SQLServerThread;
import hiro.yoshioka.sql.params.ConnectionProperties;
import hiro.yoshioka.sql.resource.DBResource;
import hiro.yoshioka.sql.resource.DBResourceTransfer;
import hiro.yoshioka.sql.resource.DBRoot;
import hiro.yoshioka.sql.resource.IDBResource;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.sql.resource.ITextExtension;
import hiro.yoshioka.sql.util.ConnectionSettingDialog;
import hiro.yoshioka.sql.util.SelectTableDialog;
import hiro.yoshioka.sql.util.SqlKeyHighlightDialog;
import hiro.yoshioka.util.ImageUtil;
import hiro.yoshioka.util.StringUtil;
import hiro.yoshioka.util.ui.IPressEnterListener;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;

public class DBResourceTreeViewer extends TreeViewer implements
		IRequestListener, IPressEnterListener, IMenuListener {
	List<IDBResourceTreeViewerListener> listenerList;
	IDBResourceTreeViewerContextMenuListener menuListener;
	ConnectionProperties latestSelectedConnectionProperties;
	DBResourceTreeSelection dbresource_selection;
	public static final String[] HEADER_NAME = { "Resource", "Comment" }; //$NON-NLS-1$ //$NON-NLS-2$
	public static final int[] HEADER_WIDTH = { 200, 110 };
	protected transient Log fLogger = LogFactory.getLog(getClass());
	ResourceSorter fResourceSorter = new ResourceSorter();
	DBTreeFilter fFilter = new DBTreeFilter();
	static final GridLayout RootGridLayout = new GridLayout();
	private boolean adjustedColumns = false;

	public DBResourceTreeViewer(Composite parent) {
		super(parent, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);

		try {
			int idx = 0;
			for (String name : HEADER_NAME) {
				TreeViewerColumn col = new TreeViewerColumn(this, SWT.NONE);
				col.getColumn().setText(name);
				col.getColumn().setWidth(HEADER_WIDTH[idx++]);
				col.setLabelProvider(new DBTreeLabelProvider(name));
			}
			getTree().setHeaderVisible(true);
			getTree().setLinesVisible(true);
			setUseHashlookup(true);

			// setTitleImage(Util.getImage(IImageFileConst.OUTLINE_CO_GIF));
			setContentProvider(new DBTreeContentProvider());
			// setLabelProvider(new DBTreeLabelProvider());
			setSorter(fResourceSorter);
			addFilter(fFilter);
			if (latestSelectedConnectionProperties != null
					&& latestSelectedConnectionProperties.size() <= 1) {
				setAutoExpandLevel(3);
			} else {
				setAutoExpandLevel(2);
			}
			setInput(null);

			int dragOption = DND.DROP_COPY | DND.DROP_MOVE;
			Transfer[] transfers = new Transfer[] { TextTransfer.getInstance(),
					DBResourceTransfer.getInstance() };
			// DragSourceListener
			addDragSupport(dragOption, transfers, new DragSourceAdapterImpl(
					this));
			MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
			menuMgr.setRemoveAllWhenShown(true);
			menuMgr.addMenuListener(DBResourceTreeViewer.this);
			final Menu menu = menuMgr.createContextMenu(getControl());
			getTree().addMenuDetectListener(new MenuDetectListener() {
				@Override
				public void menuDetected(MenuDetectEvent arg0) {
					menu.setVisible(true);
				}
			});

			getTree().addControlListener(new ControlAdapter() {
				@Override
				public void controlResized(ControlEvent e) {
					if (!adjustedColumns) {
						Tree tree = getTree();
						if (tree != null && !tree.isDisposed()) {
							int width = tree.getBounds().width - 15;
							tree.getColumn(0).setWidth(width / 20 * 13 - 2);
							tree.getColumn(1).setWidth(width / 20 * 7 - 3);
							adjustedColumns = true;
						}
					}
				}
			});
			SQLServerThread.getSQLServer().addRequestListner(this);
			// showDBResouces();

		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	@Override
	public void menuAboutToShow(IMenuManager manager) {
		final Object selection = getFirstSelection();
		final ConnectionProperties prop = getLatestSelectedConnectionProperties();
		DatabaseType databaseType = DatabaseType.UNKNOWN;
		if (prop == null) {
			manager.add(new Action(
					Messages.getString("DBResourceTreeViewer.OpenConnectionConfigDialog"), ImageUtil //$NON-NLS-1$
							.getImageDescriptor(ImageUtil.ACTION_00_TREE_REFRESH)) {
				@Override
				public void run() {
					if (selection instanceof ConnectionProperties) {
						openConfigDialog((ConnectionProperties) selection);
					} else {
						openConfigDialog();
					}
				}
			});
		} else {
			final ITransactionSQL sql = SQLServerThread.getSQLServer()
					.getTransactionSQL(prop);
			databaseType = prop.getDatabaseType();
			if (menuListener != null) {
				MenuManager connectionMenu = new MenuManager(
						Messages.getString("DBResourceTreeViewer.ConnectionMenu"), //$NON-NLS-1$
						ImageUtil
								.getImageDescriptor(ImageUtil.ACTION_32_CONNECT),
						"WoldConnectId"); //$NON-NLS-1$
				manager.add(connectionMenu);
				menuListener.createConnectionMenuShow(connectionMenu,
						databaseType, prop);
			}

			manager.add(new Separator());
			if (menuListener != null) {
				if (menuListener.hasEditorMenu()) {
					menuListener.createEditorContextMenuShow(manager);
				}
				manager.add(new Separator());
			}
			if (selection instanceof IDBResource) {
				final IDBResource res = (IDBResource) selection;

				if (res instanceof ITextExtension) {
					final ITextExtension text_res = (ITextExtension) res;
					MenuManager ddlMenu = new MenuManager(
							Messages.getString("DBResourceTreeViewer.DDL_Menu"), //$NON-NLS-1$
							ImageUtil
									.getImageDescriptor(ImageUtil.ACTION_83_INFO),
							"Wolf DDL Menu"); //$NON-NLS-1$
					if (text_res.hasText()) {
						ddlMenu.add(new Action(Messages
								.getString("DBResourceTreeViewer.ShowDDL")) { //$NON-NLS-1$
							@Override
							public void run() {
								Dialog dialog = new SqlKeyHighlightDialog(
										getTree().getShell(), text_res
												.getText());
								dialog.open();
							}
						});
					} else {
						ddlMenu.add(new Action("get DDL definition") { //$NON-NLS-1$
							@Override
							public void run() {
								GettingResourceRequest rRequest = new GettingResourceRequest(
										prop, SQLOperationType.GET_DDL);
								rRequest.selectionResource = res;
								rRequest.targetType = GettingResourceRequest.GettingTarget.ONLY_SELECTED_RESOURCE;
								if (SQLServerThread.runNowThisRequest(rRequest)) {
									try {
										SQLServerThread.getSQLServer()
												.saveConnectionProperties();
										Dialog dialog = new SqlKeyHighlightDialog(
												getTree().getShell(), text_res
														.getText());
										dialog.open();

									} catch (IOException e) {
										e.printStackTrace();
									}
								} else {
									System.err.println("fault!!!!!!!!!!!!");
								}
							}
						});
					}
					manager.add(ddlMenu);
					manager.add(new Separator());
				}

				if (menuListener != null) {
					menuListener.createResourceOfDBMenuShow(getTree()
							.getShell(), manager, databaseType, prop,
							getDBResourceTreeSelection());
				}
				manager.add(new Separator());
			}
		}
		MenuManager hsqlMenu = new MenuManager(
				Messages.getString("DBResourceTreeViewer.HsqldbMenu"), //$NON-NLS-1$
				ImageUtil.getImageDescriptor(ImageUtil.DB_HSQL), "WolfHsqlMenu"); //$NON-NLS-1$
		hsqlMenu.add(new HsqlDBAction(true));
		hsqlMenu.add(new HsqlDBAction(false));
		manager.add(hsqlMenu);
		manager.add(new Separator());

		manager.add(new Action("OnlineHelp", ImageUtil
				.getImageDescriptor(ImageUtil.ACTION_HELP)) {
			@Override
			public void run() {
				Program.launch("http://yonsama--blog.blogspot.com/2011/10/wolfdbmanager2-rcp-how-to-use-6-view.html"); //$NON-NLS-1$
			}
		});
	}

	public DBResourceTreeSelection getDBResourceTreeSelection() {
		return dbresource_selection;
	}

	public Object getFirstSelection() {
		IStructuredSelection sel = (IStructuredSelection) getSelection();
		if (sel != null) {
			return sel.getFirstElement();
		}
		return null;
	}

	@Override
	public ISelection getSelection() {
		TreeSelection selection = (TreeSelection) super.getSelection();
		try {
			Field elementsField = selection.getClass().getSuperclass()
					.getDeclaredField("elements"); //$NON-NLS-1$
			elementsField.setAccessible(true);
			Object[] elements = (Object[]) elementsField.get(selection);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return selection;
	}

	@Override
	protected void fireSelectionChanged(SelectionChangedEvent event) {
		if (getTree().getSelectionCount() > 0) {
			latestSelectedConnectionProperties = getConnectionPropRecurse(getTree()
					.getSelection()[0]);
		} else {
			latestSelectedConnectionProperties = null;
		}
		IStructuredSelection selection = (IStructuredSelection) event
				.getSelection();
		Object o = selection.getFirstElement();

		dbresource_selection = new DBResourceTreeSelection(selection);
		super.fireSelectionChanged(event);
		if (listenerList != null) {
			for (IDBResourceTreeViewerListener li : listenerList) {
				li.fireSelectionChanged(
						getLatestSelectedConnectionProperties(), o);
			}
		}
	}

	@Override
	protected void fireDoubleClick(DoubleClickEvent event) {
		super.fireDoubleClick(event);
		Object o = ((IStructuredSelection) event.getSelection())
				.getFirstElement();
		if (listenerList != null) {
			for (IDBResourceTreeViewerListener li : listenerList) {
				li.fireDoubleClick(getLatestSelectedConnectionProperties(), o);
			}
		}
	}

	public ConnectionProperties getLatestSelectedConnectionProperties() {
		return latestSelectedConnectionProperties;
	}

	public ConnectionProperties getConnectionPropertiesOf(IDBResource resource) {
		Widget w = findItem(resource);
		if (w instanceof TreeItem) {
			return getConnectionPropRecurse((TreeItem) w);
		}
		return null;
	}

	private ConnectionProperties getConnectionPropRecurse(TreeItem item) {

		Object o = item.getData();
		if (o instanceof ConnectionProperties) {
			return (ConnectionProperties) o;
		}
		TreeItem parent = item.getParentItem();
		if (parent != null) {
			return getConnectionPropRecurse(parent);
		}
		return null;
	}

	public void setDBResourceTreeViewerContextMenuListener(
			IDBResourceTreeViewerContextMenuListener listener) {
		this.menuListener = listener;
	}

	public void addDBResourceTreeViewerListener(
			IDBResourceTreeViewerListener listener) {
		if (this.listenerList == null) {
			this.listenerList = new ArrayList<IDBResourceTreeViewerListener>();
		}
		this.listenerList.add(listener);
	}

	public DBTreeFilter getFilter() {
		return fFilter;
	}

	public void setFilterPattern(String pattern) {
		fFilter.setPattern(pattern);
	}

	public void setSortMode(int mode) {
		fResourceSorter.setMode(mode);
		refresh();
	}

	// -------------------------------------------------------------
	private class HsqlDBAction extends Action {
		boolean forward;

		public HsqlDBAction(boolean forward) {
			super();
			this.forward = forward;
			String text = StringUtil.EMPTY_STRING;
			ImageDescriptor desc = null;
			if (forward) {
				text = Messages
						.getString("DBResourceTreeViewer.StartHsqldbServerProcess"); //$NON-NLS-1$
				setEnabled(!HsqlServerManager.isRunning());
				desc = ImageUtil.getImageDescriptor(ImageUtil.ACTION_RUNNING);
			} else {
				text = Messages
						.getString("DBResourceTreeViewer.ShutdownHsqldbServerProcess"); //$NON-NLS-1$
				setEnabled(HsqlServerManager.isRunning());
				desc = ImageUtil.getImageDescriptor(ImageUtil.ACTION_39_CANSEL);
			}

			setText(text);
			setToolTipText(text);
			setImageDescriptor(desc);
		}

		@Override
		public void run() {
			try {
				setEnabled(false);

				if (forward) {
					HsqlServerManager.getInstance().start();
				} else {
					HsqlServerManager.getInstance().stop();
				}

			} catch (RuntimeException e) {
				fLogger.error(StringUtil.EMPTY_STRING, e);
			} finally {

				setEnabled(true);
			}
		}
	}

	private class DragSourceAdapterImpl extends DragSourceAdapter {
		private TreeViewer viewer;

		public DragSourceAdapterImpl(TreeViewer viewer) {
			this.viewer = viewer;
		}

		public void dragSetData(DragSourceEvent event) {
			fLogger.warn("dragSetData(DragSourceEvent) - start"); //$NON-NLS-1$

			TreeItem[] items = getTree().getSelection();

			DBResource[] sels = new DBResource[items.length];
			for (int i = 0; i < sels.length; i++) {
				sels[i] = (DBResource) items[i].getData();
			}

			if (DBResourceTransfer.getInstance()
					.isSupportedType(event.dataType)) {
				fLogger.warn("DBResourceTransfer event.dataType"); //$NON-NLS-1$
				event.data = sels;
			} else if (TextTransfer.getInstance().isSupportedType(
					event.dataType)) {
				fLogger.warn("TextTransfer event.dataType"); //$NON-NLS-1$
				event.data = sels[0].getNameWithComment();
			}
			fLogger.warn(event.toString());
			fLogger.warn("dragSetData(DragSourceEvent) - end"); //$NON-NLS-1$
		}
	}

	public void refreshResourceLazy(final Object o) {
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				refresh(o);
			}
		});
	}

	@Override
	public void called_pre(final Request request, SQLOperationType operation) {
		fLogger.info(String.format(
				"[PRE] [%s] %s", request, request.getConnectionProperties())); //$NON-NLS-1$
		Display.getDefault().syncExec(new Runnable() {
			@Override
			public void run() {
				ConnectionProperties p = request.getConnectionProperties();
				refresh(p);
			}
		});
	}

	@Override
	public void beginTask(String taskName, int row) {
	}

	@Override
	public void called_done(final Request request,
			final SQLOperationType operation,
			final ConnectionProperties properteis, final Object o) {
		fLogger.info(StringUtil.EMPTY_STRING);
		fLogger.info(String.format("[Done] [%s] %s", request, properteis)); //$NON-NLS-1$
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				refresh(request.getConnectionProperties());
				DBRoot root = properteis.getDBRootResource();
				switch (operation) {
				case CONNECT:
				case RESOURCE_CAPTION:
					refresh();
					Set expandSet = new HashSet();
					if (latestSelectedConnectionProperties != null) {
						expandSet.add(latestSelectedConnectionProperties);
						DBRoot r = latestSelectedConnectionProperties
								.getDBRootResource();
						if (r != null) {
							expandSet.add(r);
						}
					}
					if (properteis != null) {
						expandSet.add(properteis);
						if (root != null) {
							expandSet.add(root);
						}
					}
					if (expandSet.size() > 0) {
						setExpandedElements(expandSet
								.toArray(new Object[expandSet.size()]));
					}
					break;
				case CLOSE:
					if (properteis.isConnected()) {
						fLogger.fatal("Illegal Status: change connected to false!! hase :"
								+ request.hasException());
						properteis.setConnected(false);
					}
					refresh();
					fLogger.info("setExpandedState:[false] " + properteis); //$NON-NLS-1$
					setExpandedState(properteis, false);
					break;
				case PREPARED_EXECUTE:
				case PREPARED_EXECUTE_QUERY:
				case EXECUTE:
					refresh(properteis.getDBRootResource()
							.getRecentryUsedResource());
					break;
				}
			}
		});
	}

	@Override
	public void subTask(String subTaskName) {
	}

	@Override
	public void worked(int i) {
	}

	@Override
	public void pressEnter(String text) {
		System.out.println("pressEnter[" + text + "]"); //$NON-NLS-1$ //$NON-NLS-2$
		setFilterPattern(text);
		refresh();
	}

	public void openConfigDialog() {
		openConfigDialog(null);
	}

	public void openRecordSelectionDialog(
			ConnectionProperties connectionProperties, Shell shell,
			IDBTable table) {
		SelectTableDialog dialog = new SelectTableDialog(connectionProperties,
				shell, table, null);
		dialog.open();
	}

	public void openConfigDialog(
			ConnectionProperties defaultConnectionProperties) {
		ConnectionSettingDialog dialog = new ConnectionSettingDialog(getTree()
				.getShell(), SQLServerThread.getSQLServer()
				.getConnectionSettingBean(), defaultConnectionProperties,
				ConnectionSettingDialog.MODE_NORMAL);
		if (defaultConnectionProperties != null) {
			dialog.setDoOpenDBConfigDialog(true);
		}
		if (dialog.open() == ConnectionSettingDialog.OK) {
			try {
				SQLServerThread.getSQLServer().changeConnectionProperties(
						dialog.getChangeType(), dialog.getConnectionSet());
				refresh();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
	}

	public IDBTable refreshTableColumnsOnTree(IDBTable procedure, boolean force) {
		if (procedure == null) {
			return null;
		}
		if (procedure.getColumns().length == 0 || force) {
			ConnectionProperties connectionProperties = getConnectionPropertiesOf(procedure);
			GettingResourceRequest req = new GettingResourceRequest(
					connectionProperties);
			req.targetType = GettingResourceRequest.GettingTarget.ONLY_TABLE;
			req.selectionResource = procedure;
			try {
				SQLServerThread.runNowThisRequest(req);

				refreshResourceLazy(req.selectionResource);
				reveal(req.selectionResource);
			} catch (Exception e) {
				fLogger.debug(StringUtil.EMPTY_STRING, e);
			}
			return (IDBTable) req.selectionResource;

		}
		return procedure;
	}

}
