//
//  ISHDirectActionRequestHandler.java
//  ISHWebObjects
//
//  Created by Lachlan Deck on 30/11/05.
//  Copyright (c) 2005 ISH Group Pty Ltd. All rights reserved.
//

package com.ish.webobjects.appserver;

import org.apache.log4j.Logger;
import com.webobjects.appserver.WOAction;
import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WODirectAction;
import com.webobjects.appserver.WORequest;
import com.webobjects.appserver.WOResponse;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSBundle;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import er.extensions.ERXDirectActionRequestHandler;
import er.extensions.ERXProperties;

/**
 * Custom ModRewrite support
 * 
 * @author ldeck
 */
public class ISHDirectActionRequestHandler
    extends
        ERXDirectActionRequestHandler
{
	
	private static final Logger LOG = Logger.getLogger( ISHDirectActionRequestHandler.class );
	public static final String ModUnwriteDelimiter = "[=rw=]";
	
	static
	{
		// preload actions on default DA class
		try
		{
			Class mainDAClazz = Class.forName( ISHApplication.ishApplication().getClass().getPackage().getName() + ".DirectAction" );
			WOAction._preloadAllActionsOnClass( mainDAClazz, "Action" );
		}
		catch ( ClassNotFoundException e )
		{
			throw new IllegalStateException( "Failed to pre-init direct actions" );
		}
	}
	
	private static Class _actionClassWithPartialName( String daName )
	{
		Class actionClass = null;
		try
		{
			actionClass = NSBundle.mainBundle()._classWithName( daName );
			if ( !WODirectAction.class.isAssignableFrom( actionClass ) )
				actionClass = null;
		}
		catch ( Exception e )
		{
			
		}
		return actionClass;
	}
	
	private static boolean _isActionOnClassNamed( String actionName, String daName )
	{
		if ( actionName == null || daName == null )
			return false;
		String mainPackage = ISHApplication.ishApplication().getClass().getPackage().getName() + ".";
		try
		{
			Class daClass = Class.forName( daName.indexOf( '.' ) < 0 ? mainPackage + daName : daName );
			return WOAction._isActionOnClass( actionName, daClass );
		}
		catch ( ClassNotFoundException e )
		{
			return false;
		}
	}
	
	/**
	 * @param path -
	 *            the request path
	 * @return full direct action class name and direct action.
	 */
	public static NSArray getDirectActionClassAndNameForPath( NSArray path )
	{
		NSMutableArray adjustedPath;
		String actionClassName;
		
		adjustedPath = path == null ? new NSMutableArray() : path.mutableClone();
		actionClassName = adjustedPath.count() > 0 ? ( String )adjustedPath.objectAtIndex( 0 ) : "";
		
		if ( LOG.isDebugEnabled() )
			LOG.debug( "initial path:" + adjustedPath );
		
		// 1. Do we have a legit actionClass already?
		Class actionClass = null;
		if ( actionClassName.length() > 0 && Character.isUpperCase( actionClassName.charAt( 0 ) )
		        && ( actionClass = _actionClassWithPartialName( actionClassName ) ) != null )
		{
			if ( adjustedPath.count() == 1 )
				adjustedPath.addObject( "default" );
		}
		else if ( "".equals( actionClassName ) )
		{
			actionClassName = "DirectAction";
			if ( adjustedPath.count() == 0 )
				adjustedPath.addObject( actionClassName );
			else
				adjustedPath.replaceObjectAtIndex( actionClassName, 0 );
			
			if ( adjustedPath.count() == 1 )
				adjustedPath.addObject( "default" );
		}
		else if ( "null".equals( actionClassName ) )
		{
			actionClassName = "DirectAction";
			adjustedPath.replaceObjectAtIndex( actionClassName, 0 );
			if ( adjustedPath.count() == 1 )
				adjustedPath.addObject( "default" );
		}
		else if ( path.count() == 1 && _isActionOnClassNamed( actionClassName, "DirectAction" ) )
		{
			actionClassName = "DirectAction";
			adjustedPath.insertObjectAtIndex( actionClassName, 0 );
		}
		else
		{
			if ( ERXProperties.booleanForKeyWithDefault( ISHApplication.RewriteUrlsEnabled, false ) )
			{
				String entityName;
				
				if ( LOG.isDebugEnabled() )
					LOG.debug( "treating as mod-unrewrite for actionClass:" + actionClassName );
				
				NSDictionary pluralNames = ISHApplication.ishApplication().sharedEntitiesPerPluralNames();
				if ( LOG.isDebugEnabled() )
					LOG.debug( "plural names:" + pluralNames );
				
				// 1. test for plural entity name (used for showing lists of items)
				//   i.e., is it plural? ActionClass -> List
				if ( ( entityName = ( String )pluralNames.valueForKey( actionClassName ) ) != null )
				{
					actionClassName = "List";
					adjustedPath = new NSMutableArray( new Object[] { actionClassName, adjustedPath.componentsJoinedByString( ModUnwriteDelimiter ) } );
				}
				else if ( ( entityName = ISHDirectAction.entityForActionName( actionClassName ) ) != null )
				{
					// 2. test if actionClassName is a lower-case entity name or alias
					//    i.e., is it singular? ActionClass -> Detail
					actionClassName = "Detail";
					adjustedPath = new NSMutableArray( new Object[] { actionClassName, adjustedPath.componentsJoinedByString( ModUnwriteDelimiter ) } );
				}
				else
				{
					// 3. unknown direct action. Allow for aliased pages.
					actionClassName = "Detail";
					adjustedPath = new NSMutableArray( new Object[] { actionClassName, adjustedPath.componentsJoinedByString( ModUnwriteDelimiter ) } );
				}
			}
			
			if ( !Character.isUpperCase( actionClassName.charAt( 0 ) ) )
			{
				actionClassName = "DirectAction";
				adjustedPath.insertObjectAtIndex( actionClassName, 0 );
			}
		}
		
		if ( adjustedPath.count() > 2 )
		{
			adjustedPath =
			        new NSMutableArray( new Object[] { adjustedPath.removeObjectAtIndex( 0 ), adjustedPath.componentsJoinedByString( ModUnwriteDelimiter ) } );
		}
		
		if ( actionClassName.indexOf( '.' ) < 0 && Character.isUpperCase( actionClassName.charAt( 0 ) ) )
		{
			String pkg = ISHApplication.ishApplication().getClass().getPackage().getName();
			if ( LOG.isDebugEnabled() )
				LOG.debug( "getRequestActionClassAndNameForPath: fixing path..." + adjustedPath + " :" + pkg + "." + actionClassName );
			adjustedPath.replaceObjectAtIndex( pkg + "." + actionClassName, 0 );
		}
		return adjustedPath;
	}
	
	/**
	 * @param path -
	 *            the request path
	 * @return an array of user friendly action class and action names.
	 */
	public static NSArray getUserActionClassAndNameForPath( NSArray path )
	{
		if ( LOG.isDebugEnabled() )
			LOG.debug( "getUserActionClassAndNameForPath:" + path );
		NSMutableArray fixed = getDirectActionClassAndNameForPath( path ).mutableClone();
		if ( LOG.isDebugEnabled() )
			LOG.debug( "getUserActionClassAndNameForPath fixed1:" + fixed );
		if ( fixed.count() > 0 )
		{
			String actionClass = ( String )fixed.objectAtIndex( 0 );
			int index = actionClass.lastIndexOf( '.' );
			if ( index >= 0 )
			{
				actionClass = actionClass.substring( index + 1 );
				fixed.replaceObjectAtIndex( actionClass, 0 );
			}
		}
		if ( fixed.count() > 1 )
		{
			String userActionPath = fixed.removeObjectAtIndex( 1 ).toString().replaceAll( "\\s", "+" );
			fixed.addObjectsFromArray( NSArray.componentsSeparatedByString( userActionPath, ModUnwriteDelimiter ) );
		}
		
		if ( LOG.isDebugEnabled() )
			LOG.debug( "getUserActionClassAndNameForPath fixed2:" + fixed );
		return fixed;
	}
	
	public ISHDirectActionRequestHandler()
	{
		super();
		if ( LOG.isDebugEnabled() )
			LOG.debug( "ISHDirectActionRequestHandler() instantiated..." );
	}
	
	public ISHDirectActionRequestHandler( String actionClassName, String defaultActionName, boolean shouldAddToStatistics )
	{
		super( actionClassName, defaultActionName, shouldAddToStatistics );
		if ( LOG.isDebugEnabled() )
			LOG.debug( "ISHDirectActionRequestHandler(" + actionClassName + ", " + defaultActionName + ", " + shouldAddToStatistics + ") instantiated..." );
	}
	
	/**
	 * Overrides super's method to stop the swallowing of exceptions. This
	 * method now simply returns the response from
	 * WOApplication.application().handleException(Exception,WOContext).
	 * 
	 * @param e -
	 *            the exception thrown
	 * @param aContext -
	 *            the current context for the current request/response loop.
	 * @return the response.
	 */
	public WOResponse generateErrorResponse( Exception e, WOContext aContext )
	{
		if ( LOG.isDebugEnabled() )
			LOG.debug( "ISHDirectActionRequestHandler.generateErrorResponse with context:\n\t" + aContext, e );
		return WOApplication.application().handleException( e, aContext );
		// return super.generateErrorResponse( e, aContext );
	}
	
	/**
	 * Currently simply overrides super to see a debugging message. In the
	 * future it would be helpful to provide a more appropriate response.
	 * 
	 * @param aRequest -
	 *            the current request.
	 * @return the response.
	 */
	public WOResponse generateRequestRefusal( WORequest aRequest )
	{
		if ( LOG.isDebugEnabled() )
			LOG.debug( "ISHDirectActionRequestHandler.generateRequestRefusal with request: " + aRequest );
		return super.generateRequestRefusal( aRequest );
	}
	
	/**
	 * Overrides super to adjust the direct action class name to the full class
	 * name.
	 * 
	 * @see com.webobjects.appserver._private.WOActionRequestHandler#getRequestActionClassAndNameForPath(com.webobjects.foundation.NSArray)
	 */
	public Object[] getRequestActionClassAndNameForPath( NSArray path )
	{
		if ( LOG.isDebugEnabled() )
			LOG.debug( "getRequestActionClassAndNameForPath:" + path );
		NSArray fixedPath = getDirectActionClassAndNameForPath( path );
		if ( LOG.isDebugEnabled() )
			LOG.debug( "getRequestActionClassAndNameForPath:" + fixedPath );
		return super.getRequestActionClassAndNameForPath( fixedPath );
	}
	
	public WOResponse handleRequest( WORequest request )
	{
		return super.handleRequest( request );
	}
	
	public String toString()
	{
		return getClass().getName();
	}
}
