#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "ft2_unicode.h"
#include "mixer/ft2_windowed_sinc.h"

enum
{
	// voice flags
	IS_Vol = 1, // set volume
	IS_Period = 2, // set resampling rate
	IS_NyTon = 4, // trigger new sample
	IS_Pan = 8, // set panning
	IS_QuickVol = 16, // 5ms volramp instead of tick ms

	LOOP_DISABLED = 0,
	LOOP_FORWARD = 1,
	LOOP_PINGPONG = 2,

	// tracker playback modes
	PLAYMODE_IDLE = 0,
	PLAYMODE_EDIT = 1,
	PLAYMODE_SONG = 2,
	PLAYMODE_PATT = 3,
	PLAYMODE_RECSONG = 4,
	PLAYMODE_RECPATT = 5,

	// note cursor positions
	CURSOR_NOTE = 0,
	CURSOR_INST1 = 1,
	CURSOR_INST2 = 2,
	CURSOR_VOL1 = 3,
	CURSOR_VOL2 = 4,
	CURSOR_EFX0 = 5,
	CURSOR_EFX1 = 6,
	CURSOR_EFX2 = 7
};

// DO NOT TOUCH!
#define MIN_BPM 1
#define MAX_BPM 999
#define MAX_VOICES 32
#define TRACK_WIDTH (5 * MAX_VOICES)
#define MAX_NOTES ((12 * 10 * 16) + 16)
#define MAX_PATTERNS 256
#define MAX_PATT_LEN 256
#define MAX_INST 128
#define MAX_SMP_PER_INST 16
#define MAX_ORDERS 256
#define STD_ENV_SIZE ((6*2*12*2*2) + (6*8*2) + (6*5*2) + (6*2*2))
#define INSTR_HEADER_SIZE 263
#define INSTR_XI_HEADER_SIZE 298
#define MAX_SAMPLE_LEN 0x3FFFFFFF
#define PROG_NAME_STR "Fasttracker II clone"

/* Some of the following structs must be packed (e.g. not padded) since they
** are loaded directly into with fread and stuff.
*/

#ifdef _MSC_VER
#pragma pack(push)
#pragma pack(1)
#endif
typedef struct songHeaderTyp_t // DO NOT TOUCH!
{
	char sig[17], name[21], progName[20];
	uint16_t ver;
	int32_t headerSize;
	uint16_t len, repS, antChn, antPtn, antInstrs, flags, defTempo, defSpeed;
	uint8_t songTab[256];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
songHeaderTyp;

typedef struct patternHeaderTyp_t // DO NOT TOUCH!
{
	int32_t patternHeaderSize;
	uint8_t typ;
	int16_t pattLen;
	uint16_t dataLen;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
patternHeaderTyp;

typedef struct songMODInstrHeaderTyp_t
{
	char name[22];
	uint16_t len;
	uint8_t fine, vol;
	uint16_t repS, repL;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
songMODInstrHeaderTyp;

typedef struct songMOD15HeaderTyp_t
{
	char name[20];
	songMODInstrHeaderTyp instr[15];
	uint8_t len, repS, songTab[128];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
songMOD15HeaderTyp;

typedef struct songMOD31HeaderTyp_t
{
	char name[20];
	songMODInstrHeaderTyp instr[31];
	uint8_t len, repS, songTab[128];
	char sig[4];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
songMOD31HeaderTyp;

typedef struct sampleHeaderTyp_t // DO NOT TOUCH!
{
	int32_t len, repS, repL;
	uint8_t vol;
	int8_t fine;
	uint8_t typ, pan;
	int8_t relTon;
	uint8_t nameLen;
	char name[22];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
sampleHeaderTyp;

typedef struct instrHeaderTyp_t // DO NOT TOUCH!
{
	uint32_t instrSize;
	char name[22];
	uint8_t typ;
	int16_t antSamp;
	int32_t sampleSize;
	uint8_t ta[96];
	int16_t envVP[12][2], envPP[12][2];
	uint8_t envVPAnt, envPPAnt;
	uint8_t envVSust, envVRepS, envVRepE;
	uint8_t envPSust, envPRepS, envPRepE;
	uint8_t envVTyp, envPTyp;
	uint8_t vibTyp, vibSweep, vibDepth, vibRate;
	uint16_t fadeOut;
	uint8_t midiOn, midiChannel;
	int16_t midiProgram, midiBend;
	int8_t mute;
	uint8_t reserved[15];
	sampleHeaderTyp samp[16];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
instrHeaderTyp;

#ifdef _MSC_VER
#pragma pack(pop)
#endif

typedef struct sampleTyp_t
{
	char name[22+1];
	bool fixed;
	int8_t fine, relTon, *pek, *origPek;
	uint8_t vol, typ, pan;
	int32_t len, repS, repL;

	// fix for resampling interpolation taps
	int8_t leftEdgeTapSamples8[SINC_TAPS+SINC_LEFT_TAPS];
	int16_t leftEdgeTapSamples16[SINC_TAPS+SINC_LEFT_TAPS];
	int16_t fixedSmp[SINC_RIGHT_TAPS];
	int32_t fixedPos;
} sampleTyp;

typedef struct instrTyp_t
{
	bool midiOn, mute;
	uint8_t midiChannel, ta[96];
	uint8_t envVPAnt, envPPAnt;
	uint8_t envVSust, envVRepS, envVRepE;
	uint8_t envPSust, envPRepS, envPRepE;
	uint8_t envVTyp, envPTyp;
	uint8_t vibTyp, vibSweep, vibDepth, vibRate;
	uint16_t fadeOut;
	int16_t envVP[12][2], envPP[12][2], midiProgram, midiBend;
	int16_t antSamp; // used by loader only
	sampleTyp samp[16];
} instrTyp;

typedef struct stmTyp_t
{
	bool envSustainActive, stOff, mute;
	volatile uint8_t status, tmpStatus;
	int8_t relTonNr, fineTune;
	uint8_t sampleNr, instrNr, effTyp, eff, smpOffset, tremorSave, tremorPos;
	uint8_t globVolSlideSpeed, panningSlideSpeed, waveCtrl, portaDir;
	uint8_t glissFunk, vibPos, tremPos, vibSpeed, vibDepth, tremSpeed, tremDepth;
	uint8_t pattPos, loopCnt, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed;
	uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed;
	uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol;
	uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol;
	uint8_t oldPan, outPan, finalPan;
	int16_t midiPitch;
	uint16_t outPeriod, realPeriod, finalPeriod, tonTyp, wantPeriod, portaSpeed;
	uint16_t envVCnt, envPCnt, eVibAmp, eVibSweep;
	uint16_t fadeOutAmp, fadeOutSpeed, midiVibDepth;
	double dEnvVIPValue, dEnvPIPValue, dEnvVAmp, dEnvPAmp, dFinalVol;
	int32_t oldFinalPeriod, smpStartPos;
	sampleTyp *smpPtr;
	instrTyp *instrPtr;
} stmTyp;

typedef struct songTyp_t
{
	bool pBreakFlag, posJumpFlag, isModified;
	char name[20+1], instrName[1+MAX_INST][22+1];
	uint8_t curReplayerTimer, curReplayerPattPos, curReplayerSongPos, curReplayerPattNr; // used for audio/video sync queue
	uint8_t pattDelTime, pattDelTime2, pBreakPos, songTab[MAX_ORDERS];
	int16_t songPos, pattNr, pattPos, pattLen;
	int32_t antChn;
	uint16_t len, repS, speed, tempo, globVol, timer, ver, initialTempo;
	uint64_t musicTime64;
} songTyp;

typedef struct tonTyp_t
{
	uint8_t ton, instr, vol, effTyp, eff;
} tonTyp;

typedef struct syncedChannel_t // used for audio/video sync queue
{
	bool envSustainActive;
	int8_t fineTune, relTonNr;
	uint8_t status, sampleNr, instrNr;
	uint16_t finalPeriod;
	int32_t smpStartPos;
	double dFinalVol;
} syncedChannel_t;

void setNewSongPos(int32_t pos);
void resetReplayerState(void);

void fixSongName(void); // removes spaces from right side of song name
void fixSampleName(int16_t nr); // removes spaces from right side of ins/smp names
void calcReplayRate(int32_t rate);
void tuneSample(sampleTyp *s, int32_t midCFreq);

void calcRevMixDeltaTable(void);
void calcReplayerLogTab(void);
double dPeriod2Hz(uint16_t period);

int64_t getMixerDelta(uint16_t period);
uint32_t getRevMixerDelta(uint16_t period);

int32_t getPianoKey(uint16_t period, int32_t finetune, int32_t relativeNote); // for piano in Instr. Ed.

bool allocateInstr(int16_t nr);
void freeInstr(int32_t nr);
void freeAllInstr(void);
void freeSample(int16_t nr, int16_t nr2);

void freeAllPatterns(void);
void updateChanNums(void);
bool setupReplayer(void);
void closeReplayer(void);
void resetMusic(void);
void startPlaying(int8_t mode, int16_t row);
void stopPlaying(void);
void stopVoices(void);
void setPos(int16_t songPos, int16_t pattPos, bool resetTimer);
void pauseMusic(void); // stops reading pattern data
void resumeMusic(void); // starts reading pattern data
void setSongModifiedFlag(void);
void removeSongModifiedFlag(void);
void playTone(uint8_t stmm, uint8_t inst, uint8_t ton, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch);
void playSample(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch);
void playRange(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch, int32_t offs, int32_t len);
void keyOff(stmTyp *ch);
void conv8BitSample(int8_t *p, int32_t len, bool stereo);
void conv16BitSample(int8_t *p, int32_t len, bool stereo);
void delta2Samp(int8_t *p, int32_t len, uint8_t typ);
void samp2Delta(int8_t *p, int32_t len, uint8_t typ);
void setPatternLen(uint16_t nr, int16_t len);
void setFrqTab(bool linear);
void tickReplayer(void); // periodically called from audio callback
void resetChannels(void);
bool patternEmpty(uint16_t nr);
int16_t getUsedSamples(int16_t nr);
int16_t getRealUsedSamples(int16_t nr);
void setStdEnvelope(instrTyp *ins, int16_t i, uint8_t typ);
void setNoEnvelope(instrTyp *ins);
void setSyncedReplayerVars(void);
void decSongPos(void);
void incSongPos(void);
void decCurIns(void);
void incCurIns(void);
void decCurSmp(void);
void incCurSmp(void);
void pbPlaySong(void);
void pbPlayPtn(void);
void pbRecSng(void);
void pbRecPtn(void);

// ft2_replayer.c
extern int8_t playMode;
extern bool songPlaying, audioPaused, musicPaused;
extern volatile bool replayerBusy;
extern const uint16_t *note2Period;
extern int16_t pattLens[MAX_PATTERNS];
extern stmTyp stm[MAX_VOICES];
extern songTyp song;
extern instrTyp *instr[132];
extern tonTyp *patt[MAX_PATTERNS];
