package com.unibuy.smartdevice.controller;

import com.unibuy.smartdevice.SchedulerAbstract;
import com.unibuy.smartdevice.devices.PortTools;
import com.unibuy.smartdevice.exception.Logs;
import com.unibuy.smartdevice.tools.HandlerMain;
import com.unibuy.smartdevice.tools.HandlerMainScheduler;
import com.unibuy.smartdevice.tools.OwnScheduler;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public abstract class DevController extends HandlerMainScheduler {
    private Logs logs;
    private List<byte[]> sendBuffers;
    private int readBufferMax;
    private int delayMillisByReadBuffer;

    public void addSendBuffer(byte[] buffer) {
        if (this.isRun()) {
            logs.info("sendBuffers max:" + sendBuffers.size());

            if (sendBuffers.size() > 50) {
                setRun(false);
                return;
            }

            sendBuffers.add(buffer);
        }
    }

    public void getReadBuffer(ReadBufferOnScheduler readBufferOnScheduler, long delayMillis) {
        if (this.isRun()) {
            readBufferOnScheduler.shutdown();
            readBufferOnScheduler.setDevController(this);
            readBufferOnScheduler.start(delayMillis, TimeUnit.MILLISECONDS);
        }
    }

    public void getReadBufferByNow(ReadBufferOnScheduler readBufferOnScheduler) {
        getReadBufferByNow(readBufferOnScheduler, getDelayMillisByReadBuffer());
    }

    public void getReadBufferByNow(ReadBufferOnScheduler readBufferOnScheduler, long delayMillis) {
        clearReadBuffers();
        getLogs().debug("readBuffer size:" + getCountByReadBuffer());
        getReadBuffer(readBufferOnScheduler, delayMillis);
    }

    public byte[] getSendBuffer(int index) {
        return sendBuffers.get(index);
    }

    public byte[] getSendBufferByFirst() {
        return getSendBuffer(0);
    }

    public void removeSendBufferByFirst() {
        if (!sendBuffers.isEmpty()) {
            sendBuffers.remove(0);
        }
    }

    public int getCountBySendBuffer() {
        if (sendBuffers == null) return 0;
        return sendBuffers.size();
    }

    public void clearSendBuffers() {
        sendBuffers.clear();
    }

    public abstract static class ReadBufferOnScheduler extends HandlerMainScheduler {
        private DevController devController;

        public ReadBufferOnScheduler(HandlerMain handlerMain) {
            super(handlerMain);
        }

        public void setDevController(DevController devController) {
            this.devController = devController;
        }

        @Override
        protected String description() {
            if (this.devController != null) {
                return this.devController.setCls().getSimpleName();
            } else {
                return getClass().getSimpleName();
            }
        }

        public abstract void readBufferOnScheduler(DevController devController, HandlerMain handlerMain);

        @Override
        protected void execute(HandlerMain handlerMain) {
            readBufferOnScheduler(this.devController, handlerMain);
        }
    }

    private List<byte[]> readBuffers;

    public void publishReadBuffer(byte[] buffer1, byte[] buffer2) {
        byte[] buffer = PortTools.concatByteArrays(buffer1, buffer2);
        publishReadBuffer(buffer);
    }

    public void publishReadBuffer(byte[] buffer) {
        if (!isReadBufferMax()) {
            readBuffers.add(buffer);
        }
    }

    public boolean isReadBufferMax() {
        return getCountByReadBuffer() >= readBufferMax;
    }

    public List<byte[]> getReadBuffer(int count) {
        List<byte[]> buffers = new ArrayList<>();
        for (int index=0; index<count; index++) {
            byte[] buffer = getReadBuffer();
            if (buffer.length > 0) buffers.add(buffer);
            else break;
        }
        return buffers;
    }

    public List<byte[]> getReadBufferByMax() {
        List<byte[]> buffers = new ArrayList<>(readBuffers);
        readBuffers.clear();
        return buffers;
    }

    public byte[] getReadBuffer() {
        byte[] readBuffer = getCountByReadBuffer() > 0 ? readBuffers.get(0): new byte[0];
        if (getCountByReadBuffer() > 0) readBuffers.remove(0);
        return readBuffer;
    }

    public void limitReadBuffer(int max) {
        if (getCountByReadBuffer()>max) getReadBuffer();
    }

    public int getCountByReadBuffer() {
        if (readBuffers == null) return 0;
        return readBuffers.size();
    }

    public void clearReadBuffers() {
        readBuffers.clear();
    }

    public DevController(int readBufferMax, HandlerMain handlerMain) {
        super(handlerMain);
        this.logs = new Logs(getClass());
        this.sendBuffers = new CopyOnWriteArrayList<>();
        this.readBuffers = new CopyOnWriteArrayList<>();
        this.readBufferMax = readBufferMax;
        this.delayMillisByReadBuffer = 2000;
    }

    public int getDelayMillisByReadBuffer() {
        return delayMillisByReadBuffer;
    }

    public void setDelayMillisByReadBuffer(int delayMillisByReadBuffer) {
        this.delayMillisByReadBuffer = delayMillisByReadBuffer;
    }
}
