#include <c64/vic.h>
#include <c64/rasterirq.h>
#include <c64/sprites.h>
#include <stdlib.h>

#define Screen ((char *)0x400)

// make space until 0x2000 for code and data

#pragma region( lower, 0x0a00, 0x2000, , , {code, data} )

// then space for our sprite data

#pragma section( spriteset, 0)

#pragma region( spriteset, 0x2000, 0x2c00, , , {spriteset} )


// everything beyond will be code, data, bss and heap to the end

#pragma region( main, 0x2c00, 0xa000, , , {code, data, bss, heap, stack} )


// spriteset at fixed location

#pragma data(spriteset)

char spriteset[64 * 48] = {0};

#pragma data(data)

char charset[32 * 8] = {
	#embed "../resources/scifiglyph.bin"	
};


const char * scrolltext[] = {


"This sample uses",
"six sprites",
"multiplexed six",
"times to provide",
"twelve lines of",
"text with eighteen",
"characters each",
"",
"each character is",
"extracted from a",
"custom font into a",
"new sprite and the",
"sprites are then",
"moved up the",
"screen",
"",
"interrupts are",
"used to switch the",
"sprite vertical",
"position and",
"graphics",
"data every two",
"character lines"
"",
"",
"",
"",
""
};


RIRQCode	*	spmux[6], final;

// Expand a line of source text into buffer centered

void readline(char * dp, char n)
{
	const char * sp = scrolltext[n];

	// Count number of characters

	char s = 0;
	while (sp[s])
		s++;
	
	// Left padding

	char l = (18 - s) >> 1;
	char i = 0;
	while (i < l)
		dp[i++] = ' ';

	// Text in center
	s = 0;
	while (sp[s])
		dp[i++] = sp[s++];

	// Right padding
	while (i < 18)
		dp[i++] = ' ';
}

// Expand six characters into sprite

void expandline(const char * line, char sppos, char ty)
{
	// Target position for first character
	char	*	dp = spriteset + 64 * sppos + 3 * ty;


	char	xl = 0;
	for(char x=0; x<6; x++)
	{
		// Source character data
		const char * sp = charset + 8 * (line[x] & 0x1f);

		// Copy eight rows
		dp[ 0] = sp[0];	dp[ 3] = sp[1];	dp[ 6] = sp[2];	dp[ 9] = sp[3];
		dp[12] = sp[4];	dp[15] = sp[5];	dp[18] = sp[6];	dp[21] = sp[7];

		// Next character
		dp++;
		xl++;

		// Advance to next sprite
		if (xl == 3)
		{
			dp += 61;
			xl = 0;
		}
	}
}

int main(void)
{
	rirq_init(true);
	spr_init(Screen);

	// Vertical pixel offset
	sbyte	oy = 0;

	// Index of top left sprite
	char	sy = 128;

	// Loop over five rows of virtual sprites
	for(char i=0; i<5; i++)
	{
		// Allocate large rirq command with 12 writes
		RIRQCode	*	sp = rirq_alloc(12);		
		spmux[i] = 	sp;

		// Six sprites per row
		for(char x=0; x<6; x++)
		{
			// Vertical sprite position
			rirq_write(sp, 2 * x + 0, &vic.spr_pos[x].y, 48 * (i + 1) + oy);

			// Sprite graphic
			rirq_write(sp, 2 * x + 1, Screen + 0x3f8 + x, sy + 1 + i);
		}

		// Place in list
		rirq_set(i, 48 * i + 46 + oy, spmux[i]);
	}

	// Final irq at bottom of screen
	rirq_build(&final, 0);
	rirq_set(5, 250, &final);

	// Placing top sprites
	for(int x=0; x<6; x++)
		spr_set(x, true, 40 + 48 * x, oy, 128, 1, false, true, true);

	// Initial sort and start of processing
	rirq_sort();
	rirq_start();

	// Buffer for single line	
	char	line[20];

	// Index into text to scroll
	char	lpos = 0;

	for(;;)	
	{
		// Wait for end of irq list
		rirq_wait();

		// Advance one pixel line
		oy--;
		if (oy < 0)
		{
			// Advance one row of sprites
			oy += 48;
			sy += 6;			
			if (sy == 128 + 36)
				sy = 128;
		}

		// Update interrupt position
		for(char i=0; i<5; i++)
		{
			int ty = 48 * i + 45 + oy;

			// No interrupts below screen bottom
			if (ty < 250)
				rirq_move(i, ty);	
			else
				rirq_clear(i);
		}

		// Sort list
		rirq_sort();

		// Reset row of top level sprites
		char sty = sy;
		for(int x=0; x<6; x++)
		{			
			spr_move(x, 40 + 48 * x, oy); 
			spr_image(x, sty);
			sty ++;
		}

		// Update write data in interrupt list	
		for(char i=0; i<5; i++)
		{
			RIRQCode	*	sp = spmux[i];

			// Check sprite rotation
			if (sty == 128 + 36)
				sty = 128;

			// Update vertical position and sprite data
			char py = 48 * (i + 1) + oy;
			for(char x=0; x<6; x++)
			{
				rirq_data(sp, 2 * x + 0, py);
				rirq_data(sp, 2 * x + 1, sty);
				sty ++;
			}
		}

		// Expand next line of text into bottom row of
		// sprites while invisible
		switch (oy)
		{
		case 46:
		case 42:
			readline(line, lpos);
			lpos++;
			if (lpos == 27)
				lpos = 0;
			break;
		case 45:
			expandline(line +  0, sty - 6 - 128, 0);
			break;
		case 44:
			expandline(line +  6, sty - 4 - 128, 0);
			break;
		case 43:
			expandline(line + 12, sty - 2 - 128, 0);
			break;

		case 41:
			expandline(line +  0, sty - 6 - 128, 12);
			break;
		case 40:
			expandline(line +  6, sty - 4 - 128, 12);
			break;
		case 39:
			expandline(line + 12, sty - 2 - 128, 12);
			break;
		}
	}

	return 0;
}
