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

// This example shows the different overflow modes for texture coordinates when
// drawing polygons. When the texture coordinates passed to the GPU are outside
// of bounds of the active texture the GPU can:
//
// - Clamp them to the texture size.
// - Wrap the coordinates and repeat the texture.
// - Wrap the coordinates and flip the texture.

#include <stdio.h>

#include <nds.h>

// Header autogenerated for each PNG file inside GFXDIRS in the Makefile
#include "neon.h"

__attribute__((noreturn)) void wait_forever(void)
{
    printf("Press START to exit.");

    while (1)
    {
        swiWaitForVBlank();

        scanKeys();
        if (keysHeld() & KEY_START)
            exit(1);
    }
}

int main(int argc, char **argv)
{
    int textureID[3];

    // Setup sub screen for the text console
    consoleDemoInit();

    videoSetMode(MODE_0_3D);

    glInit();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_ANTIALIAS);

    // The background must be fully opaque and have a unique polygon ID
    // (different from the polygons that are going to be drawn) so that
    // alpha blending works.
    glClearColor(10, 10, 10, 31);
    glClearPolyID(63);

    glClearDepth(0x7FFF);

    glViewport(0, 0, 255, 191);

    // Setup some VRAM as memory for textures
    vramSetBankA(VRAM_A_TEXTURE);

    // Generate IDs for two textures
    glGenTextures(3, &textureID[0]);

    // Load texture once with the default settings (no wrap or flip)
    glBindTexture(0, textureID[0]);
    if (glTexImageNtr2D(GL_RGBA, 32, 32, TEXGEN_TEXCOORD, neonBitmap, NULL) == 0)
    {
        printf("Failed to load texture 1\n");
        wait_forever();
    }

    // Load the same texture but this time enable wrap
    glBindTexture(0, textureID[1]);
    if (glTexImageNtr2D(GL_RGBA, 32, 32,
                        TEXGEN_TEXCOORD | GL_TEXTURE_WRAP_S | GL_TEXTURE_WRAP_T,
                        neonBitmap, NULL) == 0)
    {
        printf("Failed to load texture 2\n");
        wait_forever();
    }

    // Load the same texture but this time enable wrap and flip
    glBindTexture(0, textureID[2]);
    if (glTexImageNtr2D(GL_RGBA, 32, 32,
                        TEXGEN_TEXCOORD | GL_TEXTURE_WRAP_S | GL_TEXTURE_WRAP_T |
                        GL_TEXTURE_FLIP_S | GL_TEXTURE_FLIP_T,
                        neonBitmap, NULL) == 0)
    {
        printf("Failed to load texture 3\n");
        wait_forever();
    }

    // Setup matrices
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(70, 256.0 / 192.0, 0.1, 40);

    gluLookAt(0.0, 0.0, 4.0,  // Position
              0.0, 0.0, 0.0,  // Look at
              0.0, 1.0, 0.0); // Up

    // Print some text in the demo console
    // -----------------------------------

    consoleClear();

    // Print some controls and information
    printf("Left image:   Stretch\n");
    printf("Center image: Wrap\n");
    printf("Right image:  Wrap and flip\n");
    printf("\n");
    printf("START:  Exit to loader\n");

    while (1)
    {
        // Synchronize game loop to the screen refresh
        swiWaitForVBlank();

        // Handle user input
        // -----------------

        scanKeys();

        uint16_t keys = keysHeld();
        if (keys & KEY_START)
            break;

        // Render 3D scene
        // ---------------

        glMatrixMode(GL_MODELVIEW);

        glPolyFmt(POLY_ALPHA(31) | POLY_CULL_NONE);

        glColor3f(1, 1, 1);

        glPushMatrix();

            glTranslatef(-2.5, 0, 0);

            glBindTexture(0, textureID[0]);

            glBegin(GL_QUADS);

                glTexCoord2t16(inttot16(-32), inttot16(64));
                glVertex3v16(floattov16(-1), floattov16(-1), 0);

                glTexCoord2t16(inttot16(64), inttot16(64));
                glVertex3v16(floattov16(1), floattov16(-1), 0);

                glTexCoord2t16(inttot16(64), inttot16(-32));
                glVertex3v16(floattov16(1), floattov16(1), 0);

                glTexCoord2t16(inttot16(-32), inttot16(-32));
                glVertex3v16(floattov16(-1), floattov16(1), 0);

            glEnd();

        glPopMatrix(1);

        glPushMatrix();

            glTranslatef(0, 0, 0);

            glBindTexture(0, textureID[1]);

            glBegin(GL_QUADS);

                glTexCoord2t16(inttot16(-32), inttot16(64));
                glVertex3v16(floattov16(-1), floattov16(-1), 0);

                glTexCoord2t16(inttot16(64), inttot16(64));
                glVertex3v16(floattov16(1), floattov16(-1), 0);

                glTexCoord2t16(inttot16(64), inttot16(-32));
                glVertex3v16(floattov16(1), floattov16(1), 0);

                glTexCoord2t16(inttot16(-32), inttot16(-32));
                glVertex3v16(floattov16(-1), floattov16(1), 0);

            glEnd();

        glPopMatrix(1);

        glPushMatrix();

            glTranslatef(2.5, 0, 0);

            glBindTexture(0, textureID[2]);

            glBegin(GL_QUADS);

                glTexCoord2t16(inttot16(-32), inttot16(64));
                glVertex3v16(floattov16(-1), floattov16(-1), 0);

                glTexCoord2t16(inttot16(64), inttot16(64));
                glVertex3v16(floattov16(1), floattov16(-1), 0);

                glTexCoord2t16(inttot16(64), inttot16(-32));
                glVertex3v16(floattov16(1), floattov16(1), 0);

                glTexCoord2t16(inttot16(-32), inttot16(-32));
                glVertex3v16(floattov16(-1), floattov16(1), 0);

            glEnd();

        glPopMatrix(1);

        glFlush(0);
    }

    glDeleteTextures(3, &textureID[0]);

    return 0;
}
