package net.northking.cctp.upperComputer.deviceManager.thread.util;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 线程块<br>
 * 在块内的代码将在指定线程执行，提交执行的代码可取消或等待至结束完毕。
 */
public class ThreadScope implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(ThreadScope.class);
    /**
     * 默认ThreadScope<br>
     * 为跨项目准备的<br>
     * 不要进行depose操作，提交的逻辑不应有死循环，并尽可能的耗时短
     */
    public static final ThreadScope Default = new ThreadScope("default");

    /**
     * 线程块名称
     */
    private final String name;

    /**
     * 线程
     */
    private final Thread thread = new Thread(this);

    /**
     * 工作队列
     */
    private final LinkedBlockingDeque<ThreadJob> jobQueue = new LinkedBlockingDeque<>();

    private final LinkedList<ThreadScope> childScopes = new LinkedList<>();

    private final ThreadScope parentScope;

    /**
     * 是否已经被废弃
     */
    private boolean deposed = false;

    /**
     * 持有的ioJob
     */
    private final ArrayList<IOJob> ioJobs = new ArrayList<>();

    private final Object launchLock = new Object();

    public ThreadScope(String name) {
        this(name, null);
    }

    public ThreadScope(String name, ThreadScope parentScope) {
        this.name = name;
        this.parentScope = parentScope;
        thread.setName("TS-" + name);
        thread.setDaemon(true);
        thread.start();
    }

    public ThreadJob launch(LaunchBlock block) {
        return launch(block, 0L);
    }

    public ThreadJob launch(LaunchBlock block, long delay) {
        if (deposed) return null;
        synchronized (launchLock) {
            ThreadJob job = new ThreadJob(block, System.currentTimeMillis() + delay);
            jobQueue.offer(job);
            return job;
        }
    }

    @Override
    public void run() {
        while (!thread.isInterrupted() && !deposed) {
            ThreadJob job;
            try {
                job = jobQueue.take();
                if (!job.isTimeToRun()) {
                    jobQueue.put(job);
                    if (jobQueue.size() == 1) {
                        Thread.sleep(1L);
                    }
                    continue;
                }
            } catch (InterruptedException e) {
                break;
            }
            if (job.isValid()) {
                try {
                    job.run();
                } catch (Exception e) {
                    log.error("执行job时出错", e);
                }
            }
        }
    }

    public String getName() {
        return name;
    }

    /**
     * 释放线程块<br>
     * 取消所有待执行的任务，正在执行的任务将收到中断异常。
     * 所有子线程块也将被释放
     */
    public void depose() {
        depose(false);
    }

    private void depose(boolean byParent) {
        if (deposed) return;
        deposed = true;

        synchronized (childScopes) {
            childScopes.forEach((childScope) -> childScope.depose(true));
            childScopes.clear();
        }
        if (!byParent && parentScope != null) {
            parentScope.childDepose(this);
        }

        if (!thread.isInterrupted()) {
            thread.interrupt();
        }
        while (!jobQueue.isEmpty()) {
            ThreadJob job;
            try {
                job = jobQueue.take();
            } catch (InterruptedException e) {
                break;
            }
            if (job.isValid()) {
                job.cancel();
            }
        }
        while (!ioJobs.isEmpty()) {
            ArrayList<IOJob> currentIoJobs;
            synchronized (ioJobs) {
                currentIoJobs = new ArrayList<>(ioJobs);
            }
            currentIoJobs.forEach(IOJob::markFinish);
        }
    }

    private void checkNotDeposed() {
        if (deposed) throw new IllegalStateException("net.northking.cctp.util.ThreadScope is deposed.");
    }

    /**
     * 创建子线程块<br>
     * 每个子线程块将运行于各自独立的线程<br>
     * 父级线程块释放时，所有子线程块也将全部释放。
     */
    public ThreadScope createChildScope(String name) {
        checkNotDeposed();
        ThreadScope threadScope = new ThreadScope(name, this);
        synchronized (childScopes) {
            childScopes.add(threadScope);
        }
        return threadScope;
    }

    /**
     * 子线程块释放时，使用此方法从父线程块中释放相关资源和引用
     *
     * @param childScope 进行释放的子线程块
     */
    private void childDepose(ThreadScope childScope) {
        synchronized (childScopes) {
            childScopes.remove(childScope);
        }
    }

    public boolean isDeposed() {
        return deposed;
    }

    /**
     * 异步读取Socket，并在此线程块中处理读取结果<br>
     * @param socket 要读取的Socket
     * @param callback 读取内容回调
     * @return 读取job
     * @throws IOException 开始读取时可能发生的IO异常
     */
    public IOJob readSocketBuffer(Socket socket, IOJob.OnReadBufferCallback<IOJob> callback) throws IOException {
        IOJob ioJob = new IOJob(socket, this, callback);
        synchronized (ioJobs) {
            ioJobs.add(ioJob);
        }
        return ioJob;
    }

    /**
     * 异步读取InputStream（不要用来读取网络Socket），并在此线程块中处理读取结果<br>
     * @param inputStream 要读取的InputStream
     * @param callback 读取内容回调
     * @return 读取job
     * @throws IOException 开始读取时可能发生的IO异常
     */
    public IOJob readSocketBuffer(InputStream inputStream, IOJob.OnReadBufferCallback<IOJob> callback) throws IOException {
        IOJob ioJob = new IOJob(inputStream, this, callback);
        synchronized (ioJobs) {
            ioJobs.add(ioJob);
        }
        return ioJob;
    }

    /**
     * 异步读取Socket文本行
     *
     * @param socket 要读取的Socket
     * @param callback    读取内容回调
     * @return 本次读取的IO工作对象
     */
    public IOJob readSocketLine(Socket socket, IOJob.OnReadLineCallback<IOJob> callback) throws IOException {
        IOJob.BufferToLineReader reader = new IOJob.BufferToLineReader<>(callback);
        return readSocketBuffer(socket, reader);
    }
    /**
     * 异步读取InputStream文本行
     *
     * @param inputStream 要读取的InputStream
     * @param callback    读取内容回调
     * @return 本次读取的IO工作对象
     */
    public IOJob readStreamLine(InputStream inputStream, IOJob.OnReadLineCallback<IOJob> callback) throws IOException {
        IOJob.BufferToLineReader reader = new IOJob.BufferToLineReader<>(callback);
        return readSocketBuffer(inputStream, reader);
    }

    protected void ioJobFinish(IOJob ioJob) {
        synchronized (ioJobs) {
            ioJobs.remove(ioJob);
        }
    }

}
