package renderer;

import optics.light.Color;
import optics.view.PlayerController;
import renderer.canvas.RenderTarget;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.RenderedImage;
import java.io.File;

public class Display {

    private JFrame window;
    private RenderTarget target;
    private Resolution resolution;

    private volatile boolean close;
    private volatile boolean resized;

    private int sampleCount;
    private long frametime;
    private long startTime;

    public Display(Resolution resolution, PlayerController controller) {
        this.resolution = resolution;

        this.window = new JFrame("JPath - CPU - Pathtracer");
        this.window.setMinimumSize(new Dimension(320, 320));
        this.window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        this.window.setSize(resolution.getWidth(), resolution.getHeight());
        this.window.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                super.windowClosing(e);

                close = true;
            }
        });
        this.window.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                super.componentResized(e);

                resized = true;
            }
        });
        this.window.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                super.keyPressed(e);

                controller.motionKeyControl(e);
            }
        });
        this.window.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                super.mouseDragged(e);

                controller.motionMouseControl(e);
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                super.mouseReleased(e);

                controller.mouseReleased();
            }
        });

        this.target = new RenderTarget(resolution);
    }

    public void start(Renderer renderer, PlayerController controller) {
        var program = new FragmentProgram() {
            @Override
            public Color fragment(double u, double v) {

                return renderer.traceCameraRay(u, v);
            }
        };

        this.window.setContentPane(target);
        this.window.setLocationByPlatform(true);
        this.window.setVisible(true);

        try (Scheduler scheduler = Scheduler.getInstance()) {
            var tasks = scheduler.generateTasks(this.target, program);

            startTime = System.currentTimeMillis();

            while (!this.close) {
                this.frametime = System.currentTimeMillis();

                if (System.currentTimeMillis() - startTime > 1000 * 60 * 20) {
                    ImageIO.write(this.target.getImage(), "png", new File("images/" + System.currentTimeMillis() + ".png"));
                    System.exit(0);
                }

                if (resized) {
                    this.resolution.setSize(window.getSize());
                    // Window has been resized, rebuild drawing buffer
                    this.target.buildBuffer(this.resolution);
                    // rebuild rendering tasks
                    tasks = scheduler.generateTasks(this.target, program);
                    this.target.getBuffer().clear();
                    this.sampleCount = 0;
                    this.resized = false;
                }

                if (controller.changed()) {
                    this.target.getBuffer().clear();
                    controller.apply(renderer);
                }

                scheduler.schedule(tasks);
                this.sampleCount ++;

                target.refresh();
                SwingUtilities.invokeLater(target::repaint);

                this.frametime = System.currentTimeMillis() - this.frametime;

                updateOverlay();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void updateOverlay() {
        var overlay = target.getOverlay();
        overlay.setTargetDimension(resolution);
        overlay.setTileDimensions(resolution.getScaledWidth() / Scheduler.THREADS_PER_SIDE, resolution.getScaledHeight() / Scheduler.THREADS_PER_SIDE);
        overlay.setThreads();
        overlay.setFrametime(this.frametime);
        overlay.setSamples(sampleCount);
        overlay.setTime(System.currentTimeMillis() - startTime);
    }
}
