package renderer;

import basics.math.algebra.Utils;
import renderer.canvas.ContributionBuffer;
import renderer.canvas.RenderTarget;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Scheduler implements AutoCloseable {
    public static final int THREADS_PER_SIDE = Utils.roundDownToSquareNumber(Runtime.getRuntime().availableProcessors());

    private static final Scheduler INSTANCE = new Scheduler();

    private ExecutorService executorService = Executors.newCachedThreadPool();

    private Scheduler() {
    }

    public static Scheduler getInstance() {
        return INSTANCE;
    }

    public void schedule(ArrayList<TileProcessor> worker) throws InterruptedException {
        executorService.invokeAll(worker);
    }

    public ArrayList<TileProcessor> generateTasks(RenderTarget target, FragmentProgram program) {
        ContributionBuffer buffer = target.getBuffer();

        int totalThreads = THREADS_PER_SIDE * THREADS_PER_SIDE;

        int tileWidth = buffer.getWidth() / THREADS_PER_SIDE;
        int tileHeight = buffer.getHeight() / THREADS_PER_SIDE;

        ArrayList processors = new ArrayList(totalThreads);

        for (int x = 0; x < THREADS_PER_SIDE; x++) {
            int x0 = x * tileWidth;

            for (int y = 0; y < THREADS_PER_SIDE; y++) {
                int y0 = y * tileHeight;

                TileProcessor processor = new TileProcessor(buffer,
                        x0, y0,
                        x0 + tileWidth, y0 + tileHeight,
                        buffer.getWidth(), buffer.getHeight(),
                        program);

                processors.add(processor);
            }
        }

        return processors;
    }

    @Override
    public void close() {
        executorService.close();
    }
}
