package org.ts.ddcs.dataStream;

import lombok.extern.slf4j.Slf4j;
import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;

import org.rocksdb.RocksDB;
import org.springframework.data.redis.core.HashOperations;
import org.ts.ddcs.common.enums.AgreementEnum;
import org.ts.ddcs.constant.BusinessConstant;
import org.ts.ddcs.datatemplate.DataTemplateManageHolder;
import org.ts.ddcs.datatemplate.Template;
import org.ts.ddcs.itemdatatemplate.ItemTemplate;
import org.ts.ddcs.main.MainProcessorHolder;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;


/**
 * 数据流主模块
 *
 * @author dylan
 */
@Slf4j
public class DataStreamManager {

    private RocksDB storeCache;

    /**
     * 连接适配器
     */
    private Map<String, DataStreamAdapter> dataStreamAdapterCache = new ConcurrentHashMap<>(2048);

    /**
     * 数据流队列
     */
    private LinkedBlockingQueue<String> streamQueue = new LinkedBlockingQueue<>(2048);
    private LinkedBlockingQueue<String> joinQueue = new LinkedBlockingQueue<>(2048);
    private LinkedBlockingQueue<String> runningCacheQueue = new LinkedBlockingQueue<>(2048);
    private LinkedBlockingQueue<String> removeCacheQueue = new LinkedBlockingQueue<>(2048);

    /**
     * 数据处理器，每个处理器支持对所有规约报文进行解析
     */
    private Map<Integer, DataStreamLineManager> streamLineCache = new ConcurrentHashMap<>(BusinessConstant.STREAM_THREAD_NUM);

    /**
     * 线程池
     **/
    private static ThreadFactory batchThreadFactory = new ThreadFactoryBuilder().setNameFormat("batch-thread-pool-%d").build();
    private static ExecutorService batchThreadPool = new ThreadPoolExecutor(4, 8,
            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), batchThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    /**
     * 任务定时器
     */
    private ScheduledExecutorService scheduleExecutor = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "batch-stream-task-" + r.hashCode()));

    /**
     * 创建数据流处理链
     *
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws ClassNotFoundException
     */
    private void createDataStreamLine(String templates) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        for (int i = 1; i <= BusinessConstant.STREAM_THREAD_NUM; i++) {
            DataStreamLineManager lineManager = new DataStreamLineManager();
            String[] list = templates.split(",");
            if (list.length > 0) {
                for (String templateName : list) {
                    List<Template> templateList = DataTemplateManageHolder.newInstance().dataTemplateManager.cloneTemplateLine(templateName);
                    if (null != templateList && templateList.size() > 0) {
                        for (Template template : templateList) {
                            if (BusinessConstant.TEMPLATE_TYPE_PROCESSOR.equals(template.getType())) {
                                lineManager.addProcessor(templateName, template);
                            } else if (BusinessConstant.TEMPLATE_TYPE_SINK.equals(template.getType())) {
                                lineManager.addSink(templateName, template);
                            }
                        }
                    }
                    List<ItemTemplate> itemTemplateList = DataTemplateManageHolder.newInstance().dataTemplateManager.cloneItemTemplateLine(templateName);
                    if (null != itemTemplateList && itemTemplateList.size() > 0) {
                        for (ItemTemplate itemTemplate : itemTemplateList) {
                            lineManager.addItemTemplates(templateName, itemTemplate);
                        }
                    }
                }
            }
            streamLineCache.put(i, lineManager);
        }
    }

    /**
     * 数据流管理器初始化
     */
    public void init(String templates, RocksDB storeCache) throws IllegalAccessException, ClassNotFoundException, InstantiationException {
        //数据缓存器
        this.storeCache = storeCache;
        //初始化数据流处理器
        this.createDataStreamLine(templates);
        //任务定时器
        this.scheduleExecutor.scheduleWithFixedDelay(() -> {
            try {
                //清除数据
                while (!removeCacheQueue.isEmpty()) {
                    String key = removeCacheQueue.poll();
                    DataStreamAdapter dataStreamAdapter = dataStreamAdapterCache.remove(key);
                    dataStreamAdapter.clear();
                }
                //处理新进入流处理器
                while (!joinQueue.isEmpty()) {
                    String key = joinQueue.poll();
                    streamQueue.offer(Objects.requireNonNull(key));
                }
                //处理上一批次流处理器
                while (!runningCacheQueue.isEmpty()) {
                    String key = runningCacheQueue.poll();
                    streamQueue.offer(Objects.requireNonNull(key));
                }
                //生成处理任务
                boolean idel = false;
                for (int i = 1; i <= BusinessConstant.STREAM_THREAD_NUM; i++) {
                    DataStreamLineManager lineManager = streamLineCache.get(i);
                    if (!lineManager.lock()) {
                        //有空闲处理器
                        idel = true;
                        while (!streamQueue.isEmpty()) {
                            String key = streamQueue.poll();
                            DataStreamAdapter dataStreamAdapter = dataStreamAdapterCache.get(key);
                            if (!dataStreamAdapter.isLock()) {
                                if (dataStreamAdapter.remaining()) {
                                    lineManager.lock(true);
                                    dataStreamAdapter.setLock(true);
                                    //创建数据处理任务
                                    long tm = System.currentTimeMillis();
                                    BatchDataStreamTask task = new BatchDataStreamTask(tm, lineManager, dataStreamAdapter);
                                    FutureTask<Integer> futureTask = new FutureTask<>(task);
                                    batchThreadPool.execute(futureTask);
                                }
                            }
                            if (dataStreamAdapter.isClose() && !dataStreamAdapter.isLock()) {
                                removeCacheQueue.offer(key);
                            } else {
                                runningCacheQueue.offer(key);
                            }
                            if (lineManager.lock()) {
                                break;
                            }
                        }
                    } else {
                        log.info("数据流处理器已被锁住 {}", i);
                    }
                }
                if (!idel) {
                    log.info("数据流处理器性能故障!!");
                    //报警
                }
            } catch (Exception e) {
                log.error(this.getClass().getName(), e);
            }
        }, 0, 100, TimeUnit.MILLISECONDS);
    }

    private class BatchDataStreamTask implements Callable<Integer> {
        /**
         * 任务提交时间
         */
        private long batchTime;
        /**
         * 数据流连接器
         */
        private DataStreamAdapter dataStreamAdapter;
        /**
         * 数据流处理器
         */
        private DataStreamLineManager dataStreamLineManager;

        BatchDataStreamTask(long batchTime, DataStreamLineManager dataStreamLineManager, DataStreamAdapter dataStreamAdapter) {
            this.batchTime = batchTime;
            this.dataStreamLineManager = dataStreamLineManager;
            this.dataStreamAdapter = dataStreamAdapter;
        }

        @Override
        public Integer call() {
            try {
                dataStreamLineManager.stream(dataStreamAdapter);
                String rtuCode = dataStreamAdapter.getRtuCode();
                if (null != rtuCode && rtuCode.length() > 0) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    HashOperations<String, String, String> ops = MainProcessorHolder.newInstance().dataProcess.redisTemplate.opsForHash();
                    ops.put(rtuCode, "last_connect_id", dataStreamAdapter.getAdapterKey());
                    ops.put(rtuCode, "last_connect_time", sdf.format(new Date(batchTime)));
                }
            } catch (Exception e) {
                log.error(this.getClass().getName(), e);
            } finally {
                dataStreamAdapter.setLock(false);
                dataStreamLineManager.lock(false);
            }
            return 0;
        }
    }


    /**
     * 注册连接
     *
     * @param adapter
     */
    public void regConnect(DataStreamAdapter adapter) {
        this.dataStreamAdapterCache.put(adapter.getAdapterKey(), adapter);
        this.joinQueue.offer(adapter.getAdapterKey());

    }

    /**
     * 写报文到测站
     *
     * @param key
     * @param respBlock
     * @return
     */
    public boolean responseEvent(String key, byte[] respBlock) {
        DataStreamAdapter dataStreamAdapter = this.dataStreamAdapterCache.get(key);
        if (null != dataStreamAdapter && !dataStreamAdapter.isClose()) {
            dataStreamAdapter.output(respBlock);
            return true;
        } else {
            log.info("运行错误,链接已经关闭: {}", key);
        }
        return false;
    }
}
