package com.eactive.eai.common.logger.async;

import com.eactive.eai.common.logger.HttpAdapterExtraLogVo;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.env.ConfigKeys;
import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import org.jetbrains.annotations.NotNull;

import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class HttpLoggingPoolObject {
    static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
    Disruptor<HttpLoggingEvent> disruptor = null;
    RingBuffer<HttpLoggingEvent> ringBuffer = null;
    int id = 0;
    int queueMax =  (int)Math.pow(2, 10);
    int workerSize = 0;

    public HttpLoggingPoolObject() {

    }

    private WaitStrategy getWaitStrategy(String waitStrategy) {
        WaitStrategy ws = null;
        if(ConfigKeys.LOGGER_ASYNC_WAITSTRATEGY_BLOCK.equals(waitStrategy)) {
            // throughput and low-latency are not as important as CPU resource
            return new BlockingWaitStrategy();
        }
        if(ConfigKeys.LOGGER_ASYNC_WAITSTRATEGY_SLEEP.equals(waitStrategy)) {
            // default : 200, 100ns
//			return new SleepingWaitStrategy();
            // default : 200, 10 ms
            return new SleepingWaitStrategy(200, 10 * 1000000);
        }
        if(ConfigKeys.LOGGER_ASYNC_WAITSTRATEGY_TIME.equals(waitStrategy)) {
            // 100ms
            return new TimeoutBlockingWaitStrategy(100 * 1000, TimeUnit.MICROSECONDS);
        }
        if(ConfigKeys.LOGGER_ASYNC_WAITSTRATEGY_BUSYSPIN.equals(waitStrategy)) {
            // when threads can be bound to specific CPU cores.
            return new BusySpinWaitStrategy();
        }
        if(ConfigKeys.LOGGER_ASYNC_WAITSTRATEGY_YIELD.equals(waitStrategy)) {
            return new YieldingWaitStrategy();
        }
        return ws;
    }

    public HttpLoggingPoolObject(int id, int queueSize, int workerSize, String waitStrategy) {
        this.id = id;
        if(logger.isWarn()) {
            logger.warn(String.format(">> Disruptor-%d queueSize = %d", id, queueSize));
            logger.warn(String.format(">> Disruptor-%d workerSize = %d", id, workerSize));
            logger.warn(String.format(">> Disruptor-%d waitStrategy = %s", id, waitStrategy));
        }
        CustomThreadFactory tFactory = new CustomThreadFactory();

        disruptor = new Disruptor<HttpLoggingEvent>(HttpLoggingEvent.EVENT_FACTORY, queueSize, tFactory,
                ProducerType.SINGLE,
                getWaitStrategy(waitStrategy));
//        BlockingWaitStrategy | SleepingWaitStrategy | YieldingWaitStrategy | BusySpinWaitStrategy
        WorkHandler<HttpLoggingEvent>[] handlers = new WorkHandler[workerSize];
        for(int i=0; i< handlers.length; i++) {
            WorkHandler handler =  new HttpLoggingWorkHandler();
            handlers[i] = handler;
        }
        disruptor.handleEventsWithWorkerPool(handlers);

        try {

            logger.warn(String.format(">> disruptor-%d.start",id));
            disruptor.start();
            ringBuffer = disruptor.getRingBuffer();
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
        finally {
            ;
        }
    }

    public void putMessage(HttpAdapterExtraLogVo httpAdapterExtraLogVo) {
        long seq = ringBuffer.next();
        try {
            if(logger.isDebug()) {
                logger.debug( String.format("disruptor-%d publish : ringBuffer seq = %d uuid=%s",id, seq, httpAdapterExtraLogVo.getGuid()) );
            }
            HttpLoggingEvent httpLoggingEvent = ringBuffer.get(seq);
            httpLoggingEvent.setHttpAdapterExtraLogVo(httpAdapterExtraLogVo);
        }
        finally {
            ringBuffer.publish(seq);
        }
    }

    public void shutdown() {
        if(disruptor == null) return;
        while(true) {
            if(queueMax == disruptor.getRingBuffer().remainingCapacity()) break;
            try {
                logger.warn(String.format("disruptor-%d Sleep 100 ms.", id));
                Thread.sleep(100);
            } catch (InterruptedException e) {
                ;
            }
        }
        if(logger.isWarn()) {
            logger.warn(String.format("<< disruptor-%d shutdown remainingCapacity : %d",id, disruptor.getRingBuffer().remainingCapacity()) );
        }
        if(disruptor !=null) disruptor.shutdown();
    }
}
