/*
 * 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.tools.compiler.annotation;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.protobuf.Descriptors;
import io.spine.code.java.ClassName;
import io.spine.code.proto.TypeSet;
import org.jboss.forge.roaster.model.Method;
import org.jboss.forge.roaster.model.impl.AbstractJavaSource;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.MethodHolderSource;

import java.nio.file.Path;

/**
 * An {@link Annotator} which annotates methods matching given patterns.
 *
 * <p>Note that, unlike other annotator implementations, {@code MethodNameAnnotator} works with
 * multiple method patterns rather than just one due to performance considerations. All
 * the generated classes are parsed and checked. This can take some time. Thus, consider
 * the selective approach to marking methods unless a pattern or a set of patterns is completely
 * necessary.
 */
final class MethodNameAnnotator extends Annotator {

    private final ImmutableSet<MethodPattern> patterns;

    MethodNameAnnotator(ClassName annotation,
                        ImmutableSet<MethodPattern> patterns,
                        ImmutableList<Descriptors.FileDescriptor> descriptors,
                        Path genProtoDir) {
        super(annotation, descriptors, genProtoDir);
        this.patterns = patterns;
    }

    @Override
    public void annotate() {
        if (!patterns.isEmpty()) {
            SourceVisitor<?> visitor = new AnnotateMethods();
            descriptors().stream()
                         .map(TypeSet::messagesAndEnums)
                         .flatMap(typeSet -> typeSet.types().stream())
                         .map(type -> type.javaClassName().resolveFile())
                         .forEach(file -> rewriteSource(file, visitor));
        }
    }

    /**
     * A function annotating methods of a given source according to the patterns.
     */
    private final class AnnotateMethods implements ClassInDepthVisitor {

        @Override
        public void accept(AbstractJavaSource<JavaClassSource> source) {
            allClasses(source)
                    .flatMap(classSource -> classSource instanceof MethodHolderSource
                                            ? ((MethodHolderSource<?>) classSource).getMethods()
                                                                                   .stream()
                                            : Stream.<MethodSource<?>>of())
                    .filter(Method::isPublic)
                    .filter(this::matching)
                    .forEach(MethodNameAnnotator.this::addAnnotation);

        }

        private boolean matching(Method<?, ?> method) {
            String methodName = method.getName();
            return patterns.stream()
                           .anyMatch(pattern -> pattern.matches(methodName));
        }
    }
}

@@ -3169,77 +3169,13 @@
     
-allClasses(source)%0A                    .flatMap(classSource -%3E classS
+if (s
 ourc
@@ -3209,37 +3209,12 @@
 urce
-%0A                            
+) %7B%0A
     
@@ -3224,18 +3224,16 @@
         
- ?
  ((Metho
@@ -3254,89 +3254,54 @@
 ?%3E) 
-classSource).getMethods()%0A                                                       
+source)%0A                        .getMethods()%0A
     
@@ -3320,20 +3320,16 @@
         
-    
 .stream(
@@ -3338,80 +3338,8 @@
     
-                                        : Stream.%3CMethodSource%3C?%3E%3Eof())%0A
     
@@ -3380,16 +3380,20 @@
 Public)%0A
+    
         
@@ -3440,32 +3440,36 @@
                 
+ 
+   
 .forEach(MethodN
@@ -3503,16 +3503,29 @@
 ation);%0A
+            %7D
 %0A       

Simplify annotation process.