package ${basePackage}.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import ${basePackage}.frame.schedule.RunTask;
import ${basePackage}.frame.utils.LogUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 计划调度配置，可以指定环境生效,根据实际情况是否需要开启
 * 开启方法:设置@Profile("dev") 指定对应环境
 */
@Configuration
@Component
public class ScheduleConfig extends ThreadPoolTaskScheduler implements BeanDefinitionRegistryPostProcessor {

    private Map<String, Class<RunTask>> classMap;

    private Map<String, ScheduledFuture<?>> futureMap;

    public ScheduleConfig() {
        classMap = new HashMap<>();
        futureMap = new HashMap<>();
        setPoolSize(4);
        initialize();
    }

    public boolean createOrRepeat(RunTask task) {
        if (futureMap.containsKey(task.taskId())) {
            ScheduledFuture<?> scheduledFuture = futureMap.get(task.taskId());
            scheduledFuture.cancel(false);
        }
        classMap.put(task.taskId(), (Class<RunTask>) task.getClass());
        futureMap.put(task.taskId(), task.schedule(this));
        return true;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        String aPackage = this.getClass().getPackage().getName();
        Pattern compile = Pattern.compile("(.*)\\.config");
        Matcher matcher = compile.matcher(aPackage);
        if (matcher.find()) {
            String basePackage = matcher.group(1);
            registryTask(basePackage, beanDefinitionRegistry);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    private int registryTask(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) {
        ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry);
        classPathBeanDefinitionScanner.resetFilters(false);
        classPathBeanDefinitionScanner.addIncludeFilter(new AssignableTypeFilter(RunTask.class));
        classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() {
            @Override
            public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
                String beanClassName = beanDefinition.getBeanClassName();
                try {
                    Class<?> aClass = Class.forName(beanClassName);
                    Object instance = aClass.newInstance();
                    RunTask task = (RunTask) instance;
                    ScheduleConfig.this.createOrRepeat(task);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                LogUtil.i("registry task " + beanClassName);
                return beanClassName;
            }
        });
        return classPathBeanDefinitionScanner.scan(basePackage);
    }
}
