/*
 * 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.sdm.application.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.api.services.ServiceFactory;
import com.scytl.cryptolib.primitives.service.PrimitivesServiceFactoryHelper;
import com.scytl.products.ov.choices.codes.BallotCastingKeyGenerator;
import com.scytl.products.ov.choices.codes.RandomWithChecksumBallotCastingKeyGenerator;
import com.scytl.products.ov.commons.verify.CSVVerifier;
import com.scytl.products.ov.utils.ChecksumUtils;
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.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.client.RestTemplate;

import com.scytl.products.ov.sdm.application.service.KeyStoreService;
import com.scytl.products.ov.sdm.application.service.KeyStoreServiceImpl;

/**
 * This class allows to configure environment of the SDM and its corresponding configuration properties.
 */
@Configuration
@EnableScheduling
@PropertySources({
    @PropertySource("${upload.config.source}"),
    @PropertySource("${download.config.source}"),
    @PropertySource(value = "${adapter.properties}", ignoreResourceNotFound = true /*not always required*/),
    @PropertySource("classpath:config/services/smartcard.properties")})
@Import({SecureLoggingConfig.class})
public class SDMConfig {

    private static final String SMARTCARDS_PROFILE = "smartcards.profile";

    private static final String PROFILE_E2E = "e2e";

    private static final int CRYPTOLIB_POOL_SIZE = 20;

    private static final int CRYPTOLIB_TIMEOUT_MS = -1;

    @Autowired
    Environment env;

    @Value("${connection.time.out}")
    private String connectionTimeOut;

    @Value("${read.time.out}")
    private String readTimeOut;

    @Value("${write.time.out}")
    private String writeTimeOut;

    @Bean
    public ObjectReader readerForDeserialization() {
        ObjectMapper mapper = mapperForDeserialization();
        return mapper.reader();
    }

    private ObjectMapper mapperForDeserialization() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
        mapper.findAndRegisterModules();
        return mapper;
    }

    /**
     * Class for accessing rest-like services. If we have problems de-serializing json data we may have to create a
     * custom jackson http message converter and added it to the RestTemplate
     *
     * @return spring rest template
     */
    @Bean
    public RestTemplate restClient() {
        return new RestTemplate(new HttpComponentsClientHttpRequestFactory());
    }

    @Bean
    SmartCardConfig getSmartCardConfigDefault() {
        return env.getProperty(SMARTCARDS_PROFILE).equals(PROFILE_E2E) ? SmartCardConfig.FILE
                : SmartCardConfig.SMART_CARD;
    }

    @Bean
    KeyStoreService getKeyStoreService(){
        return new KeyStoreServiceImpl();
    }



    @Bean
    RestConnectionConfig restConnectionConfig(){
        return new RestConnectionConfig(readTimeOut, writeTimeOut, connectionTimeOut);
    }

    @Bean
    public CSVVerifier csvVerifier() {
        return new CSVVerifier();
    }

    @Bean
    public GenericObjectPoolConfig genericObjectPoolConfig() {

        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxTotal(CRYPTOLIB_POOL_SIZE);
        genericObjectPoolConfig.setMaxIdle(CRYPTOLIB_TIMEOUT_MS);

        return genericObjectPoolConfig;
    }

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

    @Bean
    public PrimitivesServiceAPI primitivesServiceAPI(
            final ServiceFactory<PrimitivesServiceAPI> primitivesServiceAPIServiceFactory)
            throws GeneralCryptoLibException {
        return primitivesServiceAPIServiceFactory.create();
    }

    @Bean
    public ChecksumUtils checksumUtils() {
        return new ChecksumUtils();
    }

    @Bean
    public BallotCastingKeyGenerator ballotCastingKeyGenerator(PrimitivesServiceAPI primitivesService,
            ChecksumUtils checksumUtils) {
        return new RandomWithChecksumBallotCastingKeyGenerator(primitivesService, checksumUtils);
    }
}
