#include <string.h>
#include <stdio.h>
#include "ff.h"
#include "play.h"
#include "id3.h"
#include "src_ctrl.h"
#include "ui.h"

extern int decode(unsigned char const *start, unsigned long length);

#define MP3_WORK_SIZE (1152*2*2)
static unsigned char mp3_work[MP3_WORK_SIZE];
static unsigned char *next_header;
FIL fobj;

static int get_info;

static FRESULT get_header(char* filename)
{
	FRESULT res;
	UINT size;

	res = f_read(&fobj, (void*)mp3_work, 10, &size);
	if(res != FR_OK) goto get_header_return;

	if(0 == strncmp((char*)mp3_work, "ID3", 3))
	{
		UINT id3_size =
			((*(mp3_work + 6) & 0x7f) << 21) +
			((*(mp3_work + 7) & 0x7f) << 14) +
			((*(mp3_work + 8) & 0x7f) << 7) +
			((*(mp3_work + 9) & 0x7f) << 0);

		res = f_read(&fobj, (void*)(mp3_work + 10), id3_size, &size);	/* ID3ver2タグを読み出す */
		if(res != FR_OK) goto get_header_return;

		if(2 == *(char*)(mp3_work + 3))
		{
			get_info = 1;
			id3_v2(mp3_work, size);
		}
		else
		if(3 == *(char*)(mp3_work + 3))
		{
			get_info = 1;
			id3_v2_3(mp3_work, size);
		}
		res = get_header(filename);	/* もう一度 */
		goto get_header_return;
	}

	if(0 == strncmp((char*)mp3_work, "RIFF", 4))
	{
		res = f_read(&fobj, (void*)(mp3_work + 10), 10, &size);	/* RIFF MP3ヘッダを読み飛ばす */
		if(res != FR_OK) goto get_header_return;

		res = get_header(filename);	/* もう一度 */
		goto get_header_return;
	}

	if(get_info == 0)
	{
		FILINFO Finfo = {.lfname = 0, .lfsize = 0};
		UINT size;

		res = f_stat(filename, &Finfo);
		if(res != FR_OK) goto get_header_return;

		res = f_lseek(&fobj, Finfo.fsize - 128);
		if(res != FR_OK) goto get_header_return;

		res = f_read(&fobj, (void*)mp3_work, 128, &size);
		if(res != FR_OK) goto get_header_return;

		if(0 == strncmp((char*)mp3_work, "TAG", 3))
		{
			get_info = 1;
			id3_v1(mp3_work);
		}
		else
		{
			get_info = 1;
			id3_none();
		}
		res = f_lseek(&fobj, 0);
		if(res != FR_OK) goto get_header_return;

		res = get_header(filename);	/* もう一度 */
		goto get_header_return;
	}

get_header_return:
	return res;
}

int play(char* filename)
{
	FRESULT res;

	next_header = 0;
	get_info = 0;

	res = f_open(&fobj, filename, FA_READ);
	if(res != FR_OK) goto play_return;

	res = get_header(filename);
	if(res != FR_OK) goto play_return;

	if(get_info == 1)
	{
		printf("%s\r\n", id3tag_tt2());
		printf("%s\r\n", id3tag_tal());
		printf("%s\r\n", id3tag_tp1());
		printf("\r\n");
	}

	ui_file_list(filename);

	{
	unsigned short sync = (unsigned short)((mp3_work[0] << 8) | (mp3_work[1] & 0x00f0));
	if(sync != 0xfff0)goto play_return;
	}
	next_header = mp3_work;

	{
		int sf = (mp3_work[2] >> 2) & 0x03;
		src_change_ifs(sf);	/* 可変サンプリングレートには対応不可 */
	}

	decode((unsigned char const *)mp3_work, (unsigned long)MP3_WORK_SIZE);

	res = f_close(&fobj);
	if(res != FR_OK) goto play_return;

play_return:
	return (int)res;
}

void read_frame(unsigned int N)
{
	FRESULT res;
	UINT size;

	res = f_read(&fobj, (void*)(mp3_work + 10), N, &size);	/* 10バイト目以降のNバイトをワークメモリに読み込む */
	if(res != FR_OK) goto read_frame_error;
	if(N != size) goto read_frame_error;

	next_header = mp3_work + N;
	return;

read_frame_error:
	*(int*)mp3_work = 0;
	next_header = mp3_work;
	return;
}

void copy_next_header(void)
{
	memcpy(mp3_work, next_header, 10);	/* 先読みした「次のフレームのヘッダから10バイト」を先頭にコピー */
	return;
}

unsigned char* get_next_frame(void)
{
	return (unsigned char*)mp3_work;
}
