package com.leandog.gametel.driver.commands;

import java.lang.reflect.Method;
import java.util.*;

import com.leandog.gametel.driver.TestRunInformation;
import com.leandog.gametel.driver.commands.Command.Target;
import com.leandog.gametel.driver.exceptions.CommandNotFoundException;

public class CommandRunner {

    private Object theLastResult;
    
    Map<String, Object> variables =  new HashMap<String, Object>();

    public void execute(final Command... commands) throws Exception {
        clearLastRun();

        for (final Command command : commands) {
            theLastResult = findMethod(command).invoke(theTargetFor(command), theArguments(command));
            
            if( command.hasVariable() ) {
                storeVariable(command);
            }
        }
    }

    public Object theLastResult() {
        return theLastResult;
    }

    private Method findMethod(final Command command) throws CommandNotFoundException {
        return new MethodFinder()
            .find(command.getName())
            .on(theTargetFor(command).getClass())
            .with(argumentTypesFor(command));
    }

    private void clearLastRun() {
        theLastResult = null;
        variables.clear();
    }

    private Object theTargetFor(final Command command) {
        if( command.getTarget() == Target.Robotium ) {
            return TestRunInformation.getSolo();
        }
        
        return (theLastResult == null) ? TestRunInformation.getSolo() : theLastResult;
    }

    private static Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>();
    static {
        primitiveMap.put(Integer.class, int.class);
        primitiveMap.put(Boolean.class, boolean.class);
        primitiveMap.put(Float.class, float.class);
    }

    private Class<? extends Object> typeFor(final Object argument) {
        Class<?> argumentType = argument.getClass();
        Class<?> primitiveType = primitiveMap.get(argumentType);
        return (primitiveType == null) ? argumentType : primitiveType;
    }

    private Class<?>[] argumentTypesFor(final Command command) {
        Class<?>[] types = new Class<?>[theArguments(command).length];

        int index = 0;
        for (final Object argument : theArguments(command)) {
            types[index++] = typeFor(argument);
        }

        return types;
    }

    private Object[] theArguments(final Command command) {
        Object[] original = command.getArguments();
        Object[] actual = Arrays.copyOf(original, original.length);
        
        int index = 0;
        for(final Object argument : original) {
            if( isVariable(argument) ) {
                actual[index] = variables.get(argument);
            }
            index++;
        }
        
        return actual;
    }

    private boolean isVariable(final Object argument) {
        return argument instanceof String && argument.toString().matches("@@.*@@");
    }

    private void storeVariable(final Command command) {
        variables.put(command.getVariable(), theLastResult);
    }

}

@@ -91,16 +91,44 @@
 .util.*;
+%0Aimport java.util.Map.Entry;
 %0A%0Aimport
@@ -2441,28 +2441,47 @@
     
+List%3C
 Object
-%5B%5D original = 
+%3E arguments = Arrays.asList(
 comm
@@ -2498,16 +2498,17 @@
 uments()
+)
 ;%0A      
@@ -2513,183 +2513,141 @@
     
-Object%5B%5D actual = Arrays.copyOf(original, original.length);%0A        %0A        int index = 0;%0A        for(final Object argument : original) %7B
+substituteVariables(arguments);%0A        return arguments.toArray();
 %0A    
+%7D%0A%0A
     
-    if( isVariable(
+private void substituteVariables(List%3CObject%3E 
 argument
 ) ) 
@@ -2642,18 +2642,17 @@
 argument
-) 
+s
 ) %7B%0A    
@@ -2659,280 +2659,171 @@
     
-        actual%5Bindex%5D = variables.get(argument);%0A            %7D%0A            index++;%0A        %7D%0A        %0A        return actual;%0A    %7D%0A%0A    private boolean isVariable(final Object argument) %7B%0A        return argument instanceof String && argument.toString().matches(%22@@.*@@%22);
+for(final Entry%3CString, Object%3E variable : variables.entrySet()) %7B%0A            Collections.replaceAll(arguments, variable.getKey(), variable.getValue());%0A        %7D
 %0A   

refactor variable substitution code