package io.spine.tools.protoc;

import com.google.common.collect.ImmutableList;
import io.spine.annotation.Internal;
import io.spine.code.java.ClassName;

import java.util.Map;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Maps.newHashMap;

/**
 * A configuration of strongly-typed message fields to be generated for certain message types.
 *
 * @see io.spine.base.SubscribableField
 */
public final class GeneratedFields extends GeneratedConfigurations<AddFields> {

    private EntityStateConfig entityStateConfig = EntityStateConfig.getDefaultInstance();
    private final Map<String, ClassName> byType = newHashMap();

    /**
     * Configures strongly-typed fields generation for messages that represent an entity state.
     *
     * <p>Example:
     * <pre>
     * generateFor messages().entityState(), markAs("com.some.custom.Field")
     * </pre>
     *
     * <p>The statement above enables the strongly-typed fields generation for all messages that
     * represent an entity state and marks the generated fields as {@code some.custom.Field}.
     *
     * <p>It is expected by the Spine routines that the type passed to the {@code markAs} method is
     * a descendant of {@link io.spine.base.EntityStateField} and has a constructor accepting a
     * single {@link io.spine.base.Field Field} argument.
     */
    @SuppressWarnings("unused") // Gradle DSL.
    public final void generateFor(EntityState entityState, ClassName markAs) {
        entityStateConfig = EntityStateConfig
                .newBuilder()
                .setValue(markAs.value())
                .build();
    }

    /**
     * Configures the strongly-typed fields generation for messages declared in files matching
     * a given pattern.
     *
     * <p>Example:
     * <pre>
     * generateFor messages().inFiles(suffix: "messages.proto"), markAs("com.some.custom.Field")
     * </pre>
     *
     * <p>The statement above enables strongly-typed fields generation for all messages residing in
     * files with names ending with {@code messages.proto} and marks the generated fields as
     * {@code some.custom.Field}.
     *
     * <p>It is expected by the Spine routines that the type passed to the {@code markAs} method is
     * a descendant of {@link io.spine.base.SubscribableField} and has a constructor accepting a
     * single {@link io.spine.base.Field Field} argument.
     *
     * <p>In case the field generation is configured for {@link io.spine.base.EventMessage event}
     * and {@link io.spine.base.RejectionMessage rejection} messages, it is expected that the
     * passed field type inherits from {@link io.spine.base.EventMessageField}.
     *
     * <p>The configuration may be applied multiple times to enable code generation for multiple
     * file patterns.
     */
    public final void generateFor(PatternSelector pattern, ClassName markAs) {
        addPattern(pattern, markAs);
    }

    /**
     * Configures the strongly-typed fields generation for a message type with the passed name.
     *
     * <p>Example:
     * <pre>
     * generateFor "custom.message.Type", markAs("com.some.custom.Field")
     * </pre>
     *
     * <p>The statement above enables strongly-typed fields generation for the message type with
     * Proto name {@code custom.message.Type} and marks the generated fields as
     * {@code some.custom.Field}.
     *
     * <p>It is expected by the Spine routines that the type passed to the {@code markAs} method is
     * a descendant of {@link io.spine.base.SubscribableField} and has a constructor accepting a
     * single {@link io.spine.base.Field Field} argument.
     *
     * <p>The configuration may be applied multiple times to enable code generation for multiple
     * message types.
     */
    public final void generateFor(String type, ClassName markAs) {
        byType.put(type, markAs);
    }

    /**
     * A syntax sugar method used for a more natural Gradle DSL.
     */
    @SuppressWarnings({"MethodMayBeStatic", "unused"}) // Gradle DSL.
    public final ClassName markAs(String type) {
        return ClassName.of(type);
    }

    @Internal
    @Override
    public AddFields asProtocConfig() {
        AddFields.Builder result = AddFields
                .newBuilder()
                .setEntityStateConfig(entityStateConfig)
                .addAllConfigByType(generatedTypes());
        patternConfigurations()
                .stream()
                .map(GeneratedConfigurations::toPatternConfig)
                .forEach(result::addConfigByPattern);
        return result.build();
    }

    private Iterable<ConfigByType> generatedTypes() {
        ImmutableList<ConfigByType> result =
                byType.entrySet()
                      .stream()
                      .map(entry -> configByType(entry.getKey(), entry.getValue()))
                      .collect(toImmutableList());
        return result;
    }

    private static ConfigByType configByType(String type, ClassName markAs) {
        TypePattern pattern = TypePattern
                .newBuilder()
                .setExpectedType(type)
                .build();
        ConfigByType result = ConfigByType
                .newBuilder()
                .setValue(markAs.value())
                .setPattern(pattern)
                .build();
        return result;
    }
}

@@ -1221,32 +1221,65 @@
 thod is%0A     * a
+ %7B@code public%7D non-%7B@code final%7D
  descendant of %7B
@@ -1311,38 +1311,60 @@
 yStateField%7D and
- has a
+%0A     * has a %7B@code public%7D
  constructor acc
@@ -1363,39 +1363,32 @@
 ctor accepting a
-%0A     *
  single %7B@link i
@@ -1404,32 +1404,39 @@
 ase.Field Field%7D
+%0A     *
  argument.%0A     
@@ -2333,32 +2333,65 @@
 thod is%0A     * a
+ %7B@code public%7D non-%7B@code final%7D
  descendant of %7B
@@ -2424,38 +2424,60 @@
 ibableField%7D and
- has a
+%0A     * has a %7B@code public%7D
  constructor acc
@@ -2476,39 +2476,32 @@
 ctor accepting a
-%0A     *
  single %7B@link i
@@ -2517,32 +2517,39 @@
 ase.Field Field%7D
+%0A     *
  argument.%0A     
@@ -2722,38 +2722,11 @@
 es, 
-it is expected that the%0A     *
+the
  pas
@@ -2743,16 +2743,30 @@
 type
+ should%0A     *
  inherit
 s fr
@@ -2761,17 +2761,16 @@
  inherit
-s
  from %7B@
@@ -3629,16 +3629,49 @@
      * a
+ %7B@code public%7D non-%7B@code final%7D
  descend
@@ -3720,22 +3720,44 @@
 eld%7D and
- has a
+%0A     * has a %7B@code public%7D
  constru
@@ -3772,23 +3772,16 @@
 epting a
-%0A     *
  single 
@@ -3813,16 +3813,23 @@
 d Field%7D
+%0A     *
  argumen

Clarify requirements for the passed field types