/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.mixnet.mvc;

import java.time.Duration;
import java.time.Instant;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.mixnet.api.MixingServiceAPI;
import com.scytl.products.ov.mixnet.commons.configuration.ApplicationConfig;
import com.scytl.products.ov.mixnet.commons.configuration.locations.LocationsProvider;
import com.scytl.products.ov.mixnet.commons.constants.Constants;
import com.scytl.products.ov.mixnet.commons.exceptions.MixingException;

public class MixingController {

    private static final Logger LOG = LoggerFactory.getLogger("std");

    private final MixingServiceAPI mixingService;

    private TransactionInfoProvider transactionInfoProvider;

    public MixingController(final MixingServiceAPI mixingServiceAPI, final TransactionInfoProvider transactionInfoProvider) {
        this.mixingService = mixingServiceAPI;
        this.transactionInfoProvider = transactionInfoProvider;
    }

    /**
     *
     * @param applicationConfig
     * @throws MixingException
     */
    public void warmUp(final ApplicationConfig applicationConfig) throws MixingException {

        if (applicationConfig == null) {
            throw new IllegalArgumentException("The received application config was null");
        }

        validateRequiredConcurrencyParameters(applicationConfig);

        if (!applicationConfig.hasMixerConfig() || !applicationConfig.getMixer().isMix()) {
            LOG.info("Application configured to not perform mixing");
            return;
        }

        //make sure we don't get a nullpointer in secure log
        transactionInfoProvider.generate(Constants.LOGGING_TENANT, Constants.LOGGING_LOCALHOST,
            Constants.LOGGING_LOCALHOST);

        Instant start = Instant.now();

        mixingService.warmUp(applicationConfig);

        Instant end = Instant.now();
        LOG.info("Warm up duration was: " + Duration.between(start, end));
    }


    /**
     * @param applicationConfig
     */
    public void mix(final ApplicationConfig applicationConfig, final LocationsProvider locationsProvider)
            throws MixingException {

        if (applicationConfig == null) {
            throw new IllegalArgumentException("The received application config was null");
        }

        validateRequiredConcurrencyParameters(applicationConfig);

        if (!applicationConfig.hasMixerConfig() || !applicationConfig.getMixer().isMix()) {
            LOG.info("Application configured to not perform mixing");
            return;
        }

        //make sure we don't get a nullpointer in secure log
        transactionInfoProvider.generate(Constants.LOGGING_TENANT, Constants.LOGGING_LOCALHOST,
            Constants.LOGGING_LOCALHOST);


        Instant start = Instant.now();

        mixingService.mix(applicationConfig, locationsProvider);

        Instant end = Instant.now();

        LOG.info("Mixing for all batches duration was: " + Duration.between(start, end));
    }

    private void validateRequiredConcurrencyParameters(final ApplicationConfig applicationConfig) {
        if (applicationConfig.getConcurrencyLevel() <= 0) {
            throw new IllegalArgumentException("Invalid argument: 'concurrencyLevel' must be positive");
        }
        if (applicationConfig.getMinimumBatchSize() <= 0) {
            throw new IllegalArgumentException("Invalid argument: 'minimumBatchSize' must be positive");
        }
        if (applicationConfig.getNumberOfParallelBatches()  <= 0) {
            throw new IllegalArgumentException("Invalid argument: 'numberOfParallelBatches' must be positive");
        }
        if (applicationConfig.getNumOptions()  <= 0) {
            throw new IllegalArgumentException("Invalid argument: 'numOptions' must be positive");
        }

    }
}
