package cz.senslog.analytics.app;

import cz.senslog.analytics.domain.ListHolder;
import cz.senslog.analytics.domain.ThresholdViolationAlert;
import cz.senslog.analytics.domain.RawObservation;
import io.vertx.core.*;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.MessageCodec;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

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

public class VertxDeployer extends AbstractVerticle {

    private static final Logger logger = LogManager.getLogger(VertxDeployer.class);

    private final AbstractVerticle[] verticles;

    private VertxDeployer(AbstractVerticle[] verticles) {
        this.verticles = verticles;
    }

    public static VertxDeployer deploy(AbstractVerticle... verticles) {
        return new VertxDeployer(verticles);
    }

    public static VertxDeployer deploy(AbstractVerticle verticle, AbstractVerticle... verticles) {
        AbstractVerticle[] res = new AbstractVerticle[verticles.length+1];
        int i = 0;
        for (; i < verticles.length; i++) {
            res[i] = verticles[i];
        }
        res[i] = verticle;
        return deploy(res);
    }

    @Override
    public void start(Promise<Void> startPromise) {
//        vertx.eventBus().registerDefaultCodec(RawObservation.class, new IdentityCodec<>(RawObservation.class));
        vertx.eventBus().registerDefaultCodec(ListHolder.class, new IdentityCodec<>(ListHolder.class));
        vertx.eventBus().registerDefaultCodec(ThresholdViolationAlert.class, new IdentityCodec<>(ThresholdViolationAlert.class));


        List<Future<Void>> futureModules = new ArrayList<>(verticles.length);
        for (AbstractVerticle v : verticles) {
            DeploymentOptions options = new DeploymentOptions()
                    .setThreadingModel(io.vertx.core.ThreadingModel.WORKER)
                    .setConfig(config());
            futureModules.add(deployHelper(vertx, options, v));
        }
        Future.all(futureModules)
                .onSuccess(v -> startPromise.complete())
                .onFailure(startPromise::fail);
    }

    private static Future<Void> deployHelper(Vertx vertx, DeploymentOptions options, AbstractVerticle verticle) {
        logger.info("Deploying module: " + verticle.getClass().getSimpleName());
        final Promise<Void> promise = Promise.promise();
        vertx.deployVerticle(verticle, options, res -> {
            if(res.failed()){
                logger.error("Module '{}' was not deployed.", verticle.getClass().getSimpleName());
                logger.catching(res.cause());
                promise.fail(res.cause());
            } else {
                logger.info("Module '{}' was deployed successfully.", verticle.getClass().getSimpleName());
                promise.complete();
            }
        });
        return promise.future();
    }

    static class IdentityCodec<T> implements MessageCodec<T, T> {
        private final Class<T> aClass;

        public IdentityCodec(Class<T> aClass) {
            this.aClass = aClass;
        }

        @Override
        public void encodeToWire(Buffer buffer, T o) {

        }

        @Override
        public T decodeFromWire(int pos, Buffer buffer) {
            return null;
        }

        @Override
        public T transform(T o) {
            return o;
        }

        @Override
        public String name() {
            return aClass.getName() + "Codec";
        }

        @Override
        public byte systemCodecID() {
            return -1;
        }
    }
}
