<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">

    <title>DLA dashboard</title>

    <!-- Bootstrap -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

    <!-- Page styling -->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/style.css') }}">

    <!-- THREE.js only (local) -->
    <script src="{{ url_for('static',filename='js/three.js') }}"></script>

    <!-- THREE.js only -->
    <!-- <script src="//unpkg.com/three"></script> -->

    <!-- THREE.js and THREE.SpriteText -->
    <!-- <script src="//unpkg.com/three"></script> -->
    <!-- <script src="//unpkg.com/three-spritetext"></script> -->

    <!-- Drawing the coordinate system in HTML -->
    <script src="{{ url_for('static', filename='js/coordinateSystemHTML.js') }}"></script>

    <!-- Custom animation code -->
    <script src="{{ url_for('static', filename='js/animations.js') }}"></script>

    <!-- Custom DLA utils -->
    <script src="{{ url_for('static', filename='js/dla_utils.js') }}"></script>
</head>
<body>
<div class="container-fluid">
    <div class="jumbotron text-center">
        <h1>DLA dashboard</h1>
    </div>
    <hr>
    <div class="container-fluid">
        <div class="row">
            <div class="col-sm-8">
                <h3>Animation</h3>
                <p>
                    The animation shows how DLA updates particles from the prior towards the posterior.
                    Blue points represent DLA particles.
                    Red points represent samples from the normalizing flow.
                </p>

                <div id="canvasContainer" style="height: 70vh">
                    <canvas id="topLeftCanvas" style="background:transparent; width: 10%; height: 90%"></canvas>
                    <canvas id="glCanvas" style="background:transparent; width: 90%; height: 90%"></canvas>
                    <canvas id="bottomLeftCanvas" style="background:transparent; width: 10%; height: 10%"></canvas>
                    <canvas id="bottomRightCanvas" style="background:transparent; width: 90%; height: 10%"></canvas>
                </div>

                <!-- Animation controls (play/pause/stop) -->
                <div class="container-fluid card card-body mb-1">
                    <h6>DLA step <span id="DLAStepSpan"></span></h6>
                    <div style="display: flex; justify-content: space-between" class="mb-3">
                        <span style="padding-right: 1%">1</span>
                        <input type="range" class="form-range" id="stepRange" min="0" max="1" step="1" value="0"
                               disabled>
                        <span id="DLAMaxStep" style="padding-left: 1%"></span>
                    </div>
                    <div class="btn-group">
                        <button type="button" class="btn btn-default btn-outline-secondary" id="buttonPlay">
                            Play
                        </button>
                        <button type="button" class="btn btn-default btn-outline-secondary" id="buttonPause">
                            Pause
                        </button>
                        <button type="button" class="btn btn-default btn-outline-secondary" id="buttonStop">
                            Stop
                        </button>
                    </div>
                </div>

            </div>
            <div class="container col-sm-4">
                <div>
                    <form action="{{ url_for('reload') }}" method="POST">
                        <h3>Experiments
                            <button
                                    type="submit"
                                    class="btn btn-default btn-outline-secondary"
                                    id="buttonReload"
                                    style="float: right">Reload
                            </button>
                        </h3>
                    </form>
                </div>

                <div class="container-fluid card card-body mb-1">
                    <h6>Experiment selection</h6>
                    <form action="{{ url_for('home') }}" method="POST">
                        <div class="overflow-auto" style="max-height: 30vh; display:flex; flex-direction:column-reverse;">
                            <!-- The last twoo style elements are so we start with the scrollbar at the bottom when loading up the page -->
                            <!-- https://stackoverflow.com/questions/18614301/keep-overflow-div-scrolled-to-bottom-unless-user-scrolls-up -->
                            {% for experiment_path in experiment_paths %}
                            <div class="form-check">
                                <input
                                        class="form-check-input"
                                        type="radio"
                                        name="experimentRadio"
                                        value="{{ loop.index }}"
                                        onchange="this.form.submit();"
                                        id="experimentRadio{{ loop.index }}" {% if loop.index==
                                        meta_info.default_experiment_id_flask %} checked {% endif %}>
                                <label class="form-check-label" for="experimentRadio{{ loop.index }}">
                                    {{ experiment_path }}
                                </label>
                            </div>
                            {% endfor %}
                        </div>
                    </form>
                </div>

                <div class="container-fluid card card-body">
                    <h6>Experiment information</h6>
                    <ul>
                        <li>Number of dimensions: {{ experiment_info.nDimensions | safe }}</li>
                        <li>Number of DLA steps: {{ experiment_info.nSteps | safe }}</li>
                        <li>Number of DLA particles: {{ experiment_info.nDLAParticles | safe }}</li>
                        <li>Number of flow samples: {{ experiment_info.nFlowSamples | safe }}</li>
                    </ul>
                </div>
                <hr>

                <h3>Settings and controls</h3>
                <div id="settings" class="form">
                    <!-- Dim0 selector -->
                    <div class="input-group mb-1">
                        <div class="input-group-prepend">
                            <span class="input-group-text">First dimension (x)</span>
                        </div>
                        <input type="number" class="form-control text-end" id="dim0Selector" min="0" max="1"
                               value="0">
                    </div>

                    <!-- Dim1 selector -->
                    <div class="input-group mb-1">
                        <div class="input-group-prepend">
                            <span class="input-group-text">Second dimension (y)</span>
                        </div>
                        <input type="number" class="form-control text-end" id="dim1Selector" min="0" max="1"
                               value="1">
                    </div>

                    <!-- FPS selector -->
                    <div class="container-fluid card card-body mb-1">
                        <h6>FPS (animation speed)</h6>
                        <div style="display: flex; justify-content: space-between">
                            <span style="padding-right: 1%">0</span>
                            <input type="range" class="form-range" id="fpsRange" min="1" max="300" step="1" value="300">
                            <span style="padding-left: 1%">300</span>
                        </div>
                    </div>

                    <!-- Posterior display toggle -->
                    <div class="container-fluid card card-body mb-1">
                        <h6>Show data</h6>
                        <div class="form-check">
                            <input class="form-check-input" type="checkbox" value="" id="posteriorCheckbox">
                            <label class="form-check-label" for="posteriorCheckbox">Likelihood samples</label>
                        </div>
                        <div class="form-check">
                            <input class="form-check-input" type="checkbox" value="" id="flowCheckbox" checked>
                            <label class="form-check-label" for="flowCheckbox">Flow samples</label>
                        </div>
                    </div>

                </div>
            </div>
        </div>
    </div>
    <script>
        // Create the renderer
        const renderer = new THREE.WebGLRenderer({canvas: document.getElementById("glCanvas")});

        // Rendering parameters
        let limitScaling = 1.0;
        let particleSize = 25;

        // Coordinate system parameters
        let nTicks = 10;
        let tickHeight = 25;
        let tickWidth = 25;

        // Things that stay constant during initialization
        let dim0 = parseInt(document.getElementById('dim0Selector').value);
        let dim1 = parseInt(document.getElementById('dim1Selector').value);

        // Sprites
        const particleSprite = new THREE.TextureLoader().load('{{ url_for("static", filename="textures/sprites/disc.png") }}');

        // Process data
        let data = {{data | safe}}  // This is not an error, just flask template working with a json object
        let manager = new DLADatasetManager(data, particleSprite, particleSize);

        let experimentID = parseInt("{{ meta_info.experiment_id | safe }}");

        let nSteps = data.dla.length;
        document.getElementById('stepRange').setAttribute('max', nSteps.toString());
        if (nSteps > 0 && data.dla[0].length > 0) {
            let nDimensions = data.dla[0].length;
            document.getElementById("dim0Selector").setAttribute("max", (nDimensions - 1).toString());
            document.getElementById("dim1Selector").setAttribute("max", (nDimensions - 1).toString());
        }
        document.getElementById("DLAMaxStep").textContent = nSteps.toString();  // -1 does not matter, we don't use this

        // Set up animation controls
        class AnimationControls {
            constructor(nSteps) {
                this.state = 0;  // 0 - paused, 1 - playing, 2 - stopped
                this.previousStep = -1;
                this.activeStep = 0;
                this.nSteps = nSteps;
            }

            stepChanged() {
                return this.previousStep !== this.activeStep;
            }

            setStep(step) {
                this.previousStep = this.activeStep;
                this.activeStep = step;

                // Out of bounds check
                if (this.activeStep < 0) {
                    this.activeStep = 0;
                }
                if (this.activeStep >= nSteps) {
                    this.activeStep = nSteps - 1;
                }
                document.getElementById("stepRange").value = this.activeStep.toString();
            }

            incrementStep() {
                this.setStep((this.activeStep + 1) % this.nSteps);
            }

            play() {
                this.state = 1;
                document.getElementById("stepRange").disabled = true;
            }

            pause() {
                this.state = 0;
                this.setStep(this.activeStep);  // Sets the previous step to the active step too => step has not changed
                document.getElementById("stepRange").disabled = false;
            }

            stop() {
                this.setStep(0);
                this.pause();
                this.state = 2;
            }

            isPlaying() {
                return this.state === 1;
            }

            isPaused() {
                return this.state === 0;
            }

            isStopped() {
                return this.state === 2;
            }
        }

        let animationControls = new AnimationControls(nSteps);
        document.getElementById("buttonPlay").onclick = function () {
            animationControls.play();
        };
        document.getElementById("buttonPause").onclick = function () {
            animationControls.pause();
        };
        document.getElementById("buttonStop").onclick = function () {
            animationControls.stop();
        };
        document.getElementById("stepRange").oninput = function (e) {
            animationControls.setStep(parseInt(e.target.value));
        };
        animationControls.play();

        // Set up the coordinate system
        let coordinateSystem = new CoordinateSystem(
            document.getElementById("bottomRightCanvas"), manager.limits().min[dim0], manager.limits().max[dim0],
            document.getElementById("topLeftCanvas"), manager.limits().min[dim1], manager.limits().max[dim1],
            nTicks, tickHeight, tickWidth
        );
        coordinateSystem.draw(dim0, dim1, manager.limits());

        // Redraw the coordinate system each time the window is resized
        document.body.onresize = function () {
            coordinateSystem.draw(dim0, dim1, manager.limits());
        };

        // We are displaying 2D data with third component equal to 0, so having [-1, 1] as z limits is fine.
        const fixedCamera = new THREE.OrthographicCamera(-100, 100, -100, 100, -1, 1);
        let cameraController = new CameraController(fixedCamera)
        cameraController.setFixedCameraParameters(dim0, dim1, manager.limits(), limitScaling);

        function updateDimensions() {
            dim0 = parseInt(document.getElementById('dim0Selector').value);
            dim1 = parseInt(document.getElementById('dim1Selector').value);

            manager.setDimensions(dim0, dim1)
            cameraController.setFixedCameraParameters(dim0, dim1, manager.limits(), limitScaling);

            // Redraw the coordinate system
            coordinateSystem.draw(dim0, dim1, manager.limits());
        }

        document.getElementById('dim0Selector').addEventListener('input', updateDimensions)
        document.getElementById('dim1Selector').addEventListener('input', updateDimensions)

        // Setting up the scene
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0xffffff);

        // Add objects to the scene
        scene.add(manager)

        function resizeCanvasToDisplaySize(camera) {
            const canvas = renderer.domElement;
            // look up the size the canvas is being displayed
            const width = canvas.clientWidth;
            const height = canvas.clientHeight;

            // adjust displayBuffer size to match
            if (canvas.width !== width || canvas.height !== height) {
                // you must pass false here or three.js sadly fights the browser
                renderer.setSize(width, height, false);
                camera.aspect = width / height;
                camera.updateProjectionMatrix();

                // update any render target sizes here
            }
        }

        // FPS limiter variables
        let previousDelta = 0;
        let fpsTolerance = 0.001;

        function animate(currentDelta) {
            requestAnimationFrame(animate);

            resizeCanvasToDisplaySize(cameraController.getActiveCamera());

            // FPS lock related
            let delta = currentDelta - previousDelta;

            // FPS lock related
            let fpsLimit = parseInt(document.getElementById("fpsRange").value);
            if (fpsLimit && delta < (1000 / fpsLimit) - fpsTolerance) {
                return;
            }

            document.getElementById("DLAStepSpan").textContent = animationControls.activeStep.toString();

            if (!animationControls.isPlaying()) {
                // Either stopped or paused
                if (animationControls.isPaused() && !animationControls.stepChanged()) {
                    return;
                } else if (animationControls.isStopped()) {
                    animationControls.pause();  // Transition to paused state
                }
            } else {
                animationControls.incrementStep();
            }

            for (let i = 0; i < manager.dlaDatasets.length; i++) {
                manager.dlaDatasets[i].visible = (i === animationControls.activeStep);
            }
            for (let i = 0; i < manager.flowDatasets.length; i++) {
                manager.flowDatasets[i].visible = (i === animationControls.activeStep) && document.getElementById("flowCheckbox").checked;
            }
            if (manager.hasLikelihoodData()) {
                manager.likelihoodDataset.visible = document.getElementById("posteriorCheckbox").checked
            }

            renderer.render(scene, cameraController.getActiveCamera());

            previousDelta = currentDelta;  // FPS lock related
        }

        animate();
    </script>
</div>
</body>
</html>