/*
 * 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.channel.cc.orchestrator.commons.config;

import java.io.IOException;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.scytl.products.ov.commons.messaging.Queue;

public final class QueuesConfig {
    public static final Queue[] CHOICE_CODES_KEY_GENERATION_REQ_QUEUES;

    public static final Queue[] CHOICE_CODES_KEY_GENERATION_RES_QUEUES;

    public static final Queue[] GENERATION_CONTRIBUTIONS_REQ_QUEUES;

    public static final Queue[] GENERATION_CONTRIBUTIONS_RES_QUEUES;

    public static final Queue[] VERIFICATION_COMPUTE_CONTRIBUTIONS_REQ_QUEUES;

    public static final Queue[] VERIFICATION_COMPUTE_CONTRIBUTIONS_RES_QUEUES;
    
    public static final Queue[] VERIFICATION_DECRYPTION_CONTRIBUTIONS_REQ_QUEUES;

    public static final Queue[] VERIFICATION_DECRYPTION_CONTRIBUTIONS_RES_QUEUES;

    public static final Queue[] MIX_DEC_KEY_GENERATION_REQ_QUEUES;

    public static final Queue[] MIX_DEC_KEY_GENERATION_RES_QUEUES;

    public static final Queue[] MIX_DEC_COMPUTATION_REQ_QUEUES;

    public static final Queue[] MIX_DEC_COMPUTATION_RES_QUEUES;
    
    private static final String CC_QUEUE_NAMES_PROPERTY = "cc_queue_names";
    
    private static final String GENERATION_COMPUTATION = "cg-comp";
    
    private static final String GENERATION_KEYGENERATION = "cg-keygen";
    
    private static final String VERIFICATION_COMPUTATION = "cv-comp";
    
    private static final String VERIFICATION_DECRYPTION = "cv-dec";
    
    private static final String MIXDEC_COMPUTATION = "md-mixdec";
    
    private static final String MIXDEC_KEYGENERATION = "md-keygen";
    
    private static final String REQUEST_QUEUE = "req";
    
    private static final String RESPONSE_QUEUE = "res";

    static {
        String ccQueueNames = System.getProperty(CC_QUEUE_NAMES_PROPERTY);
        if (ccQueueNames != null) {
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode jsonNode = mapper.readTree(ccQueueNames);
                
                Comparator<Queue> comparator = (q1, q2) -> q1.name().compareTo(q2.name());

                SortedSet<Queue> keyGenerationRequestQueues = new TreeSet<>(comparator);
                SortedSet<Queue> keyGenerationResponseQueues = new TreeSet<>(comparator);
                SortedSet<Queue> generationComputeContributionsRequestQueues = new TreeSet<>(comparator);
                SortedSet<Queue> generationComputeContributionsResponseQueues = new TreeSet<>(comparator);
                SortedSet<Queue> verificationComputeContributionsRequestQueues = new TreeSet<>(comparator);
                SortedSet<Queue> verificationComputeContributionsResponseQueues = new TreeSet<>(comparator);
                SortedSet<Queue> verificationDecryptContributionsRequestQueues = new TreeSet<>(comparator);
                SortedSet<Queue> verificationDecryptContributionsResponseQueues = new TreeSet<>(comparator);
                SortedSet<Queue> mixDecKeyGenerationRequestQueues = new TreeSet<>(comparator);
                SortedSet<Queue> mixDecKeyGenerationResponseQueues = new TreeSet<>(comparator);
                SortedSet<Queue> mixDecComputationRequestQueues = new TreeSet<>(comparator);
                SortedSet<Queue> mixDecComputationResponseQueues = new TreeSet<>(comparator);

                for (JsonNode nodeField : iteratorToIterable(jsonNode.elements())) {
                    for (Map.Entry<String, JsonNode> action : iteratorToIterable(nodeField.fields())) {
                        switch (action.getKey()) {
                        case GENERATION_KEYGENERATION:
                            keyGenerationRequestQueues.add(getRequestQueue(action.getValue()));
                            keyGenerationResponseQueues.add(getResponseQueue(action.getValue()));
                            break;

                        case GENERATION_COMPUTATION:
                            generationComputeContributionsRequestQueues
                                    .add(getRequestQueue(action.getValue()));
                            generationComputeContributionsResponseQueues
                                    .add(getResponseQueue(action.getValue()));
                            break;

                        case VERIFICATION_COMPUTATION:
                            verificationComputeContributionsRequestQueues
                                    .add(getRequestQueue(action.getValue()));
                            verificationComputeContributionsResponseQueues
                                    .add(getResponseQueue(action.getValue()));
                            break;

                        case VERIFICATION_DECRYPTION:
                            verificationDecryptContributionsRequestQueues
                                    .add(getRequestQueue(action.getValue()));
                            verificationDecryptContributionsResponseQueues
                                    .add(getResponseQueue(action.getValue()));
                            break;

                        case MIXDEC_COMPUTATION:
                            mixDecComputationRequestQueues.add(getRequestQueue(action.getValue()));
                            mixDecComputationResponseQueues.add(getResponseQueue(action.getValue()));
                            break;

                        case MIXDEC_KEYGENERATION:
                            mixDecKeyGenerationRequestQueues.add(getRequestQueue(action.getValue()));
                            mixDecKeyGenerationResponseQueues.add(getResponseQueue(action.getValue()));
                            break;

                        default:
                            throw new RuntimeException("Unknown action " + action.getKey() + " found when parsing "
                                    + CC_QUEUE_NAMES_PROPERTY + " property");
                        }
                    }
                }

                CHOICE_CODES_KEY_GENERATION_REQ_QUEUES =
                    keyGenerationRequestQueues.toArray(new Queue[keyGenerationRequestQueues.size()]);

                CHOICE_CODES_KEY_GENERATION_RES_QUEUES =
                    keyGenerationResponseQueues.toArray(new Queue[keyGenerationResponseQueues.size()]);

                GENERATION_CONTRIBUTIONS_REQ_QUEUES = generationComputeContributionsRequestQueues
                        .toArray(new Queue[generationComputeContributionsRequestQueues.size()]);

                GENERATION_CONTRIBUTIONS_RES_QUEUES = generationComputeContributionsResponseQueues
                        .toArray(new Queue[generationComputeContributionsResponseQueues.size()]);

                VERIFICATION_COMPUTE_CONTRIBUTIONS_REQ_QUEUES = verificationComputeContributionsRequestQueues
                        .toArray(new Queue[verificationComputeContributionsRequestQueues.size()]);

                VERIFICATION_COMPUTE_CONTRIBUTIONS_RES_QUEUES = verificationComputeContributionsResponseQueues
                        .toArray(new Queue[verificationComputeContributionsResponseQueues.size()]);

                VERIFICATION_DECRYPTION_CONTRIBUTIONS_REQ_QUEUES = verificationDecryptContributionsRequestQueues
                        .toArray(new Queue[verificationDecryptContributionsRequestQueues.size()]);

                VERIFICATION_DECRYPTION_CONTRIBUTIONS_RES_QUEUES = verificationDecryptContributionsResponseQueues
                        .toArray(new Queue[verificationDecryptContributionsResponseQueues.size()]);

                MIX_DEC_KEY_GENERATION_REQ_QUEUES = mixDecKeyGenerationRequestQueues
                        .toArray(new Queue[mixDecKeyGenerationRequestQueues.size()]);

                MIX_DEC_KEY_GENERATION_RES_QUEUES = mixDecKeyGenerationResponseQueues
                        .toArray(new Queue[mixDecKeyGenerationResponseQueues.size()]);

                MIX_DEC_COMPUTATION_REQ_QUEUES =
                        mixDecComputationRequestQueues.toArray(new Queue[mixDecComputationRequestQueues.size()]);

                MIX_DEC_COMPUTATION_RES_QUEUES =
                    mixDecComputationResponseQueues.toArray(new Queue[mixDecComputationResponseQueues.size()]);

            } catch (NullPointerException | JsonProcessingException e) {
                throw new RuntimeException("Error parsing " + CC_QUEUE_NAMES_PROPERTY + " property", e);
            } catch (IOException e) {
                throw new RuntimeException("Error accessing to " + CC_QUEUE_NAMES_PROPERTY + " property", e);
            }
        } else {
            throw new RuntimeException("System property " + CC_QUEUE_NAMES_PROPERTY + " is missing");
        }
    }
    
    private QueuesConfig() {
    }

    private static Queue getRequestQueue(JsonNode action) {
        return getQueue(action, REQUEST_QUEUE);
    }
    
    private static Queue getResponseQueue(JsonNode action) {
        return getQueue(action, RESPONSE_QUEUE);
    }
    
    private static Queue getQueue(JsonNode action, String type) {
        return new Queue(action.get(type).asText());
    }

    private static <T> Iterable<T> iteratorToIterable(Iterator<T> iterator) {
        return () -> iterator;
    }
}
