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

import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.ov.validation.path.PathResolver;
import com.scytl.products.ov.validation.path.PrefixPathResolver;
import com.scytl.products.ov.validation.progress.ProgressManager;
import com.scytl.products.ov.validation.progress.ProgressManagerImpl;
import com.scytl.products.ov.validation.services.input.VoteWithProofEntryParser;
import com.scytl.products.ov.validation.services.input.VotesWithProofReader;
import com.scytl.products.ov.validation.utils.JobSelectionStrategy;
import com.scytl.products.ov.validation.utils.PropertiesBasedJobSelectionStrategy;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;

import com.scytl.cryptolib.api.asymmetric.AsymmetricServiceAPI;
import com.scytl.cryptolib.api.certificates.CertificatesServiceAPI;
import com.scytl.cryptolib.api.elgamal.ElGamalServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.api.proofs.ProofsServiceAPI;
import com.scytl.cryptolib.api.services.ServiceFactory;
import com.scytl.cryptolib.asymmetric.service.AsymmetricServiceFactoryHelper;
import com.scytl.cryptolib.certificates.service.CertificatesServiceFactoryHelper;
import com.scytl.cryptolib.elgamal.service.ElGamalServiceFactoryHelper;
import com.scytl.cryptolib.primitives.service.PrimitivesServiceFactoryHelper;
import com.scytl.cryptolib.proofs.service.ProofsServiceFactoryHelper;
import com.scytl.products.ov.commons.sign.MetadataFileSigner;
import com.scytl.products.ov.commons.util.ConfigObjectMapper;
import com.scytl.products.ov.commons.verify.MetadataFileVerifier;
import com.scytl.products.ov.validation.commons.beans.Constants;
import com.scytl.products.ov.validation.decompress.GroupElementDecompressor;
import com.scytl.products.ov.utils.FingerprintGenerator;

@Configuration
@PropertySources({@PropertySource("classpath:properties/springConfig.properties"),
        @PropertySource(value = "classpath:springConfig-override.properties", ignoreResourceNotFound = true) })
public class SpringConfigServices {

    @Value("${user.home}")
    private String prefix;

    @Autowired
    Environment env;

    @Bean
    public GenericObjectPoolConfig genericObjectPoolConfig() {

        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxTotal(Integer.valueOf(env.getProperty("services.cryptolib.pool.size")));
        genericObjectPoolConfig.setMaxIdle(Integer.valueOf(env.getProperty("services.cryptolib.timeout")));

        return genericObjectPoolConfig;
    }

    @Bean
    public ServiceFactory<PrimitivesServiceAPI> primitiveServiceFactory(
            final GenericObjectPoolConfig genericObjectPoolConfig) {
        return PrimitivesServiceFactoryHelper.getFactoryOfThreadSafeServices(genericObjectPoolConfig);
    }

    @Bean
    public PrimitivesServiceAPI PrimitivesService(final ServiceFactory<PrimitivesServiceAPI> primitivesServiceFactory)
            throws GeneralCryptoLibException {

        return primitivesServiceFactory.create();
    }

    @Bean
    public ServiceFactory<ElGamalServiceAPI> elGamalServiceAPIServiceFactory(
            final GenericObjectPoolConfig genericObjectPoolConfig) {
        return ElGamalServiceFactoryHelper.getFactoryOfThreadSafeServices(genericObjectPoolConfig);
    }

    @Bean
    public ElGamalServiceAPI elGamalService(final ServiceFactory<ElGamalServiceAPI> elGamalServiceAPIServiceFactory)
            throws GeneralCryptoLibException {

        return elGamalServiceAPIServiceFactory.create();
    }

    @Bean
    public ServiceFactory<ProofsServiceAPI> proofsServiceFactory(
            final GenericObjectPoolConfig genericObjectPoolConfig) {
        return ProofsServiceFactoryHelper.getFactoryOfThreadSafeServices(genericObjectPoolConfig);
    }

    @Bean
    public ProofsServiceAPI proofsService(final ServiceFactory<ProofsServiceAPI> proofsServiceFactory)
            throws GeneralCryptoLibException {

        return proofsServiceFactory.create();
    }

    @Bean
    public MetadataFileSigner getMetadataFileSigner(AsymmetricServiceAPI asymmetricService) {
        return new MetadataFileSigner(asymmetricService);
    }

    @Bean
    public MetadataFileVerifier getMetadataFileVerifier(AsymmetricServiceAPI asymmetricService) {
        return new MetadataFileVerifier(asymmetricService);
    }

    @Bean
    public PathResolver getPrefixPathResolver() {
        return new PrefixPathResolver(prefix);
    }

    @Bean
    public ServiceFactory<AsymmetricServiceAPI> asymmetricServiceAPIServiceFactory(
            final GenericObjectPoolConfig genericObjectPoolConfig) {
        return AsymmetricServiceFactoryHelper.getFactoryOfThreadSafeServices(genericObjectPoolConfig);
    }

    @Bean
    public AsymmetricServiceAPI asymmetricServiceAPI(
            final ServiceFactory<AsymmetricServiceAPI> asymmetricServiceAPIServiceFactory)
            throws GeneralCryptoLibException {

        return asymmetricServiceAPIServiceFactory.create();
    }

    @Bean
    public ServiceFactory<CertificatesServiceAPI> certificatesServiceAPIServiceFactory(
            final GenericObjectPoolConfig genericObjectPoolConfig) {
        return CertificatesServiceFactoryHelper.getFactoryOfThreadSafeServices(genericObjectPoolConfig);
    }

    @Bean
    public CertificatesServiceAPI certificatesServiceAPI(
            final ServiceFactory<CertificatesServiceAPI> certificatesServiceAPIServiceFactory)
            throws GeneralCryptoLibException {

        return certificatesServiceAPIServiceFactory.create();
    }

    @Bean
    public JobSelectionStrategy jobSelectionStrategy() {
        return new PropertiesBasedJobSelectionStrategy(Constants.JOB_NAME_PREFIX);
    }

    // this is to allow injection of @Value from properties
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer p = new PropertySourcesPlaceholderConfigurer();
        p.setIgnoreResourceNotFound(false);
        return p;
    }

    @Bean
    public GroupElementDecompressor groupElementDecompressor() {
        return new GroupElementDecompressor();
    }

    @Bean
    public ProgressManager progressManager() {
        return new ProgressManagerImpl();
    }

    @Bean
    public FingerprintGenerator fingerprintGenerator(final PrimitivesServiceAPI primitivesService) {
        return new FingerprintGenerator(primitivesService);
    }

    @Bean
    public VotesWithProofReader votesWithProofReader(SecureLoggingWriter secureLoggingWriter){
    	return new VotesWithProofReader(getEntryParser(), secureLoggingWriter);
    }

	private VoteWithProofEntryParser getEntryParser() {
		return new VoteWithProofEntryParser(new ConfigObjectMapper());
	}
}
