/*
 * JBoss, Home of Professional Open Source
 * Copyright 2007, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags.
 * See the copyright.txt in the distribution for a full listing
 * of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License, v. 2.0.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * v. 2.0 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * (C) 2007,
 * @author JBoss Inc.
 */
package org.jboss.txbridge.ba.execute;

import org.apache.log4j.Logger;
import org.jboss.txbridge.ba.annotation.DataMatch;
import org.jboss.txbridge.ba.datamgmt.ExecutionDataProvider;
import org.jboss.txbridge.ba.exception.ActionExecutionException;
import org.jboss.txbridge.ba.service.MethodDescription;

/**
 * Arguments Provider component provides methods to obtain necessary data, which has been
 * remembered during execution of the original method. This data will be used when invoking
 * the datamgmt action.
 *
 * @author Maciej P. Machulak (mmachulak@redhat.com)
 * @version 0.1
 */
public class ArgumentsProvider implements org.jboss.txbridge.ba.execute.DataProvider
{
    private static Logger log = Logger.getLogger(ArgumentsProvider.class);

    /**
     * This method 
     *
     * @param md is the service description.
     * @param cdp is the datamgmt data provider.
     * @return array of objects.
     * @throws ActionExecutionException
     */
    public Object[] getArguments(MethodDescription md, ExecutionDataProvider cdp)
            throws ActionExecutionException
    {
        log.info("getArguments()");
        int successfullMatches = 0;

        // Parameter objects and their types
        Object[] parameterObjects = null;
        Class[] parameterTypes = null;

        // Check the type of the service
        log.info("Compensation type: " + md.getDataMatch());

        // Depending on the datamgmt type try to cast the datamgmt data and proceed
        if (md.getDataMatch() == DataMatch.PARAMETERS_MATCH)
        {
            // Get parameters
            log.info("Getting parameters");
            parameterObjects = cdp.getArguments();
            if (parameterObjects != null)
            {
                parameterTypes = new Class[parameterObjects.length];
                for (int i = 0; i < parameterObjects.length; i++)
                {
                    parameterTypes[i] = parameterObjects[i].getClass();
                }
                successfullMatches = parameterObjects.length;
                log.info("Number of successfull matches: " + successfullMatches);
            }
            else
            {
                throw new ActionExecutionException("Compensation data incorrect.");
            }

        }
        else if (md.getDataMatch() == DataMatch.RETURN_VALUE)
        {
            // Get parameter
            log.info("Getting return value");
            parameterObjects = cdp.getReturn();
            if (parameterObjects != null)
            {
                parameterTypes = new Class[1];
                parameterTypes[0] = parameterObjects[0].getClass();
                successfullMatches = 1;
            }
            else
            {
                throw new ActionExecutionException("Compensation data incorrect.");
            }
        }
        else if (md.getDataMatch() == DataMatch.CUSTOM)
        {

            // Get parameter annotations
            log.info("Getting datamgmt parameter annotations");
            Object[] parameterAnnotations = md.getParameterAnnotations();
            if (parameterAnnotations != null)
            {
                parameterObjects = new Object[parameterAnnotations.length];
                parameterTypes = new Class[parameterObjects.length];

                // For parameter annotations get objects
                log.info("Looping through annotations");
                for (int i=0; i< parameterAnnotations.length; i++)
                {
                    log.info("### Parameter " + i);
                    if (parameterAnnotations[i] != null)
                    {
                        log.info("Annotation found: " + parameterAnnotations[i]);
                        // Get object for this identifier
                        Object singleObject = cdp.get(parameterAnnotations[i]);
                        if (singleObject != null)
                        {
                            log.info("Object found: " + singleObject);

                            // Remember the object
                            parameterObjects[i] = singleObject;
                            parameterTypes[i] = singleObject.getClass();

                            // Increase the number of successfull matches
                            successfullMatches++;
                        }
                        else
                        {
                            log.info("Object not found");
                        }
                    }
                }
            }
            else
            {
                log.info("No annotations present - adding parameters by order.");
                parameterTypes = md.getParameterTypes();
                if (parameterTypes != null)
                {
                    log.info("Number of required objects: " + parameterTypes.length);
                    parameterObjects = new Object[parameterTypes.length];
                    for (int i=0; i<parameterObjects.length; i++)
                    {
                        Object singleArgument = cdp.get(i);
                        parameterObjects[i] = singleArgument;
                        if (singleArgument == null)
                        {
                            log.info("Object no. " + i + " not found.");
                        }
                    }
                }

            }

        }

        /**
         // Check if the number of parameters is correct
         log.info("Checking if number of parameters matches");
         int compensationParameters = sd.getCompensationParameterTypes().length;
         if (successfullMatches < compensationParameters)
         {
         log.info("Number of parameters is smaller");
         ParameterMatch parameterMatch = sd.getParameterMatch();
         log.info("ParameterMatch: " + parameterMatch);
         if ((parameterMatch == ParameterMatch.STRICT))
         {
         throw new ActionExecutionException("Original parameters do not match datamgmt parameters.");
         }
         else if (sd.getParameterMatch().equals(ParameterMatch.ALLOW_NULL))
         {
         if ( dataMatch == DataMatch.RETURN_VALUE )
         {
         log.info("Return value extended with null values to match argument list");
         Object tempObject = parameterObjects[0];
         parameterObjects = new Object[compensationParameters];
         parameterObjects[0] = tempObject;
         }
         else if ( dataMatch == DataMatch.PARAMETERS_MATCH )
         {
         log.info("Parameter list extended with null values to match argument list");
         Object[] tempObjects = parameterObjects;
         parameterObjects = new Object[compensationParameters];
         System.arraycopy(tempObjects,0,parameterObjects,0,tempObjects.length);
         }
         else
         {
         // If the datamgmt type was CUSTOM we don't have to do anything.
         }
         }
         }
         else if (successfullMatches > compensationParameters)
         {
         log.info("Impossible!:)");
         }
         */

        return parameterObjects;
    }

    /**
     * This method takes an array of objects as an argument. It returns an array of types of
     * those objects.
     *
     * @param arguments is the array containing objects, that will be used as arguments.
     * @return array containing types of those objects.
     */
    public Class[] getArgumentTypes(Object[] arguments)
    {
        log.info("getArgumentTypes()");

        Class[] argumentTypes = null;
        if (arguments != null)
        {
           argumentTypes = new Class[arguments.length];
        }
        else
        {
            return argumentTypes;
        }

        log.info("Looping through all arguments: " + arguments.length);
        int i = 0;
        for (Object argument: arguments)
        {
            if (argument != null)
            {
                log.info("Argument " + i + " not null");
                argumentTypes[i] = argument.getClass();
                log.info("Argument class: " + argument.getClass());
            }
            i++;
        }
        return argumentTypes;
    }

}
