import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.FPSAnimator;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;

// --- HDGL Machine State ---
class HDGLMachine {
    double phi = 1.6180339887;
    double pi = 3.1415926535;
    double phiPowerPhi = 2.6180339887;
    double prismState = 105.0;

    double voidVal = 0.0;
    boolean recursionActive = false;
    double currentState = 0.0;
    double t = 0.0;

    public double compute(String expr) {
        // Very simple parser placeholder
        if (expr.equals("phi")) return phi;
        if (expr.equals("pi")) return pi;
        if (expr.equals("phi^phi")) return phiPowerPhi;
        try {
            return Double.parseDouble(expr);
        } catch (Exception e) {
            return 0.0;
        }
    }

    public String toggleRecursion() {
        recursionActive = !recursionActive;
        return "Recursion: " + (recursionActive ? "ON" : "OFF");
    }

    public double step(double dt) {
        t += dt;
        double factor = recursionActive ? phi : pi;
        currentState = Math.sin(t * factor) * (currentState + 1);
        return currentState;
    }
}

// --- OpenGL Renderer ---
class HDGLRenderer implements GLEventListener {
    private HDGLMachine machine;
    private int glyphCount = 100000;
    private float[] positions;
    private Random rng = new Random();

    public HDGLRenderer(HDGLMachine m) {
        this.machine = m;
        generatePositions();
    }

    private void generatePositions() {
        positions = new float[glyphCount * 3];
        for (int i = 0; i < glyphCount; i++) {
            positions[i * 3] = rng.nextFloat() * 200f - 100f;
            positions[i * 3 + 1] = rng.nextFloat() * 200f - 100f;
            positions[i * 3 + 2] = rng.nextFloat() * 200f - 100f;
        }
    }

    public void setGlyphCount(int n) {
        glyphCount = n;
        generatePositions();
    }

    @Override
    public void init(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glClearColor(0.05f, 0.05f, 0.08f, 1f);
        gl.glPointSize(1.0f);
    }

    @Override
    public void display(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // Step the HDGL state
        double state = machine.step(0.02);

        gl.glBegin(GL.GL_POINTS);
        for (int i = 0; i < glyphCount; i++) {
            float x = positions[i * 3];
            float y = positions[i * 3 + 1];
            float z = positions[i * 3 + 2];

            // Apply HDGL displacement
            float dx = (float)(Math.cos(i * 0.01 + machine.t) * state * 0.01);
            float dy = (float)(Math.sin(i * 0.015 + machine.t) * state * 0.01);

            gl.glColor3f(1f, 0.9f, 0.5f);
            gl.glVertex3f(x + dx, y + dy, z);
        }
        gl.glEnd();
    }

    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glViewport(0, 0, w, h);
        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();
        float aspect = (float) w / h;
        gl.glOrtho(-200 * aspect, 200 * aspect, -200, 200, -500, 500);
        gl.glMatrixMode(GL2.GL_MODELVIEW);
    }

    @Override
    public void dispose(GLAutoDrawable drawable) {}
}

// --- App ---
public class HDGLApp {
    public static void main(String[] args) {
        HDGLMachine machine = new HDGLMachine();
        HDGLRenderer renderer = new HDGLRenderer(machine);

        GLProfile profile = GLProfile.get(GLProfile.GL2);
        GLCapabilities caps = new GLCapabilities(profile);
        GLCanvas canvas = new GLCanvas(caps);
        canvas.addGLEventListener(renderer);

        // UI controls
        JTextField exprField = new JTextField(10);
        JButton evalBtn = new JButton("=");
        JButton recBtn = new JButton("Recursion");
        JSpinner glyphSpinner = new JSpinner(new SpinnerNumberModel(100000, 1000, 2000000, 1000));

        evalBtn.addActionListener(e -> {
            double res = machine.compute(exprField.getText());
            exprField.setText("" + res);
        });
        recBtn.addActionListener(e -> exprField.setText(machine.toggleRecursion()));
        glyphSpinner.addChangeListener(e -> renderer.setGlyphCount((Integer) glyphSpinner.getValue()));

        JPanel controls = new JPanel();
        controls.add(new JLabel("Expr:"));
        controls.add(exprField);
        controls.add(evalBtn);
        controls.add(recBtn);
        controls.add(new JLabel("Glyphs:"));
        controls.add(glyphSpinner);

        JFrame frame = new JFrame("HDGL Machine GPU Java");
        frame.setLayout(new BorderLayout());
        frame.add(canvas, BorderLayout.CENTER);
        frame.add(controls, BorderLayout.SOUTH);
        frame.setSize(800, 600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

        FPSAnimator animator = new FPSAnimator(canvas, 60);
        animator.start();
    }
}
