package com.tspoon.androidtoolbelt.compiler.writer;

import android.app.Service;
import android.content.Context;
import android.content.Intent;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import com.tspoon.androidtoolbelt.compiler.ToolbeltProcessor;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import javax.lang.model.element.Modifier;

import timber.log.Timber;

public class ServiceHolderWriter implements SourceWriter {

    private static final String CLASS_NAME = "ServiceHolderImpl";

    private static final String FIELD_SERVICES = "SERVICES";
    private static final String FIELD_CONNECTIONS = "CONNECTIONS";

    @Override
    public String getFileName() {
        return PACKAGE + "." + CLASS_NAME;
    }

    @Override
    public void writeSource(Writer writer) throws IOException {
        TypeSpec typeSpec = TypeSpec.classBuilder(CLASS_NAME)
                .addSuperinterface(ClassName.get(PACKAGE, "ServiceHolder"))
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addField(createFieldServices())
                .addField(createFieldConnections())
                .addMethod(createStartServices())
                .addMethod(createStopServices())
                .build();

        JavaFile javaFile = JavaFile.builder(PACKAGE, typeSpec)
                .addFileComment("Generated by MemoryServiceWriter.java. Do not modify!")
                .build();

        javaFile.writeTo(writer);
    }

    private FieldSpec createFieldServices() {
        TypeName type = ParameterizedTypeName.get(ClassName.get(List.class), ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(ClassName.get(Service.class))));

        CodeBlock.Builder builder = CodeBlock.builder()
                .add("new $T(){{\n", ParameterizedTypeName.get(ClassName.get(ArrayList.class), ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(ClassName.get(Service.class)))));

        for (int i = 1; i <= ToolbeltProcessor.NUMBER_OF_SERVICES; i++) {
            builder.addStatement("add($T.class)", ClassName.bestGuess("MemoryService" + i));
        }
        builder.add("}}");

        return FieldSpec.builder(type, FIELD_SERVICES, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                .initializer(builder.build().toString())
                .build();
    }

    private FieldSpec createFieldConnections() {
        TypeName type = ParameterizedTypeName.get(ClassName.get(List.class), ClassName.get(PACKAGE, "MemoryServiceConnection"));
        return FieldSpec.builder(type, FIELD_CONNECTIONS, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                .initializer("new $T<>()", ArrayList.class)
                .build();
    }


    private MethodSpec createStartServices() {
        ClassName memoryServiceConnection = ClassName.get(PACKAGE, "MemoryServiceConnection");

        CodeBlock code = CodeBlock.builder()
                .beginControlFlow("for($T service: $L)", ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(Service.class)), FIELD_SERVICES)
                .addStatement("$T intent = new $T(context, service)", Intent.class, Intent.class)
                .addStatement("context.startService(intent)")
                .add("\n")
                .addStatement("$T connection = new $T()", memoryServiceConnection, memoryServiceConnection)
                .addStatement("context.bindService(intent, connection, Context.BIND_AUTO_CREATE)")
                .endControlFlow()
                .build();

        return MethodSpec.methodBuilder("startServices")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(ClassName.get(Context.class), "context")
                .addCode(code)
                .build();
    }

    private MethodSpec createStopServices() {
        CodeBlock code = CodeBlock.builder()
                .addStatement("$T.d(\"Stopping Services... \" + $L.size() + \" connections found.\")", Timber.class, FIELD_CONNECTIONS)
                .beginControlFlow("while($L.size() > 0)", FIELD_CONNECTIONS)
                .addStatement("$L.remove(0).stopService()", FIELD_CONNECTIONS)
                .endControlFlow()
                .build();

        return MethodSpec.methodBuilder("stopServices")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addCode(code)
                .build();
    }


}