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

// There are two main ways to animate a sprite: we can either load all frames
// to VRAM from the start (high VRAM usage, low CPU usage), or we can load one
// frame and replace it by new ones whenever we want to change it (low VRAM
// usage, high CPU usage). This example shows how to animate a sprite with both
// systems, as they are both important depending on the situation.

#include <nds.h>

// This is autogenerated from advnt.png and advnt.grit
#include "advnt.h"

// Copy the requested frame of the advnt sprite to the provided address.
void copy_sprite_frame(void *dst, int frame)
{
    uint32_t frame_size = 32 * 64;
    uint32_t offset = frame_size * frame;
    uint8_t *base = (uint8_t *)advntTiles;

    memcpy(dst, base + offset, frame_size);
}

int main(int argc, char *argv[])
{
    videoSetMode(MODE_0_2D);
    videoSetModeSub(MODE_0_2D);

    consoleDemoInit();

    vramSetBankA(VRAM_A_MAIN_SPRITE);
    vramSetBankD(VRAM_D_SUB_SPRITE);

    oamInit(&oamMain, SpriteMapping_1D_32, false);
    oamInit(&oamSub, SpriteMapping_1D_32, false);

    // Load palette
    memcpy(SPRITE_PALETTE, advntPal, advntPalLen);
    memcpy(SPRITE_PALETTE_SUB, advntPal, advntPalLen);

    // The sprite on the main screen will only have one frame in VRAM
    u16 *gfxOneFrame = oamAllocateGfx(&oamMain, SpriteSize_32x64,
                                      SpriteColorFormat_256Color);
    copy_sprite_frame(gfxOneFrame, 0);

    // The sprite on the sub screen will keep all frames in VRAM
    u16 *gfxAllFrames[6];
    for (int i = 0; i < 6; i++)
    {
        gfxAllFrames[i] = oamAllocateGfx(&oamSub, SpriteSize_32x64,
                                         SpriteColorFormat_256Color);
        copy_sprite_frame(gfxAllFrames[i], i);
    }

    oamSet(&oamMain, 0,
           100, 50, // X, Y
           0, // Priority
           0, // Palette index
           SpriteSize_32x64, SpriteColorFormat_256Color, // Size, format
           gfxOneFrame,  // Graphics offset
           -1, // Affine index
           false, // Double size
           false, // Hide
           false, false, // H flip, V flip
           false); // Mosaic

    oamSet(&oamSub, 0,
           150, 70, // X, Y
           0, // Priority
           0, // Palette index
           SpriteSize_32x64, SpriteColorFormat_256Color, // Size, format
           gfxAllFrames[0],  // Graphics offset
           -1, // Affine index
           false, // Double size
           false, // Hide
           false, false, // H flip, V flip
           false); // Mosaic

    printf("START: Exit to loader\n");

    int frame = 0;
    int delay = 0;

    while (1)
    {
        swiWaitForVBlank();

        oamUpdate(&oamMain);
        oamUpdate(&oamSub);

        delay++;
        if (delay > 20)
        {
            delay = 0;

            frame++;
            if (frame > 5)
                frame = 0;

            // Copy a new frame for the pointer in the main screen
            copy_sprite_frame(gfxOneFrame, frame);

            // Point the sprite in the sub screen to a new pre-loaded frame
            oamSetGfx(&oamSub, 0, SpriteSize_32x64, SpriteColorFormat_256Color,
                      gfxAllFrames[frame]);
        }

        scanKeys();

        u16 keys_held = keysHeld();

        if (keys_held & KEY_START)
            break;
    }

    oamFreeGfx(&oamMain, gfxOneFrame);
    for (int i = 0; i < 6; i++)
        oamFreeGfx(&oamSub, gfxAllFrames[i]);

    return 0;
}
