/*
 * 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.ov.mixing.core;

import static java.util.Objects.requireNonNull;

import java.util.HashMap;
import java.util.Map;

/**
 * Mixer implementation. Can be used for creating jobs.
 */
public final class MixerImpl implements Mixer {

    private static final String ALGORITHM_WAS_NULL = "The algorithm was null";
    
	private MixerContext mixerContext;

    private Map<String, MixerSPI> mixers;

    private MixerImpl(MixerImplBuilder mixerImplBuilder) {

        mixerContext = mixerImplBuilder.mixerContext;
        mixers = mixerImplBuilder.mixers;

        // Do some validation on the mixerContext once we have an implementation, at least check that its not null

        if (mixers.size() < 1) {
            throw new IllegalStateException("Tried to create a Mixer without adding any MixerSPI implementations");
        }
    }

    @Override
    public boolean isShutdown() {
        return false;
    }

    @Override
    public MixAndProveJob newMixAndProveJob(String algorithm) throws MixingException {

        requireNonNull(algorithm, ALGORITHM_WAS_NULL);
        
        if (mixers.containsKey(algorithm)) {
            return mixers.get(algorithm).newMixAndProveJob(mixerContext);
        }

        throw new AlgorithmNotFoundException(messageWhenAlgorithmNotSupported(algorithm));
    }

    @Override
    public MixJob newMixJob(String algorithm) throws MixingException {

        requireNonNull(algorithm, ALGORITHM_WAS_NULL);

        if (mixers.containsKey(algorithm)) {
            return mixers.get(algorithm).newMixJob(mixerContext);
        }

        throw new AlgorithmNotFoundException(messageWhenAlgorithmNotSupported(algorithm));
    }

    @Override
    public VerifyJob newVerifyJob(String algorithm) throws MixingException {

        requireNonNull(algorithm, ALGORITHM_WAS_NULL);

        if (mixers.containsKey(algorithm)) {
            return mixers.get(algorithm).newVerifyJob(mixerContext);
        }

        throw new AlgorithmNotFoundException(messageWhenAlgorithmNotSupported(algorithm));
    }

    @Override
    public void shutdown() {
    }

    /**
     * Builder of MixerImpl.
     */
    public static final class MixerImplBuilder {

        private MixerContext mixerContext;

        private Map<String, MixerSPI> mixers;

        /**
         * Initialize builder, set the MixerContext.
         * 
         * @param mixerContext
         *            the MixerContext to be set.
         */
        public MixerImplBuilder(MixerContext mixerContext) {

            this.mixers = new HashMap<>();

            this.mixerContext = mixerContext;
        }

        /**
         * Add a MixerSPI.
         * 
         * @param mixerSPI
         *            the MixerSPI to be added.
         */
        public MixerImplBuilder withSPI(MixerSPI mixerSPI) {
            mixers.put(mixerSPI.algorithm(), mixerSPI);
            return this;
        }

        /**
         * Build a MixerImpl.
         * 
         * @return the MixerImpl that was built.
         */
        public MixerImpl build() {
            return new MixerImpl(this);
        }
    }

    private static String messageWhenAlgorithmNotSupported(String algorithm) throws AlgorithmNotFoundException {
        return "The specified algorithm is not supported: " + algorithm;
    }
}
