#include "Song.h"
#include "Effects.h"
#include "Instrument.h"
#include "Loaders.h"
#include "FSysLog.h"
#include <string.h>

/**
 * Channels: 8
 * Patterns: [1]
 * Rows    : [1...] (even if pattern is always stored as block of 64 rows)
 * Orders  : [1]
 * Samples : [1...8]
 * Instr.  : 0
 * Tempo   : [40...210] beats per minute
 * Speed   : [1]
 * Notes   : [C-0...C-2...B-4]
 * Pitch   : Uses frequency fraction: 100 is C-1, 200 C-2, 400 C-3, ...
 *           Slides in x/1 units per frame.
 *           See remark below on pitch limits.
 * Volume  : [0...7], log.
 * Panning : [0...6].
 * Sampling: 8300 Hz
 */

#define NoteDelta   (Note_Central-28)

static const uint8_t Tag_Maestro[] = "Maestro\x0a";
static const uint8_t Typestr_Maestro[] = "Maestro";
static const char NotBasicInt[] = "Not a basic Integer";
static const char InvalidStaves[] = "More than 4 staves defined";

static const uint8_t Tempo[15] = {40, 50, 60, 65, 70, 80, 90, 100, 115, 130, 145, 160, 175, 190, 210};
static const uint32_t Stereo[7] = {0, 42, 85, 128, 171, 214, 256};

static const _kernel_oserror* basicint(SongHdr* pSong, uint32_t* pval)
{
	const _kernel_oserror* err;

	err = FileLoad_ReadByte(pSong, pval);
	if (!err && (*pval != 0x40)) err = Loaders_Error(pSong, -1, NotBasicInt);
	if (!err) err = FileLoad_ReadReverseInt(pSong, pval, 4);

	return err;
}

const _kernel_oserror* Loader_Maestro(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*        pSubSong = Song_GetSubSong(pSong, 0);
	Pattern*        pPattern;
	uint32_t        FileSize;
	int             i, k;
	uint8_t         header[9];
	uint8_t*        pGates = NULL;
	const uint8_t*  pGatesStart = NULL;
	const uint8_t*  pGatesEnd = NULL;
    uint8_t*        pNotes[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
    const uint8_t*  pNotesStart[8];
    const uint8_t*  pNotesEnd[8];
    uint32_t        val;
    uint32_t        voice[8];
    uint32_t        midi[8];
	uint32_t        staves_music = 256 /* invalid value */, staves_percussion = 0;
	uint32_t        loadedBlocks = 0;


	const uint8_t Key_Sig[16][7]
	= { {0, 0, 0, 0, 0, 0, 0}
	  , {0, 0, 0, 0, 0, 0, 0}
	  , {0, 0, 0, 2, 0, 0, 0}
	  , {0, 0, 0, 0, 0, 0, 3}
	  , {2, 0, 0, 2, 0, 0, 0}
	  , {3, 0, 0, 0, 0, 0, 3}
	  , {2, 0, 0, 2, 0, 0, 0}
	  , {3, 0, 0, 0, 0, 0, 3}
	  , {2, 0, 0, 2, 0, 0, 0}
	  , {3, 0, 0, 0, 0, 0, 3}
	  , {2, 0, 0, 2, 0, 0, 0}
	  , {3, 0, 0, 0, 0, 0, 3}
	  , {2, 0, 0, 2, 0, 0, 0}
	  , {3, 0, 0, 0, 0, 0, 3}
	  , {2, 0, 0, 2, 0, 0, 0}
	  , {3, 0, 0, 0, 0, 0, 3}
	  };
	const uint8_t Stave_Channels[4][8]
	= { {0, 0, 0, 0, 0, 0, 0, 0}
	  , {0, 0, 0, 0, 1, 1, 1, 1}
	  , {0, 1, 1, 1, 1, 2, 2, 2}
	  , {0, 0, 1, 1, 2, 2, 3, 3}
	  };
#define MAX_STAVES 4
	const uint8_t  Ninc[7] = {0, 2, 4, 5, 7, 9, 11}; // notes semintone increment
	const int8_t   Aoff[8] = {0, 0, 1, -1, 2, -2, 1, -1}; // Accidental offsets in semitones
	const int8_t   Clef[4] = {11, 5, 3, -1};
	const uint32_t Length[32] // type [breve...hemidemisemiquaver] << 2 + dots[0...3]
	= {1024, 1536, 1792, 1920 // in 1/128 of a beat
	  , 512,  768,  896,  960
	  , 256,  384,  448,  480
	  , 128,  192,  224,  240
	  ,  64,   96,  112,  120
	  ,  32,   48,   56,   60
	  ,  16,   24,   28,   30
	  ,   8,   12,   14,   15
	  };
	uint8_t Key[7]
	= { Key_Sig[0][0], Key_Sig[0][1], Key_Sig[0][2], Key_Sig[0][3]
	  , Key_Sig[0][4], Key_Sig[0][5], Key_Sig[0][6]
	  };
	uint8_t Accidental[4][32]
	= { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	  , {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	  , {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	  , {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	};
	uint8_t S_C[8] = {0, 0, 0, 0, 0, 0, 0, 0};
	int8_t PCLEF[4] = {Clef[0], Clef[0], Clef[0], Clef[0]}; // clef for staves
	uint32_t TIE = 0xff;


	// Reject file to short to contain header
	FileSize = FileLoad_GetSize(pSong);
	if (FileSize < 10)
		return Loaders_NotThisType;

	// load header
	err = FileLoad_SetPos(pSong, 0);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, header, 8);
	if (err) goto loader_err;

	// check if really a Maestro Module?
	if (memcmp(header, Tag_Maestro, 8))
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	// set version
	pSong->pType = Typestr_Maestro;

   	err = FileLoad_ReadByte(pSong, &val);
   	if (err) goto loader_err;
   	if ((val == 0) || (val > 2))
   	{
		err = Loaders_NotThisType;
		goto loader_err;
	}
	pSong->Version = val * 100;

	pSong->Flags = 0;
	pSong->MinPitch = NoteDelta - 2;
	pSong->MaxPitch = NoteDelta + 7*12;
	pSong->MinTempo = Tempo[0];
	pSong->MaxTempo = Tempo[14];
	pSong->MinSpeed = 1;
	pSong->MaxSpeed = 3*Length[3] / 16;

	pSong->Samples  = 33;
	pSong->Channels = 8;

	if (pSong->Version == 100)
	{
		// Tempo
		// Instruments
		// Staves
		// Music
	}
	else
	{
		uint32_t block, val;

		while (FileLoad_GetPos(pSong) < FileSize)
		{
    		err = FileLoad_ReadByte(pSong, &block);
	    	if (err) goto loader_err;

			if (block > 9)
			{
				err = Loaders_Error(pSong, -1, "Unexpected block %d", block);
				goto loader_err;
			}

			if (loadedBlocks & (1 << block))
			{
				SysLog_FileCorrupted(SysLog_High, pSong, -1, "Duplicate block %d", block);
				break;
			}
			loadedBlocks |= 1 << block;

    	    switch(block)
        	{
				case 1: // Music
				{
					if (pGates != NULL)
					{
						err = Loaders_Error(pSong, -1, "Music block already defined");
						goto loader_err;
					}
					err = basicint(pSong, &val);
					if (err) goto loader_err;
					err = Loaders_Alloc(pSong, (void**) &pGates, val);
					if (err) goto loader_err;
					pGatesStart = pGates;
					pGatesEnd   = pGates + val;

					for (k = 0; k < 8; k++)
					{
						err = basicint(pSong, &val);
						if (err) goto loader_err;
						err = Loaders_Alloc(pSong, (void**) &pNotes[k], val);
						if (err) goto loader_err;
						pNotesStart[k] = pNotes[k];
						pNotesEnd[k]   = pNotes[k] + val;
					}

					err = FileLoad_Read(pSong, pGates, pGatesEnd - pGatesStart);
					if (err) goto loader_err;

					for (k = 0; k < 8; k++)
					{
						err = FileLoad_Read(pSong, pNotes[k], pNotesEnd[k] - pNotesStart[k]);
						if (err) goto loader_err;
					}
				}
				break;
				case 2: // Staves
				{
					err = FileLoad_ReadByte(pSong, &staves_music);
					if (err) goto loader_err;
					if (staves_music > 3)
					{
						err = Loaders_Error(pSong, -1, InvalidStaves);
						goto loader_err;
					}

					err = FileLoad_ReadByte(pSong, &staves_percussion);
					if (err) goto loader_err;

					for (k = 0; k < 8; k++)
						S_C[k] = Stave_Channels[staves_music][k];
				}
				break;
				case 3: // Instruments
				{
					for (k = 0; k < 8; k++)
					{
						err = FileLoad_ReadByte(pSong, &val);
						if (err) goto loader_err;
						err = FileLoad_ReadByte(pSong, &voice[k]);
						if (err) goto loader_err;
					}
				}
				break;
				case 4: // Volume
				{
					for (k = 0; k < 8; k++)
					{
						err = FileLoad_ReadByte(pSong, &val);
						if (err) goto loader_err;
						if (val > 7)
							val = 7;
						val++;
						pSubSong->Defaults.ChDef[k].Volume = val << 5;
					}
				}
				break;
				case 5: // Stereo
				{
					for (k = 0; k < 8; k++)
					{
						err = FileLoad_ReadByte(pSong, &val);
						if (err) goto loader_err;
						if (val > 6)
							val = 6;
						pSubSong->Defaults.ChDef[k].Panning = Stereo[val];
					}
				}
				break;
				case 6: // Tempo
				{
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					if (val > 14) val = 14;
					pSubSong->Defaults.Tempo = Tempo[val]; // cf. tempo base
				}
				break;
				case 7: // Title
				{
        	        err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					if (val > 0)
					{
						err = FileLoad_ReadString(pSong, &pSong->pName, val, false);
						if (err) goto loader_err;
					}
				}
				break;
				case 8: // Instruments names
				{
					for (k = 0; k < 8; k++)
					{
	            	    err = FileLoad_ReadByte(pSong, &val);
						if (err) goto loader_err;
						if (val > 0)
						{
							err = FileLoad_ReadString(pSong, &pSong->pSamples[voice[k]].pName, val, false);
							if (err) goto loader_err;
						}
					}
				}
				break;
				case 9: // Midi channels
				{
					for (k = 0; k < 8; k++)
					{
						err = FileLoad_ReadByte(pSong, &midi[k]);
						if (err) goto loader_err;
					}
				}
				break;

	        }
		}
	}

	if (((loadedBlocks & 0x4E) != 0x4E) // Music + Staves + Instruments + Tempo
	||  (pGates == NULL)
	||  (staves_music > 3))
	{
		err = Loaders_InvalidSong(pSong, 0, &pSong->pType, 0);
		goto loader_err;
	}

	// Fill the patterns
	// Due to effects cancelling previous effects,
	// we will rewite the sequence and patterns.

	pSong->Patterns = 0;

	for (pPattern = pSong->pPatterns; pGatesStart < pGatesEnd; pPattern++)
	{
		pPattern->Rows = 0;
		err = Loaders_StartPattern(pSong, pPattern - pSong->pPatterns);
		if (err) return err;

		while (pGatesStart < pGatesEnd)
		{
			val = *pGatesStart++;
			if (val)
			{
				// frame duration is min duration of notes/rest on row
				uint32_t speed = 0xffff;

				err = Loaders_StartRow(pSong, pPattern->Rows);
				if (err) return err;
				pPattern->Rows++;

				// Notes or rest
				for (k = 0; k < 8; k++, val >>= 1)
				{
					if (val & 0x01)
					{
						uint32_t Note = 0;
						uint32_t hold = 0;

						if (pNotesStart[k] + 1 < pNotesEnd[k])
						{
							uint32_t note = *pNotesStart[k]++;
							note += (*pNotesStart[k]++) << 8;

							uint32_t l = (note >> 3) & 0x1f;
							uint32_t acc = (note >> 8) & 0x7;
							hold = 3*Length[note >> 11]; // (type & dots), in 1/128 beat
							uint32_t s = S_C[k];

							if (l || acc)
							{
								// Note
								if (l && (s <= staves_music))
								{
									if (acc)
										Accidental[s][l] = acc;
									else
										acc = Accidental[s][l];

									l += PCLEF[s];
									if (!acc)
										acc = Key[l % 7];
								}

								if (TIE & (1<<k))
								{
									if ((l > 42) || (acc > 7))
									{
										SysLog_BadNote(pSong, 0, k, l);
									}
									else if (l)
									{

										Note = 12*(l / 7) + Ninc[l % 7] + Aoff[acc] + NoteDelta;
//SysLog_Log(0, "l %2d, acc %2d, A %2d, P %2d, K %2d -> Note %2d", (note >> 3) & 0x1f, (note >> 8) & 0x7
// , Accidental[s][l], PCLEF[s], Key[l % 7], Note);
									}

									// basic line 27410
									if (note & 4)
										TIE &= ~(1 << k);
								}
								else
								{
									if (~note & 4)
										TIE |= (1 << k);
								}
							}
							else
							{
								// Rest
							}
						}

						if (hold)
						{
							if (speed > hold)
								speed = hold;
						}

						err = Loaders_StartChannel(pSong, k, Note, Note ? voice[k] : 0);
						if (err) return err;

						if (Note)
						{
							// in 1/128 beat but we multiple the bpm tempo by 8, so must devide by 16
							hold = (hold + 8) / 16;
							if (!hold) hold = 1;
							Pattern_AddEffect_Instrument(pSong, cmd_sample_hold, hold);
						}

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

				if (speed)
				{
					// in 1/128 beat but we multiple the bpm tempo by 8, so must devide by 16
					speed = (speed + 8) / 16;
					if (!speed) speed = 1;
					Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, speed);
				}

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

				if (pPattern->Rows >= 32)
					break; // exit to outer loop to force new pattern
			}
			else
			{
				// Attribute (for next bar? cf Beats% delay)
				if (pGatesStart < pGatesEnd)
				{
					val = *pGatesStart++;
//SysLog_Log(0, "attribute %x", val);

					if (val & 1)
					{
						// Time Signature
						uint32_t beatsperbar = (val >> 1) & 0xf;
						uint32_t beattype = val >> 5;  // brev -> hemidemisemiquaver
						SysLog_Log(SysLog_Medium, "Beats per bar: %d beattype: %d", beatsperbar + 1, beattype);
					}
					else if (val & 2)
					{
						// Key Signature
						for (int a = 0; a < 7; a++)
							Key[a] = Key_Sig[(val >> 2) & 0xf][a];
					}
					else if (val & 4)
					{
						// Clef
						uint32_t clef_type = (val >> 3) & 3; // 0 treble, 1 alto, 2 tenor, 3 bass
						uint32_t stave = val >> 6;
						PCLEF[stave] = Clef[clef_type];
					}
					else if (val & 8)
					{
						// Slur, ignored in player
						uint32_t sluron = (val >> 4) & 1; // 0 off, 1 on
						uint32_t stave = val >> 6;
						SysLog_Log(SysLog_Medium, "Slur: %d on stave %d", sluron, stave);
					}
					else if (val & 0x10)
					{
						// Octave shift, ignored in player
						uint32_t shift = (val >> 5) & 1; // 0 up, 1 down
						SysLog_Log(SysLog_Medium, "Octave %s", shift ? "down" : "up");
					}
					else if (val & 0x20)
					{
						// Bar
					}
				}
				else pGatesStart = pGatesEnd;
			}
		}

		err = Loaders_EndPattern(pSong, pPattern);
		if (err) goto loader_err;

		if (pPattern->Rows > 0)
			pSong->Patterns++;

		if (pSong->Patterns >= song_max_patterns)
		{
			err = Loaders_InvalidSong(pSong, 0, &pSong->Patterns, pSong->Patterns);
			goto loader_err;
		}
	}

	if (pSong->Patterns == 0)
	{
		err = Loaders_InvalidSong(pSong, 0, &pSong->pType, 0);
		goto loader_err;
	}

	// fill the pattern sequence
	pSubSong->SeqLen = pSong->Patterns;
	err = Loaders_AllocSeq(pSong, pSubSong);
	if (err) goto loader_err;
	for (i = 0; i < pSubSong->SeqLen; i++)
		pSubSong->pSeqs[i] = i;

loader_err:
	for (k = 0; k < 8; k++)
		Loaders_Free(pSong, pNotes[k]);

	Loaders_Free(pSong, pGates);

	return err;
}
