package me.km.scheduler;

import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import me.hammerle.snuviscript.code.ISnuviLogger;
import me.hammerle.snuviscript.code.ISnuviScheduler;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

@OnlyIn(Dist.DEDICATED_SERVER)
public class SnuviScheduler implements ISnuviScheduler {
    private final ISnuviLogger logger;
    private final ConcurrentLinkedQueue<SnuviTask> tasks = new ConcurrentLinkedQueue<>();
    private final AsyncWorker worker = new AsyncWorker();
    private boolean noAdding = false;

    public SnuviScheduler(ISnuviLogger logger) {
        this.logger = logger;
        worker.start();
    }

    public void stop() {
        noAdding = true;
        long endTime = System.nanoTime() + 60_000_000_000l;
        logger.print("Waiting until end of synchronized tasks ...");
        while(!tasks.isEmpty() && System.nanoTime() < endTime) {
            tick(1000);
        }
        logger.print("Waiting until end of asynchronous tasks ...");
        while(worker.hasWork() && System.nanoTime() < endTime) {
            try {
                Thread.sleep(250);
            } catch(InterruptedException ex) {
            }
        }
        worker.stop();
        logger.print("the scheduler is now stopped");
    }

    public void scheduleRepeatingTask(Runnable r, long ticks, long rtimer) {
        if(noAdding) {
            return;
        }
        tasks.add(new SnuviTask(r, ticks, rtimer));
    }

    @Override
    public void scheduleTask(Runnable r, long delay) {
        scheduleRepeatingTask(r, delay, 0);
    }

    public void tick() {
        tick(1);
    }

    private void tick(int ticks) {
        Iterator<SnuviTask> iter = tasks.iterator();
        long endTime = System.currentTimeMillis() + 25;
        while(iter.hasNext()) {
            if(System.currentTimeMillis() > endTime) {
                logger.print("scheduler has run longer than 25 ms");
                break;
            }
            SnuviTask task = iter.next();
            try {
                if(task.tick(ticks, noAdding)) {
                    iter.remove();
                }
            } catch(Exception ex) {
                logger.print("scheduler exception - task will be removed", ex, null, null, null, null);
                iter.remove();
            }
        }
    }

    public void scheduleAsyncTask(Runnable r) {
        if(noAdding) {
            return;
        }
        worker.add(r);
    }

    public boolean hasAsyncWork() {
        return worker.hasWork();
    }
}
