#include "Song.h"
#include "Effects.h"
#include "Instrument.h"
#include "Loaders.h"
#include "FSysLog.h"

/*
 * Channels: 8
 * Patterns: [1...128]
 * Rows    : 64
 * Orders  : [1...128]
 * Samples : [1...64]
 * Instr.  : 0
 * Tempo   : [32...255], T frames per 2.5 seconds.
 * Speed   : [1...31], S frames per row.
 * Notes   : [C-0...C-4...B-7]
 * Pitch   : Uses Amiga periods, base is 428 units, period is divided by 2 each octave up.
 *           Slides in x/1 units per frame.
 * Volume  : [0...64], linear.
 * Panning : [0...F].
 * Sampling: 8363 Hz.
 */

#define NoteDelta (Note_Central-12*4)

static const uint8_t Typestr_Asyl[] = "ASYLUM Music Format";
static const uint8_t Asyl_Vers[] = " V1.0";

typedef struct
{
	uint8_t     Type[19];
	uint8_t     Vers[5];
	uint8_t     Dummy[8];
	uint8_t     Speed;
	uint8_t     Tempo;
	uint8_t     Samples;
	uint8_t     Patterns;
	uint8_t     Orders;
	uint8_t     RestartPos;
	uint8_t     Seq[256];
} LHdr;

static const _kernel_oserror* Pattern_Asylum(SongHdr* pSong, uint32_t i, Pattern* pPattern, const uint8_t* pPos)
{
	const _kernel_oserror* err = NULL;
	int             j, k;
	uint32_t        Note, Inst, Cmd, Value;

	err = Loaders_StartPattern(pSong, i);
	if (err) return err;

	for (j = 0; j < pPattern->Rows; j++)
	{
		err = Loaders_StartRow(pSong, j);
		if (err) return err;

		for (k = 0; k < pSong->Channels; k++)
		{
			Note = *pPos++;
			if (Note)
			{
				if (Note <= 12*8)
					Note += NoteDelta;
				else
				{
					SysLog_BadNote(pSong, 0, k, Note);
					Note = 0;
				}
			}
			Inst = *pPos++;
			Cmd = *pPos++;
			Value = *pPos++;

			err = Loaders_StartChannel(pSong, k, Note, Inst);
			if (err) return err;

			convert_effect_MOD(pSong, k, Cmd, Value, Note);
			err = Loaders_EndChannel(pSong);
			if (err) return err;
		}

		err = Loaders_EndRow(pSong);
		if (err) return err;
	}

	return Loaders_EndPattern(pSong, pPattern);
}

const _kernel_oserror* Loader_Asylum(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*    pSubSong = Song_GetSubSong(pSong, 0);
	int         i;
	Pattern*    pPattern;
	Sample*     pSmp;
	uint32_t    val;
	LHdr*       pLHdr = NULL;
	uint8_t*    pLTrack = NULL;

	// Allocate memory
	err = Loaders_Alloc(pSong, (void**) &pLHdr, sizeof(*pLHdr));
	if (err) goto loader_err;

	// is it an Asylum file?
	err = FileLoad_SetPos(pSong, 0);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, pLHdr, 0x126);
	if (err)
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	if (Loaders_strncmp(&pLHdr->Type[0], &Typestr_Asyl[0], sizeof(pLHdr->Type))
	||  Loaders_strncmp(&pLHdr->Vers[0], &Asyl_Vers[0], sizeof(pLHdr->Vers)))
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	pSong->Flags = 0;
	pSong->MinPitch = NoteDelta;
	pSong->MaxPitch = NoteDelta + 12 * 8 - 1;

	// ... file type ptr
	pSong->pType = Typestr_Asyl;
	pSong->Version = 100;
	pSong->Channels = 8;
	pSubSong->Defaults.Speed = pLHdr->Speed;
	pSubSong->Defaults.Tempo = pLHdr->Tempo;
	pSong->Samples = pLHdr->Samples;
	if (!pSong->Samples || (pSong->Samples > 64))
	{
		err = Loaders_InvalidSong(pSong, 22, &pSong->Samples, pSong->Samples);
		goto loader_err;
	}
	pSong->Patterns = pLHdr->Patterns;
	if (!pSong->Patterns)
	{
		err = Loaders_InvalidSong(pSong, 23, &pSong->Patterns, pSong->Patterns);
		goto loader_err;
	}
	pSubSong->SeqLen = pLHdr->Orders;
	if (!pSubSong->SeqLen)
	{
		err = Loaders_InvalidSong(pSong, 24, &pSubSong->SeqLen, pSubSong->SeqLen);
		goto loader_err;
	}
	pSubSong->RestartPos = pLHdr->RestartPos;

	// Read sequence
	err = Loaders_AllocSeq(pSong, pSubSong);
	if (err) goto loader_err;

	for (i = 0; i < pSubSong->SeqLen; i++)
		pSubSong->pSeqs[i] = pLHdr->Seq[i];

	// Read samples definitions
	for(i = 0, pSmp = pSong->pSamples; i < 64; i++, pSmp++)
	{
		pSmp->Type = 0;
		pSmp->Frequency = 8363;

		// read sample name
		err = FileLoad_ReadString(pSong, &pSmp->pName, 22, false);
		if (err) goto loader_err;

		// read fine tune
		err = FileLoad_ReadByte(pSong, &val);
		if (err) return err;
		pSmp->FineTune = (val & 0xf) << 28;

		// read sample volume
		err = FileLoad_ReadByte(pSong, &pSmp->DefaultVolume);
		if (err) goto loader_err;
		if (pSmp->DefaultVolume > 64) pSmp->DefaultVolume = 256;
		else pSmp->DefaultVolume <<= 2;

		// read key offset?
		err = FileLoad_ReadByte(pSong, &val);
		if (err) return err;
		pSmp->RelTone = (val << 24);
		pSmp->RelTone >>= 16;

		// read sample size
		err = FileLoad_ReadInt(pSong, &pSmp->Size, 4);
		if (err) goto loader_err;

		// read sample loop start
		err = FileLoad_ReadInt(pSong, &pSmp->LoopStart, 4);
		if (err) goto loader_err;

		// read sample loop size
		err = FileLoad_ReadInt(pSong, &pSmp->LoopEnd, 4);
		if (err) goto loader_err;
		if (pSmp->LoopEnd > 2)
		{
			pSmp->LoopEnd += pSmp->LoopStart;
			pSmp->Type |= Smp_Type_Loop;
		}
	}

	// Read patterns
	err = Loaders_Alloc(pSong, (void**) &pLTrack, 4*64*pSong->Channels);
	if (err) goto loader_err;

	for (i = 0, pPattern = pSong->pPatterns; i < pSong->Patterns; i++, pPattern++)
	{
		pPattern->Rows = 64;

		err = FileLoad_Read(pSong, pLTrack, 4*64*pSong->Channels);
		if (err) goto loader_err;

		err = Pattern_Asylum(pSong, i, pPattern, pLTrack);
		if (err) goto loader_err;
	}

	// Read samples data
	for (pSmp = pSong->pSamples, i = 0; i < pSong->Samples; i++, pSmp++)
	{
		err = FileLoad_ReadSample(pSong, pSmp);
		if (err) goto loader_err;
	}

loader_err:
	Loaders_Free(pSong, pLHdr);
	Loaders_Free(pSong, pLTrack);

	return err;
}
