/**
 * Copyright 2011-2015 John Ericksen
 *
 * 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 org.androidtransfuse.gen;

import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JPackage;
import com.sun.codemodel.JType;

import org.androidtransfuse.TransfuseAnalysisException;
import org.androidtransfuse.adapter.ASTArrayType;
import org.androidtransfuse.adapter.ASTType;
import org.androidtransfuse.adapter.PackageClass;
import org.androidtransfuse.validation.Validator;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

/**
 * Utility class unifying the creation of a basic class from a PackageClass
 *
 * Annotates the generated class with the recommended annotation:
 * `@Generated(value = "org.androidtransfuse.TransfuseAnnotationProcessor", date = "2001-07-04T12:08:56.235-0700")`
 *
 * @author John Ericksen
 */
public class ClassGenerationUtil {

    private final JCodeModel codeModel;
    private final ClassGenerationStrategy classGenerationStrategy;
    private final Validator validator;

    @Inject
    public ClassGenerationUtil(JCodeModel codeModel, ClassGenerationStrategy classGenerationStrategy, Validator validator) {
        this.codeModel = codeModel;
        this.classGenerationStrategy = classGenerationStrategy;
        this.validator = validator;
    }

    public JDefinedClass defineClass(PackageClass className) throws JClassAlreadyExistsException {

        JPackage jPackage = codeModel._package(className.getPackage());

        JDefinedClass definedClass = jPackage._class(className.getClassName());

        classGenerationStrategy.annotateGenerated(definedClass);
        definedClass.annotate(SuppressWarnings.class).param("value", "unchecked");

        return definedClass;
    }

    public JType type(Class clazz){
        return codeModel._ref(clazz);
    }

    public JType type(ASTType astType){

        try {
            return codeModel.parseType(astType.getName());
        } catch (ClassNotFoundException e) {
            validator.error("Unable to parse type " + astType.getName()).element(astType).build();
            throw new TransfuseAnalysisException("Unable to parse type " + astType.getName(), e);
        } catch (ExceptionInInitializerError e){
            // Tried to initialize an Android class
            return codeModel.directClass(astType.getName());
        } catch (NoClassDefFoundError e){
            return codeModel.directClass(astType.getName());
        }
    }

    public JClass ref(PackageClass packageClass){
        return ref(packageClass.getFullyQualifiedName());
    }

    public JClass ref(ASTType astType){
        String typeName = astType.getName();
        if(astType instanceof ASTArrayType){
            return ref(typeName.substring(0, typeName.length() - 2)).array();
        }
        return ref(typeName);
    }

    public JClass narrowRef(ASTType astType){
        JClass reference = ref(astType);

        if(astType.getGenericParameters().size() > 0){
            List<JClass> genericParameterRefs = new ArrayList<JClass>();

            for (ASTType genericParam : astType.getGenericParameters()) {
                genericParameterRefs.add(narrowRef(genericParam));
            }

            reference = reference.narrow(genericParameterRefs);
        }

        return reference;
    }

    public JClass ref(String typeName){
        return codeModel.directClass(typeName);
    }

    public JClass ref(Class<?> clazz) {
        return codeModel.ref(clazz);
    }
}

@@ -2338,16 +2338,21 @@
 aram
+Array
 (%22value%22
 , %22u
@@ -2347,18 +2347,24 @@
 (%22value%22
-, 
+).param(
 %22uncheck
@@ -2367,16 +2367,37 @@
 hecked%22)
+.param(%22deprecation%22)
 ;%0A%0A     

add suppress for deprecation warnings