package com.ziyuan;

import com.lmax.disruptor.LiteBlockingWaitStrategy;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.ziyuan.chain.ListenerChainBuilderNew;
import com.ziyuan.channel.Channel;
import com.ziyuan.channel.NormalChannel;
import com.ziyuan.channel.SpecChannel;
import com.ziyuan.events.Electron;
import com.ziyuan.events.ElectronsWrapper;
import com.ziyuan.events.HomelessEle;
import com.ziyuan.events.ListenerCollectWrapper;
import com.ziyuan.exceptions.ElecExceptionHandler;
import com.ziyuan.exceptions.OpNotSupportException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.EventCountCircuitBreaker;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Dispatcher 分发器,用来把任务放到各个channel中
 *
 * @author ziyuan
 * @since 2017-03-08
 */
public final class Dispatcher {

    /**
     * 是否已经开始了
     */
    private AtomicBoolean started = new AtomicBoolean(false);

    /**
     * 通道 特殊通道的键:前缀:tag-class.simpleName
     *
     * TODO 应该会移除这个channelMap 改成两个不同的事件发布者
     */
    private Map<String, Channel> channelMap = new HashMap<>();

    /**
     * 针对有after逻辑的特殊管道
     */
    private static final String SPEC_CHANNEL_PREFIX = "spec_channel";

    /**
     * normal channel的key
     */
    private static final String NORMAL_CHANNEL_KEY = "normal_channel";

    /**
     * config
     */
    private Config conf;

    /**
     * key 事件的包装类  value 该事件的监听器包装类
     */
    private Map<ElectronsWrapper, ListenerCollectWrapper> wrapperMap;

    /**
     * 线程池,由于disruptor不会停止线程池,所以需要我们保留,并且在关闭的时候关掉
     */
    private List<ExecutorService> pools = new ArrayList<>();

    /**
     * 启动分发器
     */
    public synchronized void start() {
        if (started.get()) {
            return;
        }
        started.set(true);
        if (channelMap.size() != 0) {
            for (Channel c : channelMap.values()) {
                c.open();
            }
        }
    }

    /**
     * 停止分发器
     */
    public synchronized void stop() {
        if (!started.get()) {
            return;
        }
        started.set(false);
        if (channelMap.size() != 0) {
            for (Channel c : channelMap.values()) {
                c.close();
            }
            channelMap.clear();
        }
        if (CollectionUtils.isNotEmpty(pools)) {
            for (ExecutorService p : pools) {
                p.shutdown();
            }
            pools.clear();
        }
        wrapperMap.clear();
    }

    public Dispatcher(Map<ElectronsWrapper, ListenerCollectWrapper> wrapperMap, Config config) {
        this.conf = config;
        if (wrapperMap == null || wrapperMap.size() == 0) {
            throw new NullPointerException("WrapperMap can not be null or contains nothing !");
        }
        this.wrapperMap = wrapperMap;
        //初始化pool
        ExecutorService pool = Executors.newFixedThreadPool(conf.getCircuitNum(), new ThreadFactory() {

            final AtomicInteger cursor = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Electrons Thread : thread" + cursor.incrementAndGet());
            }
        });
        this.pools.add(pool);
        initNormalChannel(pool);
        initSpecChannel(wrapperMap.entrySet());
    }

    /**
     * 初始化特殊通道
     *
     * @param entries 事件和监听器集合
     */
    private void initSpecChannel(Set<Map.Entry<ElectronsWrapper, ListenerCollectWrapper>> entries) {
        for (Map.Entry<ElectronsWrapper, ListenerCollectWrapper> entry : entries) {
            ListenerCollectWrapper lisWrapper = entry.getValue();
            if (lisWrapper.isHasAfter()) {
                //走特殊通道,初始化特殊通道的Disruptor
                ElectronsWrapper eleWrapper = entry.getKey();
                initSpecDisruptor(eleWrapper.getSymbol(), lisWrapper.getElectronsListeners());
            } else {
                //走普通通道
                continue;
            }
        }
    }

    /**
     * 根据config初始化特殊通道
     *
     * @param symbol    事件
     * @param listeners 对应的监听器集合
     */
    private void initSpecDisruptor(String symbol, List<ElectronsListener> listeners) {
        ExecutorService specPool = Executors.newFixedThreadPool(conf.getSpecCircuitNum(), new ThreadFactory() {

            final AtomicInteger cursor = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Electrons Thread (from spec channel) : thread" + cursor.incrementAndGet());
            }
        });
        pools.add(specPool);

        Disruptor<ElectronsHolder> disruptor = new Disruptor<>(ElectronsHolder::new, conf.getSpecCircuitLen(), specPool, ProducerType.MULTI, new LiteBlockingWaitStrategy());
        disruptor.handleExceptionsWith(new ElecExceptionHandler("Spec Disruptor {" + symbol + "}"));

        //初始化管道并放入集合中
        SpecChannel specChannel = new SpecChannel(disruptor);
        if (conf.isBreaker()) {
            EventCountCircuitBreaker breaker = new EventCountCircuitBreaker(conf.getErrorNum(), conf.getPerUnit(), conf.getUnit(), conf.getCloseThreshold(), conf.getRest(), conf.getRestUnit());
            specChannel.setBreaker(breaker);
        }

        //构建listener顺序
        ListenerChainBuilderNew.buildChain(specChannel, listeners);

        channelMap.put(SPEC_CHANNEL_PREFIX + symbol, specChannel);
    }

    /**
     * 初始化正常管道,任何情况下都会有
     *
     * @param pool 线程池
     */
    private void initNormalChannel(ExecutorService pool) {
        Disruptor<ElectronsHolder> normalDis = new Disruptor<>(ElectronsHolder::new, conf.getCircuitLen(), pool, ProducerType.MULTI, new LiteBlockingWaitStrategy());
        WorkHandler[] workHandlers = new WorkHandler[conf.getCircuitNum()];
        Arrays.fill(workHandlers, (WorkHandler<ElectronsHolder>) electronsHolder -> electronsHolder.handle());
        normalDis.handleEventsWithWorkerPool(workHandlers);
        normalDis.handleExceptionsWith(new ElecExceptionHandler("Normal Disruptor"));

        //初始化channel
        Channel normalChannel = new NormalChannel(normalDis);
        //配置限流相关
        normalChannel.confLimitRate(conf.isLimitRate(), conf.getPermitsPerSecond(), conf.isWarmup(), conf.getWarmupPeriod(), conf.getWarmPeriodUnit());
        channelMap.put(NORMAL_CHANNEL_KEY, normalChannel);
    }

    /**
     * 根据事件找到一个channel
     *
     * @return 根据wrapper选中的channel
     */
    private Channel selectOne(String tag, Electron electron, boolean hasAfter) {
        if (StringUtils.isBlank(tag) || electron == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        if (hasAfter) {
            builder.append(SPEC_CHANNEL_PREFIX).append(tag).append("-").append(electron.getClass().getSimpleName());
            //特殊管道
        } else {
            builder.append(NORMAL_CHANNEL_KEY);
        }
        return channelMap.get(builder.toString());
    }

    /**
     * 分发
     *
     * @param electron 电子
     * @param tag      tag
     * @param sync     是否同步
     */
    public boolean dispatch(String tag, Electron electron, boolean sync) throws Exception {
        ElectronsWrapper wrapper = new ElectronsWrapper(tag, electron.getClass());
        ListenerCollectWrapper lisWrapper = wrapperMap.get(wrapper);

        //没有找到监听器集合,并且不是HomelessEle,用homelessEle发一次
        if (lisWrapper == null) {
            if (electron.getClass().isAssignableFrom(HomelessEle.class)) {
                //homelessEle还没找到监听器,return false
                return false;
            } else {
                HomelessEle homelessEle = new HomelessEle(electron.getSource());
                return dispatch(tag, homelessEle, true);
            }
        }

        boolean hasAfterLis = lisWrapper.isHasAfter();
        if (sync && hasAfterLis) {
            //如果同步并且还有after逻辑直接抛出异常
            throw new OpNotSupportException();
        }

        //找对应的channel,找不到对应的channel返回false
        Channel channel = selectOne(tag, electron, hasAfterLis);
        if (channel == null) {
            return false;
        }

        ElectronsHolder holder = new ElectronsHolder();
        holder.setElectron(electron);
        holder.setListeners(lisWrapper.getElectronsListeners());
        if (sync) {
            //同步
            return channel.handle(holder);
        } else {
            return channel.publish(holder);
        }
    }
}