/**
 * 
 */


package gov.va.med.cds.aspects.exception;


import gov.va.med.cds.exception.AbstractCdsBaseException;
import gov.va.med.cds.exception.ErrorCodeEnum;

import java.lang.reflect.Constructor;


/**
 * Replace exception thrown with specified CDS exception.
 * -- 'afterThrowing' advice.
 * 
 * If the exception to be replaced is not specified - throws back the original exception
 *  
 * @author susarlan
 *
 */
public class ReplaceAspect
    extends
        LogAspect
{
    private AbstractCdsBaseException exceptionToReplaceWith;


    /***
     * Throws the exceptionToReplaceWith in place of the original exception if specified.
     * Else, rethrows the original exception.
     * 
     * @param cdsException
     */
    public void doAfterThrowing( AbstractCdsBaseException cdsException )
    {
        if ( isLog() )
        {
            logException( cdsException );
        }

        if ( exceptionToReplaceWith != null )
            throw exceptionToReplaceWith;
        throw cdsException;
    }


    public void doAfterThrowingWithArgs( AbstractCdsBaseException cdsException, String replaceExceptionName, String errorCode, String message )
    {
        if ( isLog() )
        {
            logException( cdsException );
        }

        replaceAndThrowException( cdsException, replaceExceptionName, errorCode, message );
    }


    public void setExceptionToReplaceWith( AbstractCdsBaseException exceptionToReplaceWith )
    {
        this.exceptionToReplaceWith = exceptionToReplaceWith;
    }


    protected void replaceAndThrowException( AbstractCdsBaseException cdsException, String replaceExceptionName, String errorCode, String message )
    {
        Class exceptionClass = null;
        try
        {
            // Get the exception class based on the given class name
            exceptionClass = Class.forName( replaceExceptionName );

            // Obtain the exception constructor that takes the error code enum and string
            Class[] types = new Class[] { ErrorCodeEnum.class, Throwable.class, String[].class };
            Constructor cons = exceptionClass.getConstructor( types );

            // Set up the constructor arguments and create the exception
            ErrorCodeEnum enumVal = ErrorCodeEnum.valueOf( errorCode );
            Object args[] = new Object[] { enumVal, cdsException, new String[] { message } };
            AbstractCdsBaseException newException = ( AbstractCdsBaseException )cons.newInstance( args );

            // throw the newly constructed exception
            throw newException;
        }
        catch ( Exception ex )
        {
            String[] customMsgValues = cdsException.getCustomMsgValues();
            int newLen = customMsgValues.length + 1;
            String[] newValues = new String[newLen];
            for ( int i = 0; i < customMsgValues.length; i++ )
            {
                newValues[i] = customMsgValues[i];
            }
            newValues[newLen - 1] = "Unable to replace with exception 'replaceExceptionName'";
            cdsException.setCustomMsgValues( newValues );
            throw cdsException;
        }
    }


    /**
     * Indicate whether exception is to be logged
     * 
     * @return
     */
    protected boolean isLog( )
    {
        return false;
    }
}
