package com.quantum.actions;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import com.quantum.ImageStore;
import com.quantum.Messages;
import com.quantum.model.Bookmark;
import com.quantum.model.BookmarkCollection;
import com.quantum.sql.MultiSQLServer;
import com.quantum.sql.SQLResultSetCollection;
import com.quantum.sql.SQLResultSetResults;
import com.quantum.sql.SQLResults;
import com.quantum.sql.SQLUpdateResults;
import com.quantum.ui.dialog.ExceptionDisplayDialog;
import com.quantum.ui.dialog.SQLExceptionDialog;
import com.quantum.ui.dialog.SimpleSelectionDialog;
import com.quantum.util.connection.ConnectionUtil;
import com.quantum.view.LogProxy;
import com.quantum.view.tableview.TableView;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.actions.SelectionListenerAction;

/**
 * @author BC
 */
public abstract class BaseExecuteAction extends SelectionListenerAction {
	
	class Metrics {
		int resultCount = 0;
		int resultUpdateCount = 0;
		int errorCount = 0;
		int resultsDisplayed = 0;
		double queryDuration= 0.0;
		
		public void measure(SQLResults results) {
			if (results == null) {
				errorCount++;
			} else {
				queryDuration += results.getTime()/1000.0; 
				resultCount++;
				if (results.isResultSet()) {
					resultsDisplayed++;
				} else {
					resultUpdateCount += ((SQLUpdateResults) results).getUpdateCount();
				}
			}
		}
		
		public boolean hasErrors() {
			return this.errorCount > 0;
		}
		
		Object[] getResults() {
			return new Object[] {
				new Integer(this.resultCount),
				new Integer(this.resultUpdateCount),
				new Integer(this.resultsDisplayed),
				new Integer(this.errorCount),
				new Double(this.queryDuration),
			};
		}
	}
    
    private ConnectionUtil connectionUtil = new ConnectionUtil();
    
    protected abstract Shell getShell();
    
    protected Connection getConnection(Bookmark bookmark) {
        return this.connectionUtil.getConnection(bookmark, getShell());
    }

	String execute1 = Messages.getString(BaseExecuteAction.class, "execute1");
	String execute2 = Messages.getString(BaseExecuteAction.class, "execute2");

	protected BaseExecuteAction(String text) {
		super(text);
	}
	
	protected BaseExecuteAction() {
		super(null);
	}
	
	public void run() {
		Bookmark bookmark = getBookmark();
		if (bookmark != null) {
           	execute(bookmark);
		}
	}

	/**
	 * @param bookmark
	 */
	protected void execute(Bookmark bookmark) {
		if (bookmark != null) {
	        try {
				getStatusLineManager().setErrorMessage(null);
				Connection connection = getConnection(bookmark);
				if (connection != null) {
					execute(bookmark, connection);
				}
	        } catch (IOException e) {
	            ExceptionDisplayDialog.openError(getShell(), null, null, e);
	        } catch (SQLException e) {
	            SQLExceptionDialog.openException(getShell(), bookmark, e);
	        } catch (CoreException e) {
	            ErrorDialog.openError(getShell(), null, null, e.getStatus());
	        }
		}
	}

	/**
	 * @param bookmark
	 * @param connection
	 * @throws IOException
	 * @throws CoreException
	 */
	protected void execute(Bookmark bookmark, Connection connection) 
			throws IOException, CoreException, SQLException {
		getStatusLineManager().setMessage(execute1);
		MultiSQLServer server = MultiSQLServer.getInstance();

		Metrics metrics = new Metrics();
		
		List queries = getQueries();
		IProgressMonitor progressBar = getStatusLineManager().getProgressMonitor();
		progressBar.beginTask("queries", queries.size());
		for (int i = 0; i < queries.size(); i++) {
			getStatusLineManager().setMessage((i % 2 == 0) ? execute1 : execute2);
		   
			String query = (String) queries.get(i);
			System.out.println(">" + query + "<"); //$NON-NLS-1$ //$NON-NLS-2$
			if (query != null && query.trim().length() > 0) {
				SQLResults results = getSQLResults(bookmark, connection, server, query);
				metrics.measure(results);
				if (results != null) {
					bookmark.addQuery(query);
					if (results.isResultSet()) {
						SQLResultSetCollection.getInstance().addSQLResultSet(
								(SQLResultSetResults) results);
						activateTableView();
					}
				}
		    }
			progressBar.worked(i);
		}
		progressBar.done();
		displayFinalStatusMessage(metrics);
	}

	/**
	 * 
	 */
	private void activateTableView() {
		TableView.getInstance();
	}

	/**
	 * @return
	 * @throws CoreException
	 * @throws IOException
	 */
	protected abstract List getQueries() throws IOException, CoreException;

	/**
	 * @return
	 */
	protected abstract IStatusLineManager getStatusLineManager();

	/**
	 * @param metrics
	 */
	private void displayFinalStatusMessage(Metrics metrics) {
		String message = Messages.getString(
				BaseExecuteAction.class, 
				"done", //$NON-NLS-1$ 
				metrics.getResults()); 
		if (metrics.hasErrors()) {
			getStatusLineManager().setErrorMessage(
					ImageStore.getImage(ImageStore.STOP), message);
		} else {
			getStatusLineManager().setMessage(message);
		}
	}

	/**
	 * @param connection
	 * @param server
	 * @param query
	 * @return
	 */
	private SQLResults getSQLResults(Bookmark bookmark, Connection connection, MultiSQLServer server, String query) {
		SQLResults results = null;
		try {
			results = server.execute(bookmark, connection, query);
		} catch (SQLException e) {
			LogProxy log = LogProxy.getInstance();
			log.addText(LogProxy.ERROR,
				"Error Executing: " + query + ":" + e.toString(), e); //$NON-NLS-1$ //$NON-NLS-2$
			SQLExceptionDialog.openException(getShell(), bookmark, e);
		}
		return results;
	}

	protected Bookmark getBookmark() {
		Bookmark bookmark = null;
		SimpleSelectionDialog dialog = new SimpleSelectionDialog(
				getShell(), 
				Messages.getString(BaseExecuteAction.class, "selectBookmark"), 
				BookmarkCollection.getInstance().getBookmarks(),
				ImageStore.getImage(ImageStore.BOOKMARK));
		if (SimpleSelectionDialog.OK == dialog.open()) {
			IStructuredSelection selection = dialog.getSelection();
			
			bookmark = (Bookmark) selection.getFirstElement();
		}
		return bookmark;
	}
}