/**
 *
 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/
package lucee.runtime.tag;

import java.sql.Connection;

import javax.servlet.jsp.JspException;

import lucee.commons.lang.ExceptionUtil;
import lucee.runtime.db.DataSourceManager;
import lucee.runtime.db.DatasourceManagerImpl;
import lucee.runtime.exp.DatabaseException;
import lucee.runtime.exp.PageException;
import lucee.runtime.ext.tag.BodyTagTryCatchFinallyImpl;

/**
 * Transaction class
 */
public final class Transaction extends BodyTagTryCatchFinallyImpl {

	private static final int ACTION_NONE = 0;

	private static final int ACTION_BEGIN = 1;

	private static final int ACTION_COMMIT = 2;

	private static final int ACTION_ROLLBACK = 4;

	private static final int ACTION_SET_SAVEPOINT = 8;

	// private boolean hasBody;
	private int isolation = Connection.TRANSACTION_NONE;
	private int action = ACTION_NONE;
	private boolean innerTag = false;

	private boolean ignore = false;

	@Override
	public void release() {
		// hasBody=false;
		isolation = Connection.TRANSACTION_NONE;
		action = ACTION_NONE;
		innerTag = false;
		ignore = false;
		super.release();
	}

	/**
	 * @param action The action to set.
	 * @throws DatabaseException
	 */
	public void setAction(String strAction) throws DatabaseException {
		strAction = strAction.trim().toLowerCase();
		if (strAction.equals("begin")) action = ACTION_BEGIN;
		else if (strAction.equals("commit")) action = ACTION_COMMIT;
		else if (strAction.equals("rollback")) action = ACTION_ROLLBACK;
		else if (strAction.equals("setsavepoint")) action = ACTION_SET_SAVEPOINT;
		else {
			throw new DatabaseException("attribute action has an invalid value, valid values are [begin,commit,setsavepoint and rollback]", null, null, null);
		}

	}

	/**
	 * @param isolation The isolation to set.
	 * @throws DatabaseException
	 */
	public void setIsolation(String isolation) throws DatabaseException {
		isolation = isolation.trim().toLowerCase();
		if (isolation.equals("read_uncommitted")) this.isolation = Connection.TRANSACTION_READ_UNCOMMITTED;
		else if (isolation.equals("read_committed")) this.isolation = Connection.TRANSACTION_READ_COMMITTED;
		else if (isolation.equals("repeatable_read")) this.isolation = Connection.TRANSACTION_REPEATABLE_READ;
		else if (isolation.equals("serializable")) this.isolation = Connection.TRANSACTION_SERIALIZABLE;
		else if (isolation.equals("none")) this.isolation = Connection.TRANSACTION_NONE;
		else throw new DatabaseException(
				"transaction has an invalid isolation level (attribute isolation, valid values are [read_uncommitted,read_committed,repeatable_read,serializable])", null, null,
				null);
	}

	@Override
	public int doStartTag() throws PageException {
		DataSourceManager manager = pageContext.getDataSourceManager();
		// first transaction
		if (manager.isAutoCommit()) {
			// if(!hasBody)throw new DatabaseException("transaction tag with no end Tag can only be used inside
			// a transaction tag",null,null,null);
			manager.begin(isolation);
			return EVAL_BODY_INCLUDE;
		}
		// inside transaction
		innerTag = true;
		switch (action) {
		/*
		 * nested transaction no longer throw an exception, they are simply ignored case ACTION_NONE: throw
		 * new
		 * DatabaseException("you can't have a nested transaction with no action defined",null,null,null);
		 * case ACTION_BEGIN: throw new
		 * DatabaseException("you can't start a transaction inside a transaction tag",null,null,null);
		 */
		case ACTION_NONE:
		case ACTION_BEGIN:
			ignore = true;
			break;

		case ACTION_COMMIT:
			manager.commit();
			break;
		case ACTION_ROLLBACK:
			manager.rollback();
			break;
		case ACTION_SET_SAVEPOINT:
			((DatasourceManagerImpl) manager).savepoint();
			break;
		}

		return EVAL_BODY_INCLUDE;
	}

	@Override
	public void doCatch(Throwable t) throws Throwable {
		ExceptionUtil.rethrowIfNecessary(t);
		if (innerTag || ignore) throw t;

		DataSourceManager manager = pageContext.getDataSourceManager();
		try {
			manager.rollback();
		}
		catch (DatabaseException e) {
			// print.printST(e);
		}
		throw t;
	}

	/**
	 * @param hasBody
	 */
	public void hasBody(boolean hasBody) {// print.out("hasBody"+hasBody);
		// this.hasBody=hasBody;
	}

	@Override
	public void doFinally() {
		if (!ignore && !innerTag) {
			pageContext.getDataSourceManager().end();
		}
		super.doFinally();
	}

	@Override
	public int doAfterBody() throws JspException {

		if (!ignore && !innerTag) {
			pageContext.getDataSourceManager().commit();
		}
		return super.doAfterBody();
	}
}
@@ -2322,17 +2322,17 @@
 eption(%22
-a
+A
 ttribute
@@ -2332,22 +2332,24 @@
 tribute 
+%5B
 action
+%5D
  has an 
@@ -3184,17 +3184,17 @@
 n(%0A%09%09%09%09%22
-t
+T
 ransacti
@@ -3238,16 +3238,17 @@
 tribute 
+%5B
 isolatio
@@ -3248,16 +3248,17 @@
 solation
+%5D
 , valid 
@@ -5236,8 +5236,9 @@
 ();%0A%09%7D%0A%7D
+%0A

format exceptions