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

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.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import com.scytl.cryptolib.api.asymmetric.AsymmetricServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.services.ServiceFactory;
import com.scytl.cryptolib.asymmetric.service.AsymmetricServiceFactoryHelper;
import com.scytl.cryptolib.primitives.service.PrimitivesService;
import com.scytl.products.oscore.logging.api.factory.SecureLoggingFactory;
import com.scytl.products.oscore.logging.api.formatter.MessageFormatter;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.oscore.logging.core.factory.SecureLoggingFactoryLog4j;
import com.scytl.products.oscore.logging.core.formatter.SplunkFormatter;
import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.commons.sign.MetadataFileSigner;
import com.scytl.products.ov.commons.verify.MetadataFileVerifier;
import com.scytl.products.ov.tallying.api.TallyingService;
import com.scytl.products.ov.tallying.api.TallyingServiceImpl;
import com.scytl.products.ov.tallying.commons.HashService;
import com.scytl.products.ov.tallying.commons.MappingStrategy;
import com.scytl.products.ov.tallying.commons.SignatureVerifier;
import com.scytl.products.ov.tallying.commons.io.reader.AuditableVotesReader;
import com.scytl.products.ov.tallying.commons.io.reader.BallotsReader;
import com.scytl.products.ov.tallying.commons.io.reader.FailedVotesReader;
import com.scytl.products.ov.tallying.commons.io.writer.SignResultWriter;
import com.scytl.products.ov.tallying.path.PathResolver;
import com.scytl.products.ov.tallying.path.PrefixPathResolver;
import com.scytl.products.ov.tallying.spring.CommonSpringConfig;
import com.scytl.products.ov.tallying.webapp.mvc.TallyingWebappAdapter;

@Configuration
@PropertySource("classpath:tallying.properties")
@ComponentScan("com.scytl.products.ov.tallying.webapp.mvc")
@Import(CommonSpringConfig.class)
@EnableWebMvc
public class WebAppConfig {

    @Autowired
    Environment env;

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

    @Bean
    public TallyingWebappAdapter tallyingWebappAdapter(BallotsReader ballotsReader,
            AuditableVotesReader auditableVotesReader, FailedVotesReader failedVotesReader,
            MappingStrategy mappingStrategy) {
        return new TallyingWebappAdapter(ballotsReader, auditableVotesReader, failedVotesReader, mappingStrategy);
    }

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

    @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<AsymmetricServiceAPI> asymmetricServiceAPIServiceFactory(
            final GenericObjectPoolConfig genericObjectPoolConfig) {
        return AsymmetricServiceFactoryHelper.getFactoryOfThreadSafeServices(genericObjectPoolConfig);
    }

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

        return asymmetricServiceAPIServiceFactory.create();
    }

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

    @Bean
    public SignResultWriter getSignResultWriter(MetadataFileSigner metadataFileSigner) {
        return new SignResultWriter(metadataFileSigner);
    }

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

    @Bean
    public TallyingService tallyingService(final SignResultWriter signResultWriter, final HashService hashService,
            final SecureLoggingWriter loggingWriter) {
        return new TallyingServiceImpl(signResultWriter, hashService, loggingWriter);
    }

    @Bean
    public SignatureVerifier signatureVerifier(MetadataFileVerifier metadataFileVerifier) {
        return new SignatureVerifier(metadataFileVerifier);
    }

    @Bean
    public PrimitivesService primitivesService() throws GeneralCryptoLibException {
        return new PrimitivesService();
    }

    @Bean
    public HashService hashService() throws GeneralCryptoLibException {
        return new HashService(primitivesService());
    }

    @Bean
    public SecureLoggingFactory loggingFactory() {
        return new SecureLoggingFactoryLog4j(messageFormatter());
    }

    @Bean
    public SecureLoggingWriter loggingWriter() {
        return loggingFactory().getLogger("SecureLogger");
    }

    @Bean
    public MessageFormatter messageFormatter() {
        return new SplunkFormatter("OV", "TALLYING", transactionInfoProvider());
    }

    @Bean
    public TransactionInfoProvider transactionInfoProvider() {
        return new TransactionInfoProvider();
    }
}
