/*
 * 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.config.model.authentication.service;

import javax.annotation.PostConstruct;

import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.securerandom.cryptoapi.CryptoAPIRandomString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.primitives.securerandom.configuration.SecureRandomPolicyFromProperties;
import com.scytl.cryptolib.primitives.securerandom.factory.CryptoRandomString;
import com.scytl.cryptolib.primitives.securerandom.factory.SecureRandomFactory;
import com.scytl.products.ov.config.model.authentication.AuthenticationKeyGenerator;
import com.scytl.products.ov.constants.Constants;

/**
 * Service responsible of generating the inner SVK, which has to ensure to have at least the same Entropy as the
 * Authentication Key used for the login
 */
@Service
public class StartVotingKeyService {

    protected static final int DESIRED_BASE = 2;

    /**
     * The default charset for the SVK is 32 so 5 is the number of bits that cover this representation
     *
     * @see Constants#SVK_ALPHABET
     *
     *
     * Ideally the PrimitiveService should be invoked for generating the random Strings, replace the current
     * implementation whenever this services allows to pass an alphabet as parameter to generate the Random
     * String Factory
     *
     */
    protected static final int NUMBER_OF_BITS_PER_SVK_CHARACTER = 5;

    private AuthenticationKeyGenerator authenticationKeyGenerator;

    private int startVotingKeyLength;

    private CryptoRandomString cryptoRandomString;

    public StartVotingKeyService(AuthenticationKeyGenerator authenticationKeyGenerator) {

        this.authenticationKeyGenerator = authenticationKeyGenerator;
        final String path = com.scytl.cryptolib.commons.constants.Constants.CRYPTOLIB_POLICY_PROPERTIES_FILE_PATH;
        SecureRandomFactory secureRandomFactory = new SecureRandomFactory(new SecureRandomPolicyFromProperties(path));
        cryptoRandomString = secureRandomFactory.createStringRandom(Constants.SVK_ALPHABET);
    }

    @PostConstruct
    public void init() {

        startVotingKeyLength = calculateStartVotingKeyLength();
    }

    public String generateStartVotingKey() throws GeneralCryptoLibException {

        return cryptoRandomString.nextRandom(getStartVotingKeyLength());

    }

    /**
     * Calculates the necessary length for the SVK to guarantee the same entropy as the AUTH KEY
     * 
     * @return
     */
    private int calculateStartVotingKeyLength() {

        int result;

        if (authenticationKeyGenerator.getAlphabet().length() == Constants.SVK_ALPHABET.length()) {
            // By default is the same a as the SVK. therefore a different value will depend on the secrets length
            result = authenticationKeyGenerator.getSecretsLength();
        } else {

            int alphabetLength = authenticationKeyGenerator.getAlphabet().length();
            int secretsLength = authenticationKeyGenerator.getSecretsLength();

            Double computation = computeAuthenticationEntropy(alphabetLength, secretsLength);
            result = computation.intValue();

        }

        return result;

    }

    private double computeAuthenticationEntropy(final int alphabetLength, final int secretsLength) {

        double pow = Math.pow(alphabetLength, secretsLength);
        double log = Math.log(pow) / Math.log(DESIRED_BASE);
        double logValueRounded = Math.round(log);

        return Math.round(logValueRounded / NUMBER_OF_BITS_PER_SVK_CHARACTER);

    }

    /**
     * Gets startVotingKeyLength.
     *
     * @return Value of startVotingKeyLength.
     */
    public int getStartVotingKeyLength() {
        return startVotingKeyLength;
    }
}
