// -----------------------------------------------------------
// main.c - C64 demo: slide show (8 seconds per slide)
//   slide 1: giant TTG logo + credits scroller
//   slide 2: OUR FIRST GAME - Definitely not an Impostor
//   slide 3: OUR SECOND GAME IS COMING (border raster bars)
//   slide 4: CONTACT
//   slide 5: the assembly confession (typewriter)
//   slides transition through black between switches
//   (fade / random dissolve / column wipe, per slide)
//   all timed by polling the raster beam
//
// Files:
//   defs.h          - hardware pointers, constants
//   transitions.c   - slide transitions: fade, dissolve, wipe
//   common.c        - shared routines, tables, variables
//   slideN.c        - one file per slide: sN_init, sN_update + data
//   music.c         - SID music driver
//
// Build: oscar64 -n -O2 -o=build/demo.prg src/main.c  (see Makefile)
// -----------------------------------------------------------
#include <c64/cia.h>
#include "defs.h"
#include "common.h"
#include "transitions.h"
#include "slides.h"
#include "music.h"

// ---- slide table: init, update, duration ----
typedef void (*slidefn)(void);

static const slidefn slide_init_tbl[NUM_SLIDES] = {
	s1_init, s2_init, s3_init, s4_init, s5_init
};
static const slidefn slide_upd_tbl[NUM_SLIDES] = {
	s1_update, s2_update, s3_update, s4_update, s5_update
};
static const unsigned slide_dur_tbl[NUM_SLIDES] = {
	SLIDE_TIME, SLIDE_TIME, SLIDE_TIME, SLIDE_TIME, S5_TIME
};

// ---- per-slide transitions: reveal type / hide type ----
const char tin_tbl[NUM_SLIDES]  = { TR_FADE, TR_WIPE, TR_DISSOLVE, TR_FADE, TR_DISSOLVE };
const char tout_tbl[NUM_SLIDES] = { TR_DISSOLVE, TR_FADE, TR_WIPE, TR_DISSOLVE, TR_WIPE };

// ---- variables ----
char slide;                          // current slide index
static slidefn update;               // current slide's update routine
static unsigned dur;                 // frames left on this slide
static char paused;                  // 1 = slide countdown frozen
static char pheld;                   // 'p' key edge detection state
static char mheld;                   // 'm' key edge detection state

// ---- keyboard matrix scanning ----
// colmask selects the column (active low), rowmask the row bit;
// returns true while the key is held down (rows read active low)
static bool scan_key(char colmask, char rowmask)
{
	cia1.pra = colmask;
	return !(cia1.prb & rowmask);
}

static inline bool scan_space(void) { return scan_key(0x7f, 0x10); }  // column 7, row 4
static inline bool scan_p(void)     { return scan_key(0xdf, 0x02); }  // column 5, row 1
static inline bool scan_del(void)   { return scan_key(0xfe, 0x01); }  // column 0, row 0
static inline bool scan_m(void)     { return scan_key(0xef, 0x10); }  // column 4, row 4

// ---- load a slide's vectors and duration, then run its init ----
// (called by show_slide while the display is switched off)
void slide_setup(void)
{
	dur    = slide_dur_tbl[slide];
	update = slide_upd_tbl[slide];
	vic.color_border = VCOL_BLACK;
	vic.color_back   = VCOL_BLACK;
	vic.spr_enable   = 0;            // a slide that wants sprites enables them itself
	slide_init_tbl[slide]();
}

// ---- flip the pause state, remove the marker when resuming ----
static void toggle_pause(void)
{
	paused ^= 1;
	if (!paused)                     // turned off: clear the marker
		SCREEN[39] = ' ';            // (turned on: the main loop draws it)
}

// ---- flip the mute state ----
static void toggle_mute(void)
{
	mus_muted ^= 1;
	if (mus_muted)
		music_mute();
	else
		music_unmute();
}

int main(void)
{
#ifdef __OSCAR64C__
	__asm { sei }                    // no KERNAL IRQ - raster polling must not jitter
#endif

	music_init();                    // set up SID and start the music

	framectr = 0;
	slide = 0;
	show_slide();                    // draw slide 0 hidden, then fade it in

	// ============================================================
	// main loop - one update call per frame; when the slide's
	// duration runs out, switch to the next slide
	// ============================================================
	for (;;)
	{
		update();                    // each update runs exactly one frame
		music_play();                // slide updates end at RAS_BOTTOM, so this
		                             //   ticks the music once per frame

		// 'p' toggles pause - edge detected, so holding the key
		// does not keep flipping the state
		if (scan_p())
		{
			if (!pheld)
			{
				pheld = 1;
				toggle_pause();
			}
		}
		else
			pheld = 0;

		if (paused)                  // paused: keep the marker in the top-right
		{                            //   corner alive (slide switches clear it)
			SCREEN[39] = 'p';
			COLRAM[39] = VCOL_WHITE;
		}

		if (scan_m())                // 'm' toggles mute - edge detected
		{
			if (!mheld)
			{
				mheld = 1;
				toggle_mute();
			}
		}
		else
			mheld = 0;

		if (scan_del())              // backspace: one slide back
		{
			trans_out();
			while (scan_del())       // a held key must not race through
				;                    //   the whole show
			if (slide == 0)
				slide = NUM_SLIDES;
			slide--;                 // step back one slide, wrap to the last
			show_slide();
			continue;
		}

		bool timeout = false;
		if (!paused)                 // paused: the countdown stands still
		{
			dur--;
			timeout = dur == 0;
		}

		if (scan_space() || timeout) // space: next slide right away
		{
			trans_out();             // hide the old slide (its out-transition)
			while (scan_space())
				;
			slide++;                 // advance to the next slide
			if (slide == NUM_SLIDES)
				slide = 0;
			show_slide();            // draw it hidden, then fade it in
		}
	}

	return 0;
}
