#include "gba.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mod.h"
#include "sound.h"

#define gbaprintf(x...) sprintf(foo, x); print(foo);

const u16 amiga_period_table[] = {
	856,808,762,720,678,640,604,570,538,508,480,453,
	428,404,381,360,339,320,302,285,269,254,240,226,
	214,202,190,180,170,160,151,143,135,127,120,113 };

const char* note_names[] = {
	"C-1", "C#1", "D-1", "D#1", "E-1", "F-1", "F#1", "G-1", "G#1", "A-1", "A#1", "B-1",
	"C-2", "C#2", "D-2", "D#2", "E-2", "F-2", "F#2", "G-2", "G#2", "A-2", "A#2", "B-2",
	"C-3", "C#3", "D-3", "D#3", "E-3", "F-3", "F#3", "G-3", "G#3", "A-3", "A#3", "B-3",
	"---"};

const u16 amiga_period_table_size = 12*3;

void dump_mod(mod_header* m)
{
	int i;
	char foo[100];
	gbaprintf("MOD name: %s\n\n", m->name);

	gbaprintf("Samples:\n\n");
	for(i=0; i < m->num_samples; i++)
	{
		mod_sample* s = &m->samples[i];
		
		gbaprintf("Sample %d:\n", i);
		gbaprintf("\tname: %s\n", s->name);
		gbaprintf("\tlength: %d\n", s->length);
		gbaprintf("\tfinetune: %d\n", s->finetune);
		gbaprintf("\tvolume: %d\n", s->volume);
		gbaprintf("\tloop start: %d\n", s->loop_start);
		gbaprintf("\tloop end: %d\n", s->loop_end);
		gbaprintf("\tdata location: %08x\n", s->data);
	}

	gbaprintf("Song length: %d\nNumber of patterns: %d\n\n", m->length, m->num_patterns);
	gbaprintf("Order list:\n");

	for(i=0; i < m->length; i++)
	{
		gbaprintf("pos: %03d pattern: %03d\n", i, m->order[i]);
	}

	gbaprintf("Patterns:\n\n");
	for(i=0; i < m->num_patterns; i++)
	{
		mod_pattern* p = &m->patterns[i];
		int x, y, n=0;
		gbaprintf("Pattern: %d\n", i);
		gbaprintf("Channels: %d Length %d\n", p->channels, p->length);
		
		for(y=0; y < p->length; y++)
		{
			gbaprintf("%02d: ", y);
			for(x=0; x < p->channels; x++)
			{
				//mod_note* note = &p->data[(y * p->channels) + x];
				mod_note* note = &p->data[n++];
				
				gbaprintf("%s %02d %01x%02x  ",
					note_names[note->note],
					note->sample_number,
					note->effect_number,
					note->effect_params);
			}
			gbaprintf("\n");
		}
		//break;
	}
}

void mod_parse(mod_header* m, const u8* cur)
{
	int i;

	/* read mod name */
	memcpy(&m->name[0], cur, 20); m->name[20]=0;
	cur += 20;

	/* read samples */
	m->num_samples = 31;
	for(i=0; i < m->num_samples; i++)
	{
		u16 a, b;
		mod_sample* s = &m->samples[i];

		/* sample name */
		memcpy(s->name, cur, 22); s->name[22]=0;
		cur+=22;
			
		/* sample length */
		a = *cur++; b = *cur++;
		s->length = ((a << 8) | b) * 2;

		/* finetune and volume */
		s->finetune = *cur++;
		s->volume = *cur++;

		/* loop start and length */
		a = *cur++; b = *cur++;
		s->loop_start = ((a << 8) | b) * 2;
		a = *cur++; b = *cur++;
		s->loop_end = s->loop_start + (((a << 8) | b) * 2);
		if(s->loop_end == 2) s->loop_end = 0; 
	}

	/* read order information */
	m->num_patterns = 0;
	m->length = *cur++; // get length of song
	cur++;	// skip PT restart position/unused byte
	for(i=0; i < 128; i++)
	{
		u16 o = *cur++;
		m->order[i] = o;
		if(o > m->num_patterns) m->num_patterns = o;
	}
	m->num_patterns++;

	/* skip type identifier */
	cur+=4;

	/* pattern data */
	m->patterns = calloc(m->num_patterns, sizeof(mod_pattern));
	for(i=0; i < m->num_patterns; i++)
	{
		mod_pattern* pat;
		int n;
			
		pat = &m->patterns[i];
		pat->data = calloc(64 * 4, sizeof(mod_note));
		pat->channels = 4;
		pat->length = 64;

		for(n=0; n < pat->channels * pat->length; n++)
		{
			mod_note* note = &pat->data[n];
			u8 note_data[4];
			u16 period, p;
				
			note_data[0] = *cur++;
			note_data[1] = *cur++;
			note_data[2] = *cur++;
			note_data[3] = *cur++;

			note->sample_number = (note_data[0] & 0xf0) | ((note_data[2] & 0xf0) >> 4);
			note->effect_number = (note_data[2] & 0x0f);
			note->effect_params = note_data[3];
			period = ((note_data[0] & 0x0f) << 8) | note_data[1];
			note->note = amiga_period_table_size;
				
			for(p=0; p < amiga_period_table_size; p++)
			{
				if(period > amiga_period_table[p])
				{
					note->note = p-1;
					break;
				}
			}
		}
	}
	
	/* set pointers to sample data */
	for(i=0; i < m->num_samples; i++)
	{
		mod_sample* s = &m->samples[i];

		s->data = (s8*)cur;
		cur += s->length;
	}

	m->pattern = 0; m->line = 0;
	m->master_volume = 16;
}

s16 amiga_mod_channel_panning[] = { -8, 8, 8, -8 };

void mod_play(mod_header* m, u16* channels)
{
	int i;
	sfxSystemSound foo;

	foo.volume = 16;
	foo.start_offset = 0;
	foo.loop_start = 0;
	foo.loop_end = 0;

	mod_pattern* p = &m->patterns[m->order[m->pattern]];

	for(i=0; i<p->channels; i++)
	{
		mod_note* n = &p->data[(m->line * p->channels) + i];

		if(n->note < amiga_period_table_size)
		{
			mod_sample* s;
				
			/* play note */
			foo.panning = amiga_mod_channel_panning[i];

			foo.freq = AMIGA_PERIOD_TO_HZ(amiga_period_table[n->note]);

			s = &m->samples[n->sample_number - 1];
			foo.length = s->length;
			foo.pData = s->data;
			foo.loop_start = s->loop_start;
			foo.loop_end = s->loop_end;
			foo.volume = ((s->volume >> 2) * m->master_volume) >> 4;
			foo.start_offset = 0;
			sfxSystemSetChannel(channels[i], &foo);
		}
			
		/* handle effects */
		switch(n->effect_number)
		{
			case 0x9:
				/* start offset */
				sfxSystemChannelSetOffset(i, n->effect_params << 8);
				break;

			case 0xC:
				/* volume */
				sfxSystemChannelSetVolume(i,
					((n->effect_params >> 2) * m->master_volume) >> 4);
				break;
		}
	}
	
	m->line++;
	if(m->line == p->length)
	{
		m->line = 0; m->pattern++;
	}
	if(m->pattern == m->length)
	{
		m->pattern = 0;
	}
}
