#include <genesis.h>
#include <resources.h>
#include "megalaga.h"

// =============================================================
// CONSTANTS
// =============================================================
#define LEFT_EDGE 0
#define RIGHT_EDGE 320
#define BOTTOM_EDGE 224

#define SFX_LASER 64

#define ANIM_STRAIGHT 0
#define ANIM_MOVE     1

#define MAX_ENEMIES 6
#define MAX_BULLETS 6
#define MAX_PLAYER_BULLETS 3
#define SHOT_INTERVAL 120
#define POWERUP_DURATION 180

// =============================================================
// ENTITY STRUCT
// =============================================================
typedef struct {
	int x, y;
	int w, h;
	int velx, vely;
	int health;
	Sprite* sprite;
	char name[6];
} Entity;

// =============================================================
// STATIC MODULE VARIABLES (safe for multi-game build)
// =============================================================
static Entity player;
static Entity enemies[MAX_ENEMIES];
static Entity bullets[MAX_BULLETS];
static Entity powerup;

static u16 enemiesLeft;
static u16 bulletsOnScreen;
static u16 shotTicker;
static u16 shotByPlayer;
static u16 maxPlayerBullets;
static u16 powerupTimer;

static int score;
static int offset;

static char hud_string[40];

// =============================================================
// FORWARD DECLARATIONS
// =============================================================
static void killEntity(Entity* e);
static void reviveEntity(Entity* e);
static void positionEnemies();
static void positionPlayer();
static void shootBullet(Entity* shooter);
static void positionBullets();
static void handleCollisions();
static void updateScoreDisplay();
static void spawnPowerupAt(u16 X, u16 Y);
static void positionPowerup();
static void activatePowerup();
static void deactivatePowerup();
static void myJoyHandler(u16 joy, u16 changed, u16 state);

// =============================================================
// ENTRY POINT FOR MENU SYSTEM
// =============================================================
void runMegalaga()
{
    // Reset all game-specific globals
    enemiesLeft = 0;
    bulletsOnScreen = 0;
    shotTicker = 0;
    shotByPlayer = 0;
    maxPlayerBullets = MAX_PLAYER_BULLETS;
    powerupTimer = 0;
    score = 0;
    offset = 0;

    VDP_resetScreen();
    XGM_setPCM(SFX_LASER, sfx_laser, sizeof(sfx_laser));

    JOY_setEventHandler(myJoyHandler);

    //------------------------------------------------------------
    // Load background tiles/palettes
    //------------------------------------------------------------
    SYS_disableInts();
    VDP_loadTileSet(background.tileset, 1, DMA);
    PAL_setPalette(PAL1, background.palette->data, DMA);
    PAL_setPalette(PAL2, background.palette->data, DMA);
    VDP_setScrollingMode(HSCROLL_PLANE, VSCROLL_PLANE);
    PAL_setColor(34,RGB24_TO_VDPCOLOR(0x0078f8));
    SYS_enableInts();

    SPR_init();

    //------------------------------------------------------------
    // Initialize Player
    //------------------------------------------------------------
    player.x = 152;
    player.y = 192;
    player.w = 16;
    player.h = 16;
    player.velx = 0;
    player.vely = 0;
    player.health = 1;
    player.sprite = SPR_addSprite(&ship, player.x, player.y,
                                  TILE_ATTR(PAL1,0,FALSE,FALSE));

    //------------------------------------------------------------
    // Generate random star background
    //------------------------------------------------------------
    for(int i = 0; i < 1280; i++)
    {
        int x = i % 40;
        int y = i / 40;
        int tile = (random() % 10) + 1;
        if(tile > 3) tile = 1;
        VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1,0,0,0,tile), x, y);
    }

    //------------------------------------------------------------
    // Initialize Enemies
    //------------------------------------------------------------
    for(int i = 0; i < MAX_ENEMIES; i++)
    {
        Entity* e = &enemies[i];
        e->x = i * 32;
        e->y = 32;
        e->w = 16;
        e->h = 16;
        e->velx = 1;
        e->vely = 0;
        e->health = 1;

        sprintf(e->name, "En%d", i);
        e->sprite = SPR_addSprite(&ship, e->x, e->y,
                                  TILE_ATTR(PAL2,0,TRUE,FALSE));
        enemiesLeft++;
    }

    //------------------------------------------------------------
    // Initialize Bullets
    //------------------------------------------------------------
    for(int i = 0; i < MAX_BULLETS; i++)
    {
        Entity* b = &bullets[i];
        b->x = 0;
        b->y = -10;
        b->w = 8;
        b->h = 8;
        b->velx = 0;
        b->vely = 0;
        b->health = 0;

        sprintf(b->name, "Bu%d", i);
        b->sprite = SPR_addSprite(&bullet, b->x, b->y,
                                  TILE_ATTR(PAL1,0,FALSE,FALSE));

        SPR_setVisibility(b->sprite, HIDDEN);
    }

    //------------------------------------------------------------
    // Initialize Powerup
    //------------------------------------------------------------
    powerup.x = 0;
    powerup.y = 0;
    powerup.w = 8;
    powerup.h = 8;
    powerup.velx = 0;
    powerup.vely = 0;
    powerup.health = 0;
    powerup.sprite = SPR_addSprite(&spr_powerup, 0, 0,
                                   TILE_ATTR(PAL1,0,FALSE,FALSE));
    SPR_setVisibility(powerup.sprite, HIDDEN);

    updateScoreDisplay();

    //------------------------------------------------------------
    // MAIN GAME LOOP
    //------------------------------------------------------------
    while(1)
    {
        // exit to menu
        if (JOY_readJoypad(JOY_1) & BUTTON_C)
            break;

        // Scroll background
        offset -= 2;
        if(offset <= -256) offset = 0;
        VDP_setVerticalScroll(BG_B, offset);

        // Update game objects
        positionPlayer();
        positionBullets();
        positionEnemies();
        positionPowerup();
        handleCollisions();

        // Powerup decay
        if(powerupTimer > 0)
        {
            powerupTimer--;
            if(powerupTimer == 0)
                deactivatePowerup();
        }

        SPR_update();
        SYS_doVBlankProcess();
    }

    //------------------------------------------------------------
    // Cleanup when exiting to menu
    //------------------------------------------------------------
    SPR_end();
    VDP_resetScreen();
}

// =============================================================
// ENTITY HELPERS
// =============================================================
static void killEntity(Entity* e)
{
    e->health = 0;
    SPR_setVisibility(e->sprite, HIDDEN);
}

static void reviveEntity(Entity* e)
{
    e->health = 1;
    SPR_setVisibility(e->sprite, VISIBLE);
}

// =============================================================
// POSITION / MOVEMENT
// =============================================================
static void positionEnemies()
{
    shotTicker++;

    for (u16 i = 0; i < MAX_ENEMIES; i++)
    {
        Entity* e = &enemies[i];

        if(e->health > 0)
        {
            e->x += e->velx;

            // Bounce off edges
            if(e->x + e->w > RIGHT_EDGE) e->velx = -1;
            if(e->x < LEFT_EDGE)         e->velx =  1;

            SPR_setPosition(e->sprite, e->x, e->y);

            // Shooting logic
            if(shotTicker >= SHOT_INTERVAL)
            {
                if((random() % 10) > 4)
                {
                    shootBullet(e);
                }
                shotTicker = 0;
            }
        }
    }
}

static void positionPlayer()
{
    player.x += player.velx;

    if(player.x < LEFT_EDGE) player.x = LEFT_EDGE;
    if(player.x + player.w > RIGHT_EDGE) player.x = RIGHT_EDGE - player.w;

    SPR_setPosition(player.sprite, player.x, player.y);
}

static void shootBullet(Entity* shooter)
{
    bool fromPlayer = (shooter->y > 100);

    if(fromPlayer)
    {
        if(shotByPlayer >= maxPlayerBullets)
            return;
    }

    for(int i = 0; i < MAX_BULLETS; i++)
    {
        Entity* b = &bullets[i];

        if(b->health == 0)
        {
            b->x = shooter->x + 4;
            b->y = shooter->y;

            reviveEntity(b);
            b->vely = fromPlayer ? -3 : 3;

            bulletsOnScreen++;
            if(fromPlayer) shotByPlayer++;

            XGM_startPlayPCM(SFX_LASER, 1, SOUND_PCM_CH2);
            return;
        }
    }
}

static void positionBullets()
{
    for(int i = 0; i < MAX_BULLETS; i++)
    {
        Entity* b = &bullets[i];

        if(b->health > 0)
        {
            b->y += b->vely;

            // off-screen cleanup
            if(b->y + b->h < 0 || b->y > BOTTOM_EDGE)
            {
                killEntity(b);
                bulletsOnScreen--;
                if(b->vely < 0) shotByPlayer--;
            }
            else
            {
                SPR_setPosition(b->sprite, b->x, b->y);
            }
        }
    }
}

// =============================================================
// COLLISION
// =============================================================
static int collideEntityPair(Entity* a, Entity* b)
{
    return(a->x < b->x + b->w &&
           a->x + a->w > b->x &&
           a->y < b->y + b->h &&
           a->y + a->h >= b->y);
}

static void handleCollisions()
{
    Entity* b;
    Entity* e;

    for(int i = 0; i < MAX_BULLETS; i++)
    {
        b = &bullets[i];
        if(b->health == 0) continue;

        if(b->vely < 0)
        {
            // PLAYER BULLET -> ENEMY
            for(int j = 0; j < MAX_ENEMIES; j++)
            {
                e = &enemies[j];
                if(e->health > 0 && collideEntityPair(b, e))
                {
                    killEntity(e);
                    killEntity(b);
                    enemiesLeft--;
                    bulletsOnScreen--;
                    shotByPlayer--;

                    score += 10;
                    updateScoreDisplay();

                    if(enemiesLeft % 5 == 0)
                        spawnPowerupAt(e->x, e->y);

                    break;
                }
            }
        }
        else
        {
            // ENEMY BULLET -> PLAYER
            if(player.health > 0 && collideEntityPair(b, &player))
            {
                killEntity(b);
                killEntity(&player);
                bulletsOnScreen--;
            }
        }
    }
}

// =============================================================
// HUD
// =============================================================
static void updateScoreDisplay()
{
    sprintf(hud_string,"SCORE:%d LEFT:%d", score, enemiesLeft);
    VDP_clearText(0,0,40);
    VDP_drawText(hud_string,0,0);
}

// =============================================================
// POWERUPS
// =============================================================
static void activatePowerup()
{
    maxPlayerBullets = MAX_BULLETS;
    powerupTimer = POWERUP_DURATION;

    PAL_setColor(18, RGB24_TO_VDPCOLOR(0xf8fc00));
}

static void deactivatePowerup()
{
    maxPlayerBullets = MAX_PLAYER_BULLETS;
    PAL_setColor(18, RGB24_TO_VDPCOLOR(0xf83800));
}

static void spawnPowerupAt(u16 X, u16 Y)
{
    powerup.x = X;
    powerup.y = Y;
    reviveEntity(&powerup);
}

static void positionPowerup()
{
    if(powerup.health > 0)
    {
        powerup.y++;

        if(powerup.y > BOTTOM_EDGE)
        {
            killEntity(&powerup);
        }
        else if(collideEntityPair(&player, &powerup))
        {
            activatePowerup();
            killEntity(&powerup);
        }
    }

    SPR_setPosition(powerup.sprite, powerup.x, powerup.y);
}

// =============================================================
// INPUT
// =============================================================
static void myJoyHandler(u16 joy, u16 changed, u16 state)
{
    if(joy != JOY_1) return;

    if(state & BUTTON_RIGHT)
    {
        player.velx = 2;
        SPR_setAnim(player.sprite, ANIM_MOVE);
        SPR_setHFlip(player.sprite, TRUE);
    }
    else if(state & BUTTON_LEFT)
    {
        player.velx = -2;
        SPR_setAnim(player.sprite, ANIM_MOVE);
        SPR_setHFlip(player.sprite, FALSE);
    }
    else
    {
        if(changed & (BUTTON_RIGHT | BUTTON_LEFT))
        {
            player.velx = 0;
            SPR_setAnim(player.sprite, ANIM_STRAIGHT);
        }
    }

    if((state & BUTTON_B) && (changed & BUTTON_B))
        shootBullet(&player);
}
