/*
 * 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.BACompensationType;
import org.jboss.txbridge.ba.compensation.CompensationDataProvider;
import org.jboss.txbridge.ba.exception.CompensationActionUnsuccessfulException;
import org.jboss.txbridge.ba.service.ServiceDescription;

/**
 * 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 compensation action.
 *
 * @author Maciej P. Machulak (mmachulak@redhat.com)
 * @version 0.1
 */
public class ArgumentsProvider implements DataProvider
{
    private static Logger log = Logger.getLogger(ArgumentsProvider.class);

    /**
     * This method 
     *
     * @param sd is the service description which describes the original and compensation services.
     * @param cdp is the compensation data provider.
     * @return array of objects.
     * @throws CompensationActionUnsuccessfulException
     */
    public Object[] getArguments(ServiceDescription sd, CompensationDataProvider cdp)
            throws CompensationActionUnsuccessfulException
    {
        log.info("getArguments()");
        int successfullMatches = 0;

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

        // Check the type of the service
        BACompensationType baCompensationType = sd.getCompensationType();
        log.info("Compensation type: " + baCompensationType);

        // Depending on the compensation type try to cast the compensation data and proceed
        if (baCompensationType == BACompensationType.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 CompensationActionUnsuccessfulException("Compensation data incorrect.");
            }

        }
        else if (baCompensationType == BACompensationType.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 CompensationActionUnsuccessfulException("Compensation data incorrect.");
            }
        }
        else if (baCompensationType == BACompensationType.CUSTOM)
        {

            // Get parameter annotations
            log.info("Getting compensation parameter annotations");
            Object[] parameterAnnotations = sd.getCompensationParameterAnnotations();
            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 = sd.getCompensationParameterTypes();
                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");
         BAParameterMatch parameterMatch = sd.getParameterMatch();
         log.info("ParameterMatch: " + parameterMatch);
         if ((parameterMatch == BAParameterMatch.STRICT))
         {
         throw new CompensationActionUnsuccessfulException("Original parameters do not match compensation parameters.");
         }
         else if (sd.getParameterMatch().equals(BAParameterMatch.ALLOW_NULL))
         {
         if ( baCompensationType == BACompensationType.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 ( baCompensationType == BACompensationType.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 compensation 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;
    }

}
