/**
 *
 * Copyright (c) 2006-2017, Speedment, Inc. 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.speedment.plugins.enums.internal;

import com.speedment.common.codegen.constant.SimpleType;
import com.speedment.common.codegen.model.*;
import com.speedment.common.codegen.model.Enum;
import com.speedment.common.injector.Injector;
import com.speedment.generator.core.exception.SpeedmentGeneratorException;
import com.speedment.generator.translator.JavaClassTranslator;
import com.speedment.generator.translator.Translator;
import com.speedment.generator.translator.TranslatorDecorator;
import com.speedment.generator.translator.namer.JavaLanguageNamer;
import com.speedment.plugins.enums.StringToEnumTypeMapper;
import com.speedment.runtime.config.Column;
import com.speedment.runtime.config.Table;
import com.speedment.runtime.config.trait.HasEnabled;
import com.speedment.runtime.core.util.OptionalUtil;
import com.speedment.runtime.field.ReferenceField;
import com.speedment.runtime.typemapper.TypeMapper;

import java.lang.reflect.Type;
import java.util.List;

import static com.speedment.common.codegen.constant.DefaultAnnotationUsage.OVERRIDE;
import static com.speedment.common.codegen.constant.DefaultType.WILDCARD;
import static com.speedment.common.codegen.constant.DefaultType.classOf;
import static com.speedment.common.codegen.model.Value.*;
import static com.speedment.common.codegen.util.Formatting.indent;
import static com.speedment.common.codegen.util.Formatting.shortName;
import static com.speedment.generator.standard.internal.util.ColumnUtil.usesOptional;
import static com.speedment.runtime.config.util.DocumentDbUtil.isUnique;
import static java.util.Objects.requireNonNull;

/**
 *
 * @author  Emil Forslund
 * @author  Simon Jonasson
 * @since   1.0.0
 */
public final class GeneratedEntityDecorator
implements TranslatorDecorator<Table, Interface> {
    
    public final static String
        FROM_DATABASE_METHOD = "fromDatabase",
        TO_DATABASE_METHOD   = "toDatabase",
        DATABASE_NAME_FIELD  = "databaseName";
    
    private final Injector injector;
    
    public GeneratedEntityDecorator(Injector injector) {
        this.injector = requireNonNull(injector);
    }
    
    @Override
    public void apply(JavaClassTranslator<Table, Interface> translator) {
        translator.onMake((file, builder) -> {
            builder.forEveryTable(Translator.Phase.POST_MAKE, (intrf, table) -> {
                table.columns()
                    .filter(HasEnabled::test)
                    .filter(col -> col.getTypeMapper()
                        .filter(StringToEnumTypeMapper.class.getName()::equals)
                        .isPresent()
                    ).forEachOrdered(col -> {
                        final JavaLanguageNamer namer = translator.getSupport().namer();
                        
                        final String colEnumName = EnumGeneratorUtil.enumNameOf(col, injector);
                        final List<String> constants = EnumGeneratorUtil.enumConstantsOf(col);
                        final Type enumType = new GeneratedEnumType(colEnumName, constants);
                        
                        final Enum colEnum = Enum.of(shortName(colEnumName))
                            .add(Field.of(DATABASE_NAME_FIELD, String.class).private_().final_());
                        
                        // Generate enum constants
                        constants.forEach(constant -> {
                            final String javaName = namer.javaStaticFieldName(constant);
                            colEnum.add(EnumConstant.of(javaName).add(Value.ofText(constant)));
                        });
                        
                        // Generate constructor
                        colEnum.add(Constructor.of()
                            .add(Field.of(DATABASE_NAME_FIELD, String.class))
                            .add("this." + DATABASE_NAME_FIELD + " = " + DATABASE_NAME_FIELD + ";")
                        );
                        
                        // Generate fromDatabase()-method
                        final Method fromDatabase = Method.of(FROM_DATABASE_METHOD, enumType)
                            .public_().static_()
                            .add(Field.of(DATABASE_NAME_FIELD, String.class))
                            .add("switch (" + DATABASE_NAME_FIELD + ") {");
                        
                        constants.stream()
                            .map(s -> indent("case \"" + s + "\" : return " + namer.javaStaticFieldName(s) + ";"))
                            .forEachOrdered(fromDatabase::add);
                        
                        fromDatabase
                            .add(indent("default : throw new UnsupportedOperationException("))
                            .add(indent("\"Unknown enum constant '\" + " + DATABASE_NAME_FIELD + " + \"'.\"", 2))
                            .add(indent(");"))
                            .add("}");
                        
                        colEnum.add(fromDatabase);
                        
                        // Generate toDatabase()-method
                        colEnum.add(Method.of(TO_DATABASE_METHOD, String.class)
                            .public_().add("return " + DATABASE_NAME_FIELD + ";")
                        );
                        
                        // Add it to the interface.
                        intrf.add(colEnum);

                        // Rewrite the static field to use a inlined type mapper
                        // instead.
                        final String fieldName = namer.javaStaticFieldName(col.getJavaName());
                        final Field field = intrf.getFields().stream()
                            .filter(f -> f.getName().equals(fieldName))
                            .findFirst().orElseThrow(() -> new SpeedmentGeneratorException(
                                "Expected a static field with name '" +
                                fieldName + "' to be generated."
                            ));

                        if (usesOptional(col)) {
                            file.add(Import.of(OptionalUtil.class));
                        }

                        final String enumShortName = shortName(colEnumName);
                        final String enumVarName = namer.javaVariableName(enumShortName);
                        field.set(ofInvocation(ReferenceField.class, "create",
                            ofReference("Identifier." + fieldName),
                            ofReference(
                                usesOptional(col)
                                    ? "o -> " + OptionalUtil.class.getSimpleName() + ".unwrap(o.get" + translator.getSupport().typeName(col) + "()),"
                                    : translator.getSupport().entityName() + "::get" + translator.getSupport().typeName(col)
                            ),
                            ofReference(
                                translator.getSupport().entityName() + "::set" +
                                    translator.getSupport().typeName(col)
                            ),
                            ofAnonymous(TypeMapper.class)
                                .add(String.class)
                                .add(SimpleType.create(colEnumName))
                                .add(Method.of("getLabel", String.class)
                                    .public_().add(OVERRIDE)
                                    .add("return \"String to " + enumShortName + " Mapper\";")
                                )
                                .add(Method.of("getJavaType", Type.class)
                                    .public_().add(OVERRIDE)
                                    .add(Field.of("column", Column.class))
                                    .add("return " + enumShortName + ".class;")
                                )
                                .add(Method.of("toJavaType", enumType)
                                    .public_().add(OVERRIDE)
                                    .add(Field.of("column", Column.class))
                                    .add(Field.of("clazz", classOf(WILDCARD)))
                                    .add(Field.of("str", String.class))
                                    .add("return str == null ? null : " + enumShortName + "." + FROM_DATABASE_METHOD + "(str);")
                                )
                                .add(Method.of("toDatabaseType", String.class)
                                    .public_().add(OVERRIDE)
                                    .add(Field.of(enumVarName, enumType))
                                    .add("return " + enumVarName + " == null ? null : " + enumVarName + "." + TO_DATABASE_METHOD + "();")
                                ),
                            ofBoolean(isUnique(col))
                        ));
                    });
            });
        });
    }
}
@@ -2967,24 +2967,125 @@
 table) -%3E %7B%0A
+%0A                file.getImports().removeIf(i -%3E i.getType().equals(StringToEnumTypeMapper.class));%0A%0A
             

Remove StringToEnumTypeMapper import from generated code as it is not needed