

package gov.va.med.cds.persistence.hibernate;


//TODO: refactor to implement reads from HDR II that will map directly to xml

import static gov.va.med.cds.exception.ErrorCodeEnum.HDRII_OPERATION_FAILED;
import gov.va.med.cds.clinicaldata.DomainEntryPoint;
import gov.va.med.cds.clinicaldata.Operation;
import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.persistence.DeleteException;
import gov.va.med.cds.persistence.PersistenceException;
import gov.va.med.cds.persistence.WritePersistenceManagerInterface;
import gov.va.med.cds.rules.BooleanRuleInterface;

import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.dom4j.Element;
import org.hibernate.Session;
import org.hibernate.exception.ConstraintViolationException;


public class WriteableHibernatePersistenceManager
    extends
        AbstractHibernatePersistenceManager
    implements
        WritePersistenceManagerInterface
{
    private static final String LOGICAL_DELETE_VALUE = "1";
    private List<BooleanRuleInterface> rules;
    private String cdsAppName;
    private Map<String, String> logicalDeleteTemplateIdsMap;


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.persistence.WritePersistenceManagerInterface#isApplicable(java.lang.Object)
     */
    public boolean isApplicable( Object critera )
        throws PersistenceException
    {
        boolean b = false;
        for ( BooleanRuleInterface r : rules )
        {
            b = r.evaluate( critera );
            if ( b )
            {
                break;
            }
        }

        return b;
    }


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.persistence.WritePersistenceManagerInterface#performCUADOnClinicalData(java.lang.String, gov.va.med.cds.clinicaldata.Operation, java.lang.Object)
     */
    @Override
    public String performCUADOnClinicalData( String templateId, Operation operation, Object aObject, DomainEntryPoint domainEntryPoint )
        throws PersistenceException
    {
        Session session = null;
        String resultId = "";
       
        try
        {
            session = getSessionFactory( templateId ).getCurrentSession();

            if ( LOGGER.isDebugEnabled() )
            {
                LOGGER.debug( gov.va.med.cds.util.LogMessageUtil.buildMessage( templateId, null, cdsAppName, operation.name() + aObject ) );
            }
 
            aObject  = unmarshalElementToObject((Element)aObject, domainEntryPoint);
            switch ( operation )
            {
            case Create:
                resultId = createData( session, aObject );
                break;
            case Delete:
                if ( logicalDeleteTemplateIdsMap.containsKey( templateId ) )
                {
                    deleteOrUpdateData( session, aObject, logicalDeleteTemplateIdsMap.get( templateId ));
                }
                else
                {
                    deleteData( session, aObject );
                }
                break;
            case Update:
                updateData( session, aObject );
                break;
            default:
                throw new UnsupportedOperationException( String.format(
                                "The %s operation is not supported by the WriteableHibernatePersistenceManager.", operation.toString() ) );
            }

            session.flush();
            return resultId;
        }
        // try to figure out what caused the violation
        catch ( ConstraintViolationException e )
        {
            throw new HibernateConstraintViolationException( e );
        }
        // already handled and turned into one of ours so rethrow it
        catch ( PersistenceException e )
        {
            throw e;
        }
        // any other problems are just big and will require someone to look at so check log file
        catch ( Exception e )
        {
            String rootCauseMessage = ExceptionUtils.getRootCause( e ) == null ? e.getMessage() : ExceptionUtils.getRootCause( e ).getMessage();
            throw new PersistenceException( HDRII_OPERATION_FAILED, e, operation.name(), rootCauseMessage, domainEntryPoint.getName());

        }
    }


    /**
     * Handles hibernate HDRII delete processing.
     * 
     * @param session - active hibernate session.
     * @param aObject - delete request.
     * @throws IllegalAccessException 
     * @throws InstantiationException 
     */
    private void deleteOrUpdateData( Session session, Object aObject, String elementName )
        throws InstantiationException,
            IllegalAccessException
    {
        Element record = ( Element )aObject;
        Element recordStatus = ( Element )record.selectSingleNode( String.format( "//%s", elementName ) );

        if ( recordStatus != null && recordStatus.getStringValue().equals( LOGICAL_DELETE_VALUE ) )
        {
            updateData( session, aObject );//logical delete
        }
        else
        {
            deleteData( session, aObject );//physical delete
        }

    }


    /**
     * Handles hibernate HDRII delete processing.
     * 
     * @param session - active hibernate session.
     * @param aObject - delete request.
     */
    private void deleteData( Session session, Object aObject )
    {
        if ( LOGGER.isDebugEnabled() )
        {
            LOGGER.debug( gov.va.med.cds.util.LogMessageUtil.buildMessage( null, null, cdsAppName, "record to delete: " + aObject ) );
        }
        try
        {
            session.delete( aObject );
        }
        catch ( org.hibernate.exception.GenericJDBCException e )
        {
            throw new DeleteException( ErrorCodeEnum.HIBERNATE_DELETE_FAILURE, e );
        }
    }


    private String createData( Session session, Object aObject )
    {
        return session.save( aObject ).toString();
    }


    /**
     * Handles hibernate HDRII update processing.
     * 
     * @param session - active hibernate session.
     * @param aObject - update request.
     */
    protected void updateData( Session session, Object aObject )
        throws InstantiationException,
            IllegalAccessException
    {
        session.update( aObject );
    }


    /**
     * Sets the rules to evaluate to determine if the persistence manager is applicable for the request.
     * 
     * @param rules The boolean rules to be evaluated.
     */
    public void setRules( List<BooleanRuleInterface> rules )
    {
        this.rules = rules;
    }


    public void setCdsAppName( String cdsAppName )
    {
        this.cdsAppName = cdsAppName;
    }


    public void setLogicalDeleteTemplateIdsMap( Map<String, String> logicalDeleteTemplateIdsMap )
    {
        this.logicalDeleteTemplateIdsMap = logicalDeleteTemplateIdsMap;
    }
    
    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.persistence.WritePersistenceManagerInterface#performCUADOnClinicalData(java.lang.String, gov.va.med.cds.clinicaldata.Operation, java.lang.Object)
     */
    @Override
    public String performCUADOnPatientsData( String templateId, String requestId, Operation operation, Object aObject, DomainEntryPoint domainEntryPoint )
        throws PersistenceException
    {
        Session session = null;
        String resultId = "";

        try
        {
            session = getSessionFactory( templateId ).getCurrentSession();
            
            aObject  = unmarshalElementToObject((Element)aObject, domainEntryPoint);

            if ( LOGGER.isDebugEnabled() )
            {
                LOGGER.debug( gov.va.med.cds.util.LogMessageUtil.buildMessage( templateId, null, cdsAppName, operation.name() + aObject ) );
            }

            switch ( operation )
            {
            case Create:
                resultId = createData( session, aObject );
                break;
            case Delete:
                if ( logicalDeleteTemplateIdsMap.containsKey( templateId ) )
                {
                    deleteOrUpdateData( session, aObject, logicalDeleteTemplateIdsMap.get( templateId ) );
                }
                else
                {
                    deleteData( session, aObject );
                }
                break;
            case Update:
                updateData( session, aObject );
                break;
            default:
                throw new UnsupportedOperationException( String.format(
                                "The %s operation is not supported by the WriteableHibernatePersistenceManager.", operation.toString() ) );
            }

            session.flush();
            return resultId;
        }
        // try to figure out what caused the violation
        catch ( ConstraintViolationException e )
        {
            throw new HibernateConstraintViolationException( e );
        }
        // already handled and turned into one of ours so rethrow it
        catch ( PersistenceException e )
        {
            throw e;
        }
        // any other problems are just big and will require someone to look at so check log file
        catch ( Exception e )
        {
            String rootCauseMessage = ExceptionUtils.getRootCause( e ) == null ? e.getMessage() : ExceptionUtils.getRootCause( e ).getMessage();
            throw new PersistenceException( HDRII_OPERATION_FAILED, e, operation.name(), rootCauseMessage, domainEntryPoint.getName() );

        }
    }
    
    /*
    @Override
	public List<CensusMasterIngest> performReadOnCensusMetaData(
			CensusMasterIngest censusMasterIngest) throws CensusException {
		throw new OperationNotSupportedException(ErrorCodeEnum.METHOD_NOT_IMPLEMENTED);
	}

	@Override
	public String performCUADOnCensusMetaData(Operation aOperation,
			CensusMasterIngest censusMasterIngest) throws PersistenceException {
		throw new OperationNotSupportedException(ErrorCodeEnum.METHOD_NOT_IMPLEMENTED);
	}*/
	

}
