// SPDX-License-Identifier: CC0-1.0
//
// SPDX-FileContributor: Antonio Niño Díaz, 2024

#include <nds.h>

// Headers autogenerated from the files in BINDIRS in the Makefile
#include "sphere_bin.h"

void setup_fog_properties(int shift, int mass, int depth)
{
    // How much depth difference there is between table entries
    glFogShift(shift);

    // Depth at which the fog starts (and the table starts applying)
    glFogOffset(depth);

    // Generate density table

    int density = 0; // Start table at 0

    for (int i = 0; i < 32; i++)
    {
        glFogDensity(i, density);

        density += mass << 1;

        // Entries are 7 bit, so cap the density to 127
        if (density > 127)
            density = 127;
    }
}

int main(int argc, char *argv[])
{
    // Initialize GL
    glInit();

    consoleDemoInit();

    // Set mode 0, enable BG0 and set it to 3D
    videoSetMode(MODE_0_3D);

    // Set BG to black. Alpha must not be zero for fog to affect the rear plane
    glClearColor(0, 0, 0, 31);
    // The BG and polygons will have the same ID unless a polygon is highlighted
    glClearPolyID(0);
    glClearDepth(0x7FFF);

    // Set the viewport to fullscreen
    glViewport(0, 0, 255, 191);

    // Setup the projection matrix for regular drawing
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, 256.0 / 192.0, 0.1, 20);

    // Use the modelview matrix while drawing
    glMatrixMode(GL_MODELVIEW);

    // Enable fog
    glEnable(GL_FOG);

    // Apply fog to clear plane
    glClearFogEnable(true);

    // Setup color
    glFogColor(20, 20, 20, 31);

    u32 rotateX = 0;
    u32 rotateY = 0;

    int shift = 5;
    int mass = 3;
    int depth = 0x7D00;

    while (1)
    {
        // Handle key input
        scanKeys();
        u16 keys_held = keysHeld();
        u16 keys_down = keysDown();

        // Rotate balls
        if (keys_held & KEY_UP)
            rotateX += 3 << 5;
        if (keys_held & KEY_DOWN)
            rotateX -= 3 << 5;
        if (keys_held & KEY_LEFT)
            rotateY += 3 << 5;
        if (keys_held & KEY_RIGHT)
            rotateY -= 3 << 5;

        // Modify parameters
        if ((keys_down & KEY_X) && (shift < 8))
            shift++;
        if ((keys_down & KEY_B) && (shift > 0))
            shift--;
        if ((keys_down & KEY_A) && (mass < 6))
            mass++;
        if ((keys_down & KEY_Y) && (mass > 0))
            mass--;
        if ((keys_held & KEY_R) && (depth < 0x7FC0))
            depth += 0x20;
        if ((keys_held & KEY_L) && (depth > 0x4000))
            depth -= 0x20;

        consoleClear();

        printf("PAD:   Rotate\n");
        printf("B/X:   Set shift: %d\n", shift);
        printf("A/Y:   Set mass: %d\n", mass);
        printf("L/R:   Set depth: 0x%04X\n", depth);
        printf("\n");
        printf("START: Exit to loader");

        // Setup fog properties
        setup_fog_properties(shift, mass, depth);

        // Save the state of the current matrix(the modelview matrix)
        glLoadIdentity();

        // Setup the camera
        gluLookAt(0.0, 4.0, 4.0,  // Camera position
                  0.0, 0.0, -4.0, // Look at
                  0.0, 1.0, 0.0); // Up

        glRotateXi(rotateX);
        glRotateYi(rotateY);

        // Draw the scene

        glPolyFmt(POLY_ALPHA(31) | POLY_CULL_BACK | POLY_FOG);

        glCallList(sphere_bin);

        glTranslatef32(floattof32(0), floattof32(0), floattof32(-3));
        glCallList(sphere_bin);

        glTranslatef32(floattof32(0), floattof32(0), floattof32(-3));
        glCallList(sphere_bin);

        glTranslatef32(floattof32(0), floattof32(0), floattof32(-3));
        glCallList(sphere_bin);

        // Wait for everything to be drawn before starting on the next frame
        glFlush(0);

        swiWaitForVBlank();

        if (keys_held & KEY_START)
            break;
    }

    return 0;
}
