// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/actioncreator/ActionCreatorElement.java
// public class ActionCreatorElement {
//     public final List<ActionCreatorAction> actions;
//     private final TypeElement typeElement;
//     private Map<String, ActionCreatorAction> actionMap;
// 
//     private ActionCreatorElement(List<ActionCreatorAction> actions, TypeElement typeElement) {
//         this.actions = actions;
//         this.typeElement = typeElement;
//         actionMap = new HashMap<>();
//         for (ActionCreatorAction action : actions) {
//             actionMap.put(action.actionType, action);
//         }
//     }
// 
//     public boolean hasAction(String actionType, List<? extends VariableElement> reducerArgs) {
//         ActionCreatorAction actionCreator = actionMap.get(actionType);
//         if (actionCreator == null || reducerArgs.size() != actionCreator.arguments.size()) return false;
//         List<? extends VariableElement> arguments = actionCreator.arguments;
//         for (int i = 0; i < arguments.size(); i++) {
//             //we are doing black magic with checking if TypeName are equals
//             //instead of checking if two TypeMirrors are equals because TypeMirrors can be different for the same type
//             //happens when code is processed with several number of processing steps
//             TypeName creatorArgType = actionCreator.argumentTypes.get(i);
//             TypeName reducerArgType = TypeName.get(reducerArgs.get(i).asType());
//             if (!creatorArgType.equals(reducerArgType)) {
//                 return false;
//             }
//         }
//         return true;
//     }
// 
//     public String getPackageName(Env env) {
//         return env.getPackageName(typeElement);
//     }
// 
//     public String getName(Env env) {
//         return env.getElements().getBinaryName(typeElement).toString();
//     }
// 
//     public TypeMirror getType() {
//         return typeElement.asType();
//     }
// 
//     public static ActionCreatorElement parse(Element element, Env env) throws ValidationException {
//         if (element.getKind() != ElementKind.INTERFACE) {
//             throw new ValidationException(element, "%s annotated with @%s should be interface", element, ActionCreator.class.getSimpleName());
//         }
//         TypeElement typeElement = MoreElements.asType(element);
// 
//         List<ActionCreatorAction> actions = new ArrayList<>();
//         for (Element methodElement : typeElement.getEnclosedElements()) {
//             if (methodElement.getKind() != ElementKind.METHOD) continue;
//             actions.add(ActionCreatorAction.parse(MoreElements.asExecutable(methodElement), env));
//         }
// 
//         return new ActionCreatorElement(actions, typeElement);
//     }
// }


import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.yheriatovych.reductor.annotations.ActionCreator;
import com.yheriatovych.reductor.annotations.AutoReducer;
import com.yheriatovych.reductor.processor.*;
import com.yheriatovych.reductor.processor.actioncreator.ActionCreatorElement;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
package com.yheriatovych.reductor.processor.autoreducer;



public class ReduceAction {

    public final String action;
    public final List<VariableElement> args;
    public final ExecutableElement executableElement;
    public final boolean generateActionCreator;

    private ReduceAction(String action, List<VariableElement> args, ExecutableElement executableElement, boolean generateActionCreator) {
        this.executableElement = executableElement;
        this.args = args;
        this.action = action;
        this.generateActionCreator = generateActionCreator;
    }

// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/actioncreator/ActionCreatorElement.java
// public class ActionCreatorElement {
//     public final List<ActionCreatorAction> actions;
//     private final TypeElement typeElement;
//     private Map<String, ActionCreatorAction> actionMap;
// 
//     private ActionCreatorElement(List<ActionCreatorAction> actions, TypeElement typeElement) {
//         this.actions = actions;
//         this.typeElement = typeElement;
//         actionMap = new HashMap<>();
//         for (ActionCreatorAction action : actions) {
//             actionMap.put(action.actionType, action);
//         }
//     }
// 
//     public boolean hasAction(String actionType, List<? extends VariableElement> reducerArgs) {
//         ActionCreatorAction actionCreator = actionMap.get(actionType);
//         if (actionCreator == null || reducerArgs.size() != actionCreator.arguments.size()) return false;
//         List<? extends VariableElement> arguments = actionCreator.arguments;
//         for (int i = 0; i < arguments.size(); i++) {
//             //we are doing black magic with checking if TypeName are equals
//             //instead of checking if two TypeMirrors are equals because TypeMirrors can be different for the same type
//             //happens when code is processed with several number of processing steps
//             TypeName creatorArgType = actionCreator.argumentTypes.get(i);
//             TypeName reducerArgType = TypeName.get(reducerArgs.get(i).asType());
//             if (!creatorArgType.equals(reducerArgType)) {
//                 return false;
//             }
//         }
//         return true;
//     }
// 
//     public String getPackageName(Env env) {
//         return env.getPackageName(typeElement);
//     }
// 
//     public String getName(Env env) {
//         return env.getElements().getBinaryName(typeElement).toString();
//     }
// 
//     public TypeMirror getType() {
//         return typeElement.asType();
//     }
// 
//     public static ActionCreatorElement parse(Element element, Env env) throws ValidationException {
//         if (element.getKind() != ElementKind.INTERFACE) {
//             throw new ValidationException(element, "%s annotated with @%s should be interface", element, ActionCreator.class.getSimpleName());
//         }
//         TypeElement typeElement = MoreElements.asType(element);
// 
//         List<ActionCreatorAction> actions = new ArrayList<>();
//         for (Element methodElement : typeElement.getEnclosedElements()) {
//             if (methodElement.getKind() != ElementKind.METHOD) continue;
//             actions.add(ActionCreatorAction.parse(MoreElements.asExecutable(methodElement), env));
//         }
// 
//         return new ActionCreatorElement(actions, typeElement);
//     }
// }


// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/autoreducer/ReduceAction.java
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.yheriatovych.reductor.annotations.ActionCreator;
import com.yheriatovych.reductor.annotations.AutoReducer;
import com.yheriatovych.reductor.processor.*;
import com.yheriatovych.reductor.processor.actioncreator.ActionCreatorElement;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

package com.yheriatovych.reductor.processor.autoreducer;



public class ReduceAction {

    public final String action;
    public final List<VariableElement> args;
    public final ExecutableElement executableElement;
    public final boolean generateActionCreator;

    private ReduceAction(String action, List<VariableElement> args, ExecutableElement executableElement, boolean generateActionCreator) {
        this.executableElement = executableElement;
        this.args = args;
        this.action = action;
        this.generateActionCreator = generateActionCreator;
    }

    public static ReduceAction parseReduceAction(Env env, TypeMirror stateType, ExecutableElement element, Map<String, ActionCreatorElement> knownActionCreators) throws ValidationException {