package com.kboyarshinov.activityscreen.processor;

import com.squareup.javapoet.*;

import javax.annotation.processing.Filer;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Kirill Boyarshinov
 */
public final class CodeGenerator {
    private static final Map<String, String> ARGUMENT_TYPES = new HashMap<String, String>(20);

    static {
        ARGUMENT_TYPES.put("java.lang.String", "String");
        ARGUMENT_TYPES.put("int", "Int");
        ARGUMENT_TYPES.put("java.lang.Integer", "Int");
        ARGUMENT_TYPES.put("long", "Long");
        ARGUMENT_TYPES.put("java.lang.Long", "Long");
        ARGUMENT_TYPES.put("double", "Double");
        ARGUMENT_TYPES.put("java.lang.Double", "Double");
        ARGUMENT_TYPES.put("short", "Short");
        ARGUMENT_TYPES.put("java.lang.Short", "Short");
        ARGUMENT_TYPES.put("float", "Float");
        ARGUMENT_TYPES.put("java.lang.Float", "Float");
        ARGUMENT_TYPES.put("byte", "Byte");
        ARGUMENT_TYPES.put("java.lang.Byte", "Byte");
        ARGUMENT_TYPES.put("boolean", "Boolean");
        ARGUMENT_TYPES.put("java.lang.Boolean", "Boolean");
        ARGUMENT_TYPES.put("char", "Char");
        ARGUMENT_TYPES.put("java.lang.Character", "Char");
        ARGUMENT_TYPES.put("java.lang.CharSequence", "CharSequence");
        ARGUMENT_TYPES.put("android.os.Bundle", "Bundle");
        ARGUMENT_TYPES.put("android.os.Parcelable", "Parcelable");
    }

    private final Elements elementUtils;
    private final Filer filer;

    public CodeGenerator(Elements elementUtils, Filer filer) {
        this.elementUtils = elementUtils;
        this.filer = filer;
    }

    /**
     * Generate the java code
     *
     * @throws java.io.IOException
     */
    public void generate(Collection<ActivityScreenAnnotatedClass> annotatedClasses) throws IOException {
        for (ActivityScreenAnnotatedClass annotatedClass : annotatedClasses) {
            TypeElement annotatedClassElement = annotatedClass.getTypeElement();
            Name activitySimpleName = annotatedClassElement.getSimpleName();
            String screenClassName = activitySimpleName + ActivityScreenAnnotatedClass.SUFFIX;

            MethodSpec openMethod = createOpenMethod(activitySimpleName, false);
            MethodSpec openForResultMethod = createOpenMethod(activitySimpleName, true);

            MethodSpec privateConstructor = MethodSpec.constructorBuilder().
                    addModifiers(Modifier.PRIVATE).build();

            TypeSpec screenClass = TypeSpec.classBuilder(screenClassName).
                    addModifiers(Modifier.PUBLIC, Modifier.FINAL).
                    addMethod(privateConstructor).
                    addMethod(openMethod).
                    addMethod(openForResultMethod).
                    build();

            PackageElement pkg = elementUtils.getPackageOf(annotatedClassElement);

            String packageName = pkg.isUnnamed() ? "" : pkg.getQualifiedName().toString();
            JavaFile javaFile = JavaFile.builder(packageName, screenClass).indent("    ").build();

            javaFile.writeTo(filer);
        }
    }

    private static MethodSpec createOpenMethod(Name activitySimpleName, boolean forResult) {
        ClassName activityClassName = ClassName.get("android.app", "Activity");
        ClassName intentClassName = ClassName.get("android.content", "Intent");

        MethodSpec.Builder openMethodBuilder = MethodSpec.methodBuilder(forResult ? "openForResult" : "open").
                addModifiers(Modifier.PUBLIC, Modifier.STATIC).
                returns(void.class).
                addParameter(activityClassName, "activity");
        openMethodBuilder.addStatement("$T intent = new $T(activity, $L.class)", intentClassName, intentClassName, activitySimpleName);
        if (forResult) {
            openMethodBuilder.addParameter(TypeName.INT, "requestCode");
            openMethodBuilder.addStatement("activity.startActivityForResult(intent, requestCode)");
        } else {
            openMethodBuilder.addStatement("activity.startActivity(intent)");
        }
        return openMethodBuilder.build();
    }
}

@@ -511,16 +511,211 @@
 rator %7B%0A
+    private static final ClassName intentClassName = ClassName.get(%22android.content%22, %22Intent%22);%0A    private static final ClassName activityClassName = ClassName.get(%22android.app%22, %22Activity%22);%0A%0A
     priv
@@ -801,16 +801,16 @@
 g%3E(20);%0A
-
 %0A    sta
@@ -1930,24 +1930,25 @@
 ementUtils;%0A
+%0A
     private 
@@ -2661,35 +2661,37 @@
 ec openMethod = 
-cre
+gener
 ateOpenMethod(ac
@@ -2688,36 +2688,16 @@
 nMethod(
-activitySimpleName, 
 false);%0A
@@ -2741,19 +2741,21 @@
 ethod = 
-cre
+gener
 ateOpenM
@@ -2760,16 +2760,94 @@
 nMethod(
+true);%0A            MethodSpec createIntentMethod = generateCreateIntentMethod(
 activity
@@ -2860,14 +2860,8 @@
 Name
-, true
 );%0A%0A
@@ -3278,32 +3278,83 @@
 rResultMethod).%0A
+                    addMethod(createIntentMethod).%0A
                 
@@ -3726,19 +3726,21 @@
 hodSpec 
-cre
+gener
 ateOpenM
@@ -3749,214 +3749,28 @@
 hod(
-Name activitySimpleName, boolean forResult) %7B%0A        ClassName activityClassName = ClassName.get(%22android.app%22, %22Activity%22);%0A        ClassName intentClassName = ClassName.get(%22android.content%22, %22Intent%22);%0A
+boolean forResult) %7B
 %0A   
@@ -4083,101 +4083,47 @@
 nt(%22
-$T i
+I
 ntent 
-= new $T(activity, $L.class)%22, intentClassName, intentClassName, activitySimpleName
+intent = createIntent(activity)%22
 );%0A 
@@ -4420,24 +4420,24 @@
 ;%0A        %7D%0A
-
         retu
@@ -4471,11 +4471,601 @@
 );%0A    %7D
+%0A%0A    private static MethodSpec generateCreateIntentMethod(Name activitySimpleName) %7B%0A        MethodSpec.Builder createIntentBuilder = MethodSpec.methodBuilder(%22createIntent%22).%0A                addModifiers(Modifier.PUBLIC, Modifier.STATIC).%0A                addParameter(activityClassName, %22activity%22).%0A                returns(intentClassName);%0A        createIntentBuilder.addStatement(%22$T intent = new $T(activity, $L.class)%22, intentClassName, intentClassName, activitySimpleName);%0A        createIntentBuilder.addStatement(%22return intent%22);%0A        return createIntentBuilder.build();%0A    %7D
 %0A%7D%0A

Add create intent method