package com.bboylin.Generator;

import com.bboylin.Annotation.Constants;
import com.bboylin.Annotation.Exception.UrlAlreadyUsedException;
import com.bboylin.Annotation.Exception.UrlNotFoundException;
import com.bboylin.Entity.ActivityInfo;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.processing.Filer;
import javax.lang.model.element.Modifier;

/**
 * Created by lin on 2017/5/13.
 */

public class RouterTableGenerator {
    private Map<String, ClassName> routerTable;
    private static final String TAG = RouterTableGenerator.class.getName();

    public RouterTableGenerator() {
        routerTable = new HashMap<>();
    }

    public void generateMappingTable(Filer filer) throws IOException {
        if (routerTable.isEmpty()) {
            return;
        }
        FieldSpec tableFieldSpec = FieldSpec.builder(Map.class, "routerTable")
                .addModifiers(Modifier.PRIVATE)
                .build();
        MethodSpec initTableMethodSpec = getInitTableMethodSpec();
        MethodSpec queryTableMethodSpec = getqueryTableMethodSpec();
        MethodSpec constructorMethodSpec = getconstructorMethodSpec(tableFieldSpec, initTableMethodSpec);
        MethodSpec addRouterMethodSpec = getAddRouterMethodSpec();
        TypeSpec routerTableClassTypeSpec = getRouterTableClassTypeSpec(tableFieldSpec, initTableMethodSpec, queryTableMethodSpec, constructorMethodSpec, addRouterMethodSpec);
        String packageName = Constants.GENERATED_PACKAGE_NAME;
        JavaFile javaFile = JavaFile.builder(packageName, routerTableClassTypeSpec).build();
        javaFile.writeTo(filer);
    }

    private MethodSpec getAddRouterMethodSpec() {
        return MethodSpec.methodBuilder("addRouter")
                .addModifiers(Modifier.PUBLIC)
                .addParameter(String.class, "key")
                .addParameter(Class.class, "activity")
                .addStatement("routerTable.put(key,activity)")
                .returns(void.class)
                .build();
    }

    private TypeSpec getRouterTableClassTypeSpec(FieldSpec tableFieldSpec, MethodSpec initTableMethodSpec, MethodSpec queryTableMethodSpec, MethodSpec constructorMethodSpec, MethodSpec addRouterMethodSpec) {
        return TypeSpec.classBuilder(Constants.ROUTER_TABLE_CLASS_NAME)
                .addModifiers(Modifier.PUBLIC)
                .addField(tableFieldSpec)
                .addMethod(initTableMethodSpec)
                .addMethod(queryTableMethodSpec)
                .addMethod(constructorMethodSpec)
                .addMethod(addRouterMethodSpec)
                .build();
    }

    private MethodSpec getconstructorMethodSpec(FieldSpec tableFieldSpec, MethodSpec initTableMethodSpec) {
        ClassName activity = ClassName.get("android.app", "Activity");
        return MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addStatement("$N = new $T<String,Class<? extends $T>>()", tableFieldSpec, HashMap.class, activity)
                .addStatement("$N()", initTableMethodSpec)
                .build();
    }

    private MethodSpec getqueryTableMethodSpec() {
        return MethodSpec.methodBuilder("queryRouterTable")
                .addModifiers(Modifier.PUBLIC)
                .addParameter(String.class, "url")
                .addStatement("Class<? extends Activity> target = (Class<? extends Activity>) routerTable.get(url)")
                .beginControlFlow("if (target == null)")
                .addStatement("throw new $T(url)", UrlNotFoundException.class)
                .endControlFlow()
                .addStatement("return target")
                .returns(Class.class)
                .build();
    }

    private MethodSpec getInitTableMethodSpec() {
        MethodSpec.Builder builder = MethodSpec.methodBuilder("init")
                .addModifiers(Modifier.PRIVATE)
                .returns(void.class);
        for (Map.Entry<String, ClassName> entry : routerTable.entrySet()) {
            builder.addStatement("routerTable.put($S,$T.class)", entry.getKey(), entry.getValue());
        }
        return builder.build();
    }

    public void clearMappingTable() {
        routerTable.clear();
    }

    /*public static class SingleTonHolder {
        public static final RouterTableGenerator INSTANCE = new RouterTableGenerator();
    }

    public static RouterTableGenerator getInstance() {
        return SingleTonHolder.INSTANCE;
    }*/

    public void put(ActivityInfo activityInfo) {
        String url = activityInfo.getUrl();
        if (routerTable.containsKey(url)) {
            throw new UrlAlreadyUsedException(url, activityInfo.getClassName().toString());
        }
        routerTable.put(url, activityInfo.getClassName());
    }

    public Class queryMappingTable(String url) {
        if (!routerTable.containsKey(url)) {
            throw new UrlNotFoundException(url);
        }
        return routerTable.get(url).getClass();
    }
}