// Path: lib/src/main/java/com/yheriatovych/reductor/Action.java
// public class Action {
//     public final String type;
//     public final Object[] values;
// 
//     /**
//      * Create Action object with specified type and values
//      *
//      * @param type   String type of action, will be used by {@link Reducer} for dispatch
//      * @param values any number of arbitrary objects that can be attached as payload to this action
//      */
//     public Action(String type, Object[] values) {
//         this.type = type;
//         this.values = values;
//     }
// 
//     /**
//      * Create Action with defined type without any attached payload
//      *
//      * @param type String type of action, will be used by {@link Reducer} for dispatch
//      */
//     public Action(String type) {
//         this(type, new Object[0]);
//     }
// 
//     /**
//      * Factory method to create action with defined type and any number of attached values as payload
//      *
//      * @param type   String type of action, will be used by {@link Reducer} for dispatch
//      * @param values any number of arbitrary objects that can be attached as payload to this action
//      * @return created Action
//      */
//     public static Action create(String type, Object... values) {
//         return new Action(type, values);
//     }
// 
//     /**
//      * Returns action value at given position
//      * @param index value position
//      * @return value at position
//      */
//     @SuppressWarnings("unchecked")
//     public <T> T getValue(int index) {
//         return (T) values[index];
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         Action action = (Action) o;
// 
//         if (type != null ? !type.equals(action.type) : action.type != null) return false;
//         // Probably incorrect - comparing Object[] arrays with Arrays.equals
//         return Arrays.equals(values, action.values);
// 
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = type != null ? type.hashCode() : 0;
//         result = 31 * result + Arrays.hashCode(values);
//         return result;
//     }
// 
//     @Override
//     public String toString() {
//         return "Action{" +
//                 "type='" + type + '\'' +
//                 ", values=" + Arrays.toString(values) +
//                 '}';
//     }
// }
// 
// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/Env.java
// public class Env {
//     private final Types types;
//     private final Elements elements;
//     private final Messager messager;
//     private final Filer filer;
// 
//     public Env(Types types, Elements elements, Messager messager, Filer filer) {
//         this.types = types;
//         this.elements = elements;
//         this.messager = messager;
//         this.filer = filer;
//     }
// 
//     public void printError(Element element, String message, Object... args) {
//         printMessage(Diagnostic.Kind.ERROR, element, message, args);
//     }
// 
//     private void printMessage(Diagnostic.Kind level, Element element, String message, Object... args) {
//         messager.printMessage(level, String.format(message, args), element);
//     }
// 
//     public Types getTypes() {
//         return types;
//     }
// 
//     public Filer getFiler() {
//         return filer;
//     }
// 
//     public String getPackageName(Element element) {
//         return elements.getPackageOf(element).getQualifiedName().toString();
//     }
// 
//     public TypeMirror asType(Class<?> clazz) {
//         return elements.getTypeElement(clazz.getCanonicalName())
//                 .asType();
//     }
// 
//     public Elements getElements() {
//         return elements;
//     }
// }
// 
// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/ValidationException.java
// public class ValidationException extends Exception {
//     private final Element element;
//     private final String message;
// 
//     public ValidationException(Element element, String message, Object... args) {
//         this.element = element;
//         this.message = String.format(message, args);
//     }
// 
//     public Element getElement() {
//         return element;
//     }
// 
//     @Override
//     public String getMessage() {
//         return message;
//     }
// 
// }


import com.google.auto.common.MoreElements;
import com.squareup.javapoet.TypeName;
import com.yheriatovych.reductor.Action;
import com.yheriatovych.reductor.annotations.CombinedState;
import com.yheriatovych.reductor.processor.Env;
import com.yheriatovych.reductor.processor.ValidationException;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
                return null;
            } else {
                throw new ValidationException(typeElement, "Only interfaces and @AutoValue classes are supported as @%s", CombinedState.class.getSimpleName());
            }
        }

        List<StateProperty> properties = new ArrayList<>();
        for (Element element : typeElement.getEnclosedElements()) {
            StateProperty stateProperty = StateProperty.parseStateProperty(element);
            if (stateProperty != null) {
                properties.add(stateProperty);
            }
        }

        return new CombinedStateElement(typeElement, properties);
    }

    private static boolean hasAutoValueAnnotation(TypeElement typeElement, Env env) {
        Name autoValueName = env.getElements().getName("com.google.auto.value.AutoValue");
        for (AnnotationMirror annotationMirror : typeElement.getAnnotationMirrors()) {
            DeclaredType annotationType = annotationMirror.getAnnotationType();
            Name qualifiedName = MoreElements.asType(annotationType.asElement()).getQualifiedName();
            if (qualifiedName.equals(autoValueName)) {
                return true;
            }
        }
        return false;
    }

    public TypeName getCombinedReducerActionType() {
// Path: lib/src/main/java/com/yheriatovych/reductor/Action.java
// public class Action {
//     public final String type;
//     public final Object[] values;
// 
//     /**
//      * Create Action object with specified type and values
//      *
//      * @param type   String type of action, will be used by {@link Reducer} for dispatch
//      * @param values any number of arbitrary objects that can be attached as payload to this action
//      */
//     public Action(String type, Object[] values) {
//         this.type = type;
//         this.values = values;
//     }
// 
//     /**
//      * Create Action with defined type without any attached payload
//      *
//      * @param type String type of action, will be used by {@link Reducer} for dispatch
//      */
//     public Action(String type) {
//         this(type, new Object[0]);
//     }
// 
//     /**
//      * Factory method to create action with defined type and any number of attached values as payload
//      *
//      * @param type   String type of action, will be used by {@link Reducer} for dispatch
//      * @param values any number of arbitrary objects that can be attached as payload to this action
//      * @return created Action
//      */
//     public static Action create(String type, Object... values) {
//         return new Action(type, values);
//     }
// 
//     /**
//      * Returns action value at given position
//      * @param index value position
//      * @return value at position
//      */
//     @SuppressWarnings("unchecked")
//     public <T> T getValue(int index) {
//         return (T) values[index];
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         Action action = (Action) o;
// 
//         if (type != null ? !type.equals(action.type) : action.type != null) return false;
//         // Probably incorrect - comparing Object[] arrays with Arrays.equals
//         return Arrays.equals(values, action.values);
// 
//     }
// 
//     @Override
//     public int hashCode() {
//         int result = type != null ? type.hashCode() : 0;
//         result = 31 * result + Arrays.hashCode(values);
//         return result;
//     }
// 
//     @Override
//     public String toString() {
//         return "Action{" +
//                 "type='" + type + '\'' +
//                 ", values=" + Arrays.toString(values) +
//                 '}';
//     }
// }
// 
// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/Env.java
// public class Env {
//     private final Types types;
//     private final Elements elements;
//     private final Messager messager;
//     private final Filer filer;
// 
//     public Env(Types types, Elements elements, Messager messager, Filer filer) {
//         this.types = types;
//         this.elements = elements;
//         this.messager = messager;
//         this.filer = filer;
//     }
// 
//     public void printError(Element element, String message, Object... args) {
//         printMessage(Diagnostic.Kind.ERROR, element, message, args);
//     }
// 
//     private void printMessage(Diagnostic.Kind level, Element element, String message, Object... args) {
//         messager.printMessage(level, String.format(message, args), element);
//     }
// 
//     public Types getTypes() {
//         return types;
//     }
// 
//     public Filer getFiler() {
//         return filer;
//     }
// 
//     public String getPackageName(Element element) {
//         return elements.getPackageOf(element).getQualifiedName().toString();
//     }
// 
//     public TypeMirror asType(Class<?> clazz) {
//         return elements.getTypeElement(clazz.getCanonicalName())
//                 .asType();
//     }
// 
//     public Elements getElements() {
//         return elements;
//     }
// }
// 
// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/ValidationException.java
// public class ValidationException extends Exception {
//     private final Element element;
//     private final String message;
// 
//     public ValidationException(Element element, String message, Object... args) {
//         this.element = element;
//         this.message = String.format(message, args);
//     }
// 
//     public Element getElement() {
//         return element;
//     }
// 
//     @Override
//     public String getMessage() {
//         return message;
//     }
// 
// }


// Path: compiller/src/main/java/com/yheriatovych/reductor/processor/combinedstate/CombinedStateElement.java
import com.google.auto.common.MoreElements;
import com.squareup.javapoet.TypeName;
import com.yheriatovych.reductor.Action;
import com.yheriatovych.reductor.annotations.CombinedState;
import com.yheriatovych.reductor.processor.Env;
import com.yheriatovych.reductor.processor.ValidationException;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

                return null;
            } else {
                throw new ValidationException(typeElement, "Only interfaces and @AutoValue classes are supported as @%s", CombinedState.class.getSimpleName());
            }
        }

        List<StateProperty> properties = new ArrayList<>();
        for (Element element : typeElement.getEnclosedElements()) {
            StateProperty stateProperty = StateProperty.parseStateProperty(element);
            if (stateProperty != null) {
                properties.add(stateProperty);
            }
        }

        return new CombinedStateElement(typeElement, properties);
    }

    private static boolean hasAutoValueAnnotation(TypeElement typeElement, Env env) {
        Name autoValueName = env.getElements().getName("com.google.auto.value.AutoValue");
        for (AnnotationMirror annotationMirror : typeElement.getAnnotationMirrors()) {
            DeclaredType annotationType = annotationMirror.getAnnotationType();
            Name qualifiedName = MoreElements.asType(annotationType.asElement()).getQualifiedName();
            if (qualifiedName.equals(autoValueName)) {
                return true;
            }
        }
        return false;
    }

    public TypeName getCombinedReducerActionType() {
        return TypeName.get(Action.class);