package com.its.bis.config;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.annotation.PostConstruct;
import java.util.concurrent.Executor;

@Slf4j
@Getter
@Setter
@EnableAsync
@Configuration
@ConfigurationProperties(prefix = "application.thread-pool")
public class ThreadPoolInitializer extends AsyncConfigurerSupport {

    private int comm = 0;
    private int stat = 0;
    private int work = 0;
    private int dbms = 0;
    private int ping = 0;

    @PostConstruct
    private void init() {
        int MAX_CORE = Math.max(8, Runtime.getRuntime().availableProcessors());
        if (this.comm <= 0) {
            this.comm = MAX_CORE;
        }
        if (this.stat <= 0) {
            this.stat = MAX_CORE;
        }
        if (this.work <= 0) {
            this.work = MAX_CORE;
        }
        if (this.dbms <= 0) {
            this.dbms = MAX_CORE;
        }
        if (this.ping <= 0) {
            this.ping = MAX_CORE;
        }

        log.info("{}", this);
    }

    public ThreadPoolTaskExecutor getDefaultExecutor(int poolSize) {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(poolSize);              // 인스턴스 되면서 기본적으로 띄울 스레드 개수.
        // 아무작업이 없어도 corePoolSize 만큼 스레드가 생성
        threadPoolTaskExecutor.setMaxPoolSize(poolSize*2);   // 풀 최대개수, Queue Capacity 까지 꽉차는 경우 maxPoolSize 만큼 넓혀감
        threadPoolTaskExecutor.setQueueCapacity(1000);          // 스레드 대기큐, Queue Capacity 가 꽉차면 스레드가 추가로 생성됨. Async 처리시 Queue Size
        // (설정하지 않으면 Integer.MAX 이기 때문에 성능에 문제가 발생함)
        return threadPoolTaskExecutor;
    }

    @Bean(name="centerCommExecutor")
    public Executor getCenterCommExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.comm);
        threadPoolTaskExecutor.setThreadNamePrefix("comm-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Bean(name="dbmsDataExecutor")
    public Executor getDbmsDataExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.dbms);
        threadPoolTaskExecutor.setThreadNamePrefix("dbms-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Bean(name="workDataExecutor")
    public Executor getWorkDataExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.work);
        threadPoolTaskExecutor.setThreadNamePrefix("work-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Bean(name="statisticsExecutor")
    public Executor getStatisticsExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.stat);
        threadPoolTaskExecutor.setThreadNamePrefix("stat-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Bean(name="icmpPingExecutor")
    public Executor getIcmpPingExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.ping);
        threadPoolTaskExecutor.setThreadNamePrefix("icmp-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

}
