/*
 * 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.query;

import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import io.spine.code.gen.java.GeneratedJavadoc;
import io.spine.code.gen.java.GeneratedMethodSpec;
import io.spine.code.gen.java.JavaPoetName;
import io.spine.code.proto.FieldDeclaration;
import io.spine.query.EntityColumn;
import io.spine.query.EntityCriterion;
import io.spine.query.EntityQueryBuilder;

import static javax.lang.model.element.Modifier.PUBLIC;

/**
 * Generates the column criterion method for {@link EntityQueryBuilder
 * EntityQueryBuilder} to restrict the value of the column to some value.
 */
final class QueryColumnSpec implements GeneratedMethodSpec {

    private final FieldDeclaration column;
    private final TypeName queryBuilderName;
    private final TypeName entityStateName;
    private final TypeName returningValueName;

    QueryColumnSpec(FieldDeclaration column, TypeName queryBuilderName) {
        this.column = column;
        this.queryBuilderName = queryBuilderName;
        this.entityStateName = JavaPoetName.of(column.declaringType())
                                           .value();
        this.returningValueName = JavaPoetName.of(column)
                                              .value().box();
    }

    @Override
    public MethodSpec methodSpec() {
        MethodSpec result = MethodSpec
                .methodBuilder(columnName())
                .addJavadoc(javadoc().spec())
                .addModifiers(PUBLIC)
                .returns(queryCriterion())
                .addStatement(methodBody())
                .build();
        return result;
    }

    /**
     * Returns the column name as it looks in the generated Java code.
     */
    private String columnName() {
        return column.name()
                     .javaCase();
    }

    /**
     * Returns the name of the Java type of a column.
     */
    private ParameterizedTypeName queryCriterion() {
        JavaPoetName result = JavaPoetName.of(EntityCriterion.class);
        ParameterizedTypeName parameterizedResult =
                ParameterizedTypeName.get(result.className(),
                                          entityStateName, returningValueName, queryBuilderName);
        return parameterizedResult;
    }

    /**
     * Returns the method body which instantiates the {@link EntityColumn}.
     */
    private CodeBlock methodBody() {
        return CodeBlock.of(
                "return new $T<>(Column.$L(), this)",
                EntityCriterion.class,
                columnName()
        );
    }

    /**
     * Returns the method Javadoc.
     */
    private GeneratedJavadoc javadoc() {
        String columnName = column.name()
                                  .javaCase();
        return GeneratedJavadoc.singleParagraph(
                CodeBlock.of("Creates a criterion for the {@link Column#$L() Column.$L()} column.",
                             columnName, columnName)
        );
    }
}

@@ -1597,24 +1597,48 @@
 nerates the 
+method which produces a 
 column crite
@@ -1642,23 +1642,16 @@
 iterion 
-method 
 for %7B@li
@@ -1739,21 +1739,25 @@
 to some 
-value
+parameter
 .%0A */%0Afi
@@ -1996,16 +1996,442 @@
 eName;%0A%0A
+    /**%0A     * Creates a new method specification which serves to generated a criterion method%0A     * for the passed column in scope of a query builder with the passed type name.%0A     *%0A     * @param column%0A     *         the name of the column, for which the criterion generation is needed%0A     * @param queryBuilderName%0A     *         the type name of the query builder in scope of which the generation is performed%0A     */%0A
     Quer
@@ -2812,16 +2812,63 @@
 .value()
+%0A                                              
 .box();%0A
@@ -3419,32 +3419,433 @@
 aCase();%0A    %7D%0A%0A
+    /**%0A     * Returns the method Javadoc.%0A     */%0A    private GeneratedJavadoc javadoc() %7B%0A        String columnName = column.name()%0A                                  .javaCase();%0A        return GeneratedJavadoc.singleParagraph(%0A                CodeBlock.of(%22Creates a criterion for the %7B@link Column#$L() Column.$L()%7D column.%22,%0A                             columnName, columnName)%0A        );%0A    %7D%0A%0A
     /**%0A     * R
@@ -4527,16 +4527,16 @@
 .class,%0A
+
         
@@ -4576,408 +4576,7 @@
    %7D
-%0A%0A    /**%0A     * Returns the method Javadoc.%0A     */%0A    private GeneratedJavadoc javadoc() %7B%0A        String columnName = column.name()%0A                                  .javaCase();%0A        return GeneratedJavadoc.singleParagraph(%0A                CodeBlock.of(%22Creates a criterion for the %7B@link Column#$L() Column.$L()%7D column.%22,%0A                             columnName, columnName)%0A        );%0A    %7D
 %0A%7D%0A

Document the ctor and rearrange the methods.