/*
 * Copyright 2019, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.code.proto.enrichment;

import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.Descriptors.Descriptor;
import io.spine.code.proto.FieldName;
import io.spine.code.proto.ref.TypeRef;

import java.util.Optional;
import java.util.stream.Stream;

/**
 * Provides typical type references used for referencing types in proto definitions.
 *
 * <p>A type reference is usually used in a field reference.
 */
@Immutable
enum BuiltIn implements TypeRef {

    ANY("") {

        /**
         * Accepts all message types.
         *
         * <p>Since any message can reference its field, this method always returns {@code true}
         * since validity of a field reference for such a message is outside of scope of
         * responsibilities of {@link TypeRef}.
         * @see FieldRef
         */
        @Override
        public boolean test(Descriptor message) {
            return true;
        }
    },

    /**
     * A reference to a message context.
     *
     * <p>The actual type of the message context message and syntax of fields allowed in
     * such a reference is determined by the type of a message, such as {@code EventContext} or
     * {@code CommandContext}.
     */
    CONTEXT("context") {

        /**
         * The suffix for message types names which supports such a type reference.
         */
        private final String typeSuffix = FieldName.of(value())
                                                   .toCamelCase();
        /**
         * The prefix used in a field references to message context fields.
         */
        private final String fieldPrefix = value();

        @Override
        Optional<TypeRef> parse(String value) {
            if (value.startsWith(fieldPrefix)) {
                return Optional.of(this);
            }
            return Optional.empty();
        }

        /**
         * Accepts a message which type name ends with the {@code "Context"} suffix.
         */
        @Override
        public boolean test(Descriptor message) {
            return message.getName()
                          .endsWith(typeSuffix);
        }

        /**
         * Overrides to make final avoiding overridable call in the {@link #typeSuffix} initialization.
         */
        @Override
        public final String value() {
            return super.value();
        }
    };

    /** The value of the reference. */
    private final String value;

    BuiltIn(String value) {
        this.value = value;
    }

    /**
     * Matches the passed string to see if it represents this type reference.
     *
     * @return {@code this} if the string matches this type reference,
     *         empty {@code Optional} otherwise
     */
    Optional<TypeRef> parse(String value) {
        if (value().equals(value)) {
            return Optional.of(this);
        }
        return Optional.empty();
    }

    /** Obtains the value of the reference. */
    @Override
    public String value() {
        return this.value;
    }

    /**
     * Finds a value matching the passed string.
     */
    static Optional<TypeRef> parseAll(String value) {
        Optional<TypeRef> result =
                Stream.of(values())
                      .map(v -> v.parse(value))
                      .filter(Optional::isPresent)
                      .findFirst()
                      .orElse(Optional.empty());
        return result;
    }
}

@@ -1259,40 +1259,8 @@
 nal;
-%0Aimport java.util.stream.Stream;
 %0A%0A/*
@@ -2467,380 +2467,8 @@
 ();%0A
-        /**%0A         * The prefix used in a field references to message context fields.%0A         */%0A        private final String fieldPrefix = value();%0A%0A        @Override%0A        Optional%3CTypeRef%3E parse(String value) %7B%0A            if (value.startsWith(fieldPrefix)) %7B%0A                return Optional.of(this);%0A            %7D%0A            return Optional.empty();%0A        %7D%0A%0A
     
@@ -3328,24 +3328,30 @@
      */%0A    
+final 
 Optional%3CTyp
@@ -3584,32 +3584,32 @@
 tring value() %7B%0A
+
         return t
@@ -3628,410 +3628,7 @@
    %7D
-%0A%0A    /**%0A     * Finds a value matching the passed string.%0A     */%0A    static Optional%3CTypeRef%3E parseAll(String value) %7B%0A        Optional%3CTypeRef%3E result =%0A                Stream.of(values())%0A                      .map(v -%3E v.parse(value))%0A                      .filter(Optional::isPresent)%0A                      .findFirst()%0A                      .orElse(Optional.empty());%0A        return result;%0A    %7D
 %0A%7D%0A

Remove unused code