package railo.runtime.tag;


import java.sql.Connection;

import javax.servlet.jsp.JspException;

import railo.runtime.db.DataSourceManager;
import railo.runtime.db.DatasourceManagerImpl;
import railo.runtime.exp.DatabaseException;
import railo.runtime.exp.PageException;
import railo.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;
        super.release();
        ignore=false;
    }

    /**
     * @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 a 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 {
    	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();
    }
}