/*
 * Copyright 2021, TeamDev. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.mc.java.annotation.check;

import com.google.protobuf.Descriptors.FieldDescriptor;
import io.spine.code.proto.FieldName;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jboss.forge.roaster.model.JavaType;
import org.jboss.forge.roaster.model.TypeHolder;
import org.jboss.forge.roaster.model.impl.AbstractJavaSource;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.JavaSource;
import org.jboss.forge.roaster.model.source.MethodSource;

import java.util.Optional;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.spine.code.java.SimpleClassName.ofBuilder;
import static io.spine.tools.mc.java.annotation.check.Annotations.findInternalAnnotation;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class FieldAnnotationCheck implements SourceCheck {

    private final FieldDescriptor fieldDescriptor;
    private final boolean shouldBeAnnotated;

    public FieldAnnotationCheck(FieldDescriptor fieldDescriptor,
                                boolean shouldBeAnnotated) {
        this.fieldDescriptor = fieldDescriptor;
        this.shouldBeAnnotated = shouldBeAnnotated;
    }

    @Override
    public void accept(@Nullable AbstractJavaSource<JavaClassSource> input) {
        checkNotNull(input);
        JavaClassSource message = (JavaClassSource) input;
        JavaClassSource messageBuilder = getBuilder(message);
        checkAccessorsAnnotation(message);
        checkAccessorsAnnotation(messageBuilder);
    }

    private void checkAccessorsAnnotation(JavaClassSource message) {
        String fieldName = FieldName.of(fieldDescriptor.toProto())
                                    .toCamelCase();
        for (MethodSource<?> method : message.getMethods()) {
            if (method.isPublic() && method.getName()
                                           .contains(fieldName)) {
                Optional<?> annotation = findInternalAnnotation(method);
                if (shouldBeAnnotated) {
                    assertTrue(annotation.isPresent());
                } else {
                    assertFalse(annotation.isPresent());
                }
            }
        }
    }

    private static JavaClassSource getBuilder(JavaSource<?> messageSource) {
        TypeHolder<?> messageType = (TypeHolder<?>) messageSource;
        JavaType<?> builderType = messageType.getNestedType(ofBuilder().value());
        return (JavaClassSource) builderType;
    }
}

@@ -1384,68 +1384,8 @@
 me;%0A
-import org.checkerframework.checker.nullness.qual.Nullable;%0A
 impo
@@ -1472,24 +1472,79 @@
 TypeHolder;%0A
+import org.jboss.forge.roaster.model.VisibilityScoped;%0A
 import org.j
@@ -1713,94 +1713,8 @@
 rce;
-%0Aimport org.jboss.forge.roaster.model.source.MethodSource;%0A%0Aimport java.util.Optional;
 %0A%0Aim
@@ -1840,98 +1840,8 @@
 er;%0A
-import static io.spine.tools.mc.java.annotation.check.Annotations.findInternalAnnotation;%0A
 impo
@@ -2181,40 +2181,8 @@
 tor,
-%0A                               
  boo
@@ -2355,18 +2355,8 @@
 ept(
-@Nullable 
 Abst
@@ -2525,26 +2525,25 @@
 ilder = 
-getB
+b
 uilder
+Of
 (message
@@ -2844,38 +2844,8 @@
     
- for (MethodSource%3C?%3E method :
  mes
@@ -2861,19 +2861,16 @@
 ethods()
-) %7B
 %0A       
@@ -2878,47 +2878,18 @@
     
-if (method.isPublic() && method.getN
+   .stre
 am
-e
 ()%0A 
@@ -2906,36 +2906,93 @@
     
-                            
+.filter(VisibilityScoped::isPublic)%0A               .filter(method -%3E method.getName()
 .con
@@ -3008,18 +3008,16 @@
 ldName))
- %7B
 %0A       
@@ -3028,22 +3028,14 @@
     
- Optional%3C?%3E a
+.map(A
 nnot
@@ -3039,19 +3039,19 @@
 notation
- = 
+s::
 findInte
@@ -3068,18 +3068,53 @@
 tion
-(method);%0A
+)%0A               .forEach(annotation -%3E %7B%0A   
     
@@ -3166,24 +3166,27 @@
             
+   
 assertTrue(a
@@ -3225,16 +3225,19 @@
         
+ 
+  
 %7D else %7B
@@ -3237,16 +3237,19 @@
  else %7B%0A
+   
         
@@ -3305,34 +3305,40 @@
                 
-%7D%0A
+   %7D%0A   
             %7D%0A  
@@ -3338,18 +3338,10 @@
    %7D
-%0A        %7D
+);
 %0A   
@@ -3383,18 +3383,17 @@
 rce 
-getB
+b
 uilder
+Of
 (Jav

Improve names and code flow