/*
 * Copyright 2020, 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.gen.java.field;

import com.google.common.collect.ImmutableList;
import io.spine.code.java.SimpleClassName;
import io.spine.code.proto.FieldDeclaration;
import io.spine.type.MessageType;

import java.util.List;
import java.util.Queue;
import java.util.Set;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Lists.newLinkedList;
import static com.google.common.collect.Sets.newHashSet;

/**
 * A recursive collector of {@link com.google.protobuf.Message Message}-typed fields from
 * a message.
 */
final class NestedFieldScanner {

    private final MessageType messageType;

    NestedFieldScanner(MessageType messageType) {
        this.messageType = messageType;
    }

    /**
     * Traverses all top-level and nested fields of the enclosed message type to retrieve those
     * that are singular {@link com.google.protobuf.Message Message}-typed fields.
     *
     * @apiNote The returned results are unique-by-name. The reason for that is that
     *        uniqueness-by-name is required by the Spine routines when generating the
     *        strongly-typed fields of the message.
     *
     * @see FieldContainerSpec
     * @see MessageTypedField
     */
    List<MessageType> scan() {
        List<MessageType> resultTypes = newLinkedList();
        Queue<MessageType> typesToScan = newLinkedList(ImmutableList.of(messageType));
        while (!typesToScan.isEmpty()) {
            MessageType type = typesToScan.poll();
            ImmutableList<MessageType> messageTypes = uniqueMessageFields(type, resultTypes);
            resultTypes.addAll(messageTypes);
            typesToScan.addAll(messageTypes);
        }
        return resultTypes;
    }

    /**
     * Obtains {@code Message}-typed fields of a message with names that not yet occur among
     * the result types.
     */
    private static ImmutableList<MessageType> uniqueMessageFields(MessageType messageType,
                                                                  List<MessageType> resultTypes) {
        Set<SimpleClassName> distinctNames = newHashSet();
        ImmutableList<MessageType> uniqueMessageTypes =
                messageType.fields()
                           .stream()
                           .filter(FieldDeclaration::isSingularMessage)
                           .map(FieldDeclaration::messageType)
                           .filter(type -> distinctNames.add(type.simpleJavaClassName()))
                           .filter(type -> !containsTypeWithSameName(resultTypes, type))
                           .collect(toImmutableList());
        return uniqueMessageTypes;
    }

    private static boolean containsTypeWithSameName(List<MessageType> result, MessageType type) {
        boolean contains = result.stream()
                                 .map(MessageType::simpleJavaClassName)
                                 .anyMatch(name -> name.equals(type.simpleJavaClassName()));
        return contains;
    }
}

@@ -2004,20 +2004,20 @@
 e unique
--by-
+ by 
 name. Th
@@ -2082,19 +2082,25 @@
  is 
+a 
 require
-d by
+ment for
  the
@@ -2121,18 +2121,19 @@
 s wh
-en
+ich
  generat
 ing 
@@ -2132,14 +2132,8 @@
 erat
-ing th
 e%0A  
@@ -2148,44 +2148,60 @@
     
-strongly-typed fields of
+additional message code based on
  the 
-message
+collected types
 .%0A  

Reword doc