/*
 * 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.utils;

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

import com.scytl.products.ov.config.exceptions.ConfigurationEngineException;
import com.scytl.products.ov.config.model.authentication.AuthSecretSpec;

/**
 * Utility class can be used to parse authentication secret specs.
 */
public class AuthSecretsSpecParser {

    private static String AUTH_SECRETS_SPEC_DELIMITER = ";";

    private static String AUTH_SECRETS_SPEC_NAME_LENGTH_DELIMITER = ",";

    /**
     * Parse authentication secret specs from a string that has the following format:
     * <P>
     * [[<secret1 name>,<secret1 length];[<secret2 name>,<secret2 length]]
     * <P>
     * For example:
     * <ul>
     * <li>[[id,12]]</li>
     * <li>[[id,12];[password,12]]</li>
     * <li>[[id,12];[password1,12];[password2,15]]</li>
     * </ul>
     *
     * @param authSecretsSpecAsString
     * @return
     */
    public List<AuthSecretSpec> parse(final String authSecretsSpecAsString) {

        validateInput(authSecretsSpecAsString);

        String firstAndLastCharRemoved = stripFirstAndLastCharacters(authSecretsSpecAsString);

        String[] allSecrets = firstAndLastCharRemoved.split(AUTH_SECRETS_SPEC_DELIMITER);

        if (allSecrets.length < 1) {
            throw new ConfigurationEngineException("Failed to find any secrets in the received string.");
        }

        List<AuthSecretSpec> authSecretsSpecList = new ArrayList<AuthSecretSpec>();

        for (int i = 0; i < allSecrets.length; i++) {

            String secretWithFirstAndLastCharRemoved = stripFirstAndLastCharacters(allSecrets[i]);

            String[] nameAndLengthAsStringArray =
                secretWithFirstAndLastCharRemoved.split(AUTH_SECRETS_SPEC_NAME_LENGTH_DELIMITER);

            if (nameAndLengthAsStringArray.length < 2) {
                throw new ConfigurationEngineException("Failed to parse a name and length from the received string.");
            }

            String name = nameAndLengthAsStringArray[0];

            int length;
            try {
                length = Integer.parseInt(nameAndLengthAsStringArray[1]);
            } catch (NumberFormatException e) {
                throw new ConfigurationEngineException("Could not parse interger from length.", e);
            }

            AuthSecretSpec authSecretSpec = new AuthSecretSpec(name, length);

            authSecretsSpecList.add(authSecretSpec);
        }

        return authSecretsSpecList;
    }

    private String stripFirstAndLastCharacters(final String input) {

        String firstCharRemoved = input.substring(1);
        return firstCharRemoved.substring(0, input.length() - 2);
    }

    private void validateInput(final String authSecretsSpecAsString) {

        if ((authSecretsSpecAsString == null) || (authSecretsSpecAsString.length() == 0)) {

            throw new ConfigurationEngineException("The received authentication secrets spec was not initialized.");
        }

        if (authSecretsSpecAsString.length() < 7) {

            throw new ConfigurationEngineException(
                "The received authentication does not follow the expected format. It is too short to be value.");
        }

        if (!"[[".equals(authSecretsSpecAsString.substring(0, 2))) {

            throw new ConfigurationEngineException(
                "The received authentication does not follow the expected format. Does not being with [[.");
        }

        int len = authSecretsSpecAsString.length();
        if (!"]]".equals(authSecretsSpecAsString.substring(len - 2))) {

            throw new ConfigurationEngineException(
                "The received authentication does not follow the expected format. Does not end with ]].");
        }
    }
}
