/*
 * ITTools.h
 * ---------
 * Purpose: Definition of IT file structures and helper functions
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */


#pragma once

#include "openmpt/all/BuildSettings.hpp"
#include "openmpt/base/Endian.hpp"
#include "Snd_defs.h"

OPENMPT_NAMESPACE_BEGIN

struct InstrumentEnvelope;
struct ModInstrument;
struct ModSample;
class CSoundFile;
class SampleIO;

struct ITFileHeader
{
	// Header Flags
	enum ITHeaderFlags
	{
		useStereoPlayback      = 0x01,
		vol0Optimisations      = 0x02,
		instrumentMode         = 0x04,
		linearSlides           = 0x08,
		itOldEffects           = 0x10,
		itCompatGxx            = 0x20,
		useMIDIPitchController = 0x40,
		reqEmbeddedMIDIConfig  = 0x80,
		extendedFilterRange    = 0x1000,
	};

	// Special Flags
	enum ITHeaderSpecialFlags
	{
		embedSongMessage       = 0x01,
		embedEditHistory       = 0x02,
		embedPatternHighlights = 0x04,
		embedMIDIConfiguration = 0x08,
	};

	char     id[4];            // Magic Bytes (IMPM)
	char     songname[26];     // Song Name, null-terminated (but may also contain nulls)
	uint8le  highlight_minor;  // Rows per Beat highlight
	uint8le  highlight_major;  // Rows per Measure highlight
	uint16le ordnum;           // Number of Orders
	uint16le insnum;           // Number of Instruments
	uint16le smpnum;           // Number of Samples
	uint16le patnum;           // Number of Patterns
	uint16le cwtv;             // "Made With" Tracker
	uint16le cmwt;             // "Compatible With" Tracker
	uint16le flags;            // Header Flags
	uint16le special;          // Special Flags, for embedding extra information
	uint8le  globalvol;        // Global Volume (0...128)
	uint8le  mv;               // Master Volume (0...128), referred to as Sample Volume in OpenMPT
	uint8le  speed;            // Initial Speed (1...255)
	uint8le  tempo;            // Initial Tempo (31...255)
	uint8le  sep;              // Pan Separation (0...128)
	uint8le  pwd;              // Pitch Wheel Depth
	uint16le msglength;        // Length of Song Message
	uint32le msgoffset;        // Offset of Song Message in File (IT crops message after first null)
	uint32le reserved;         // Some IT versions save an edit timer here. ChibiTracker writes "CHBI" here. OpenMPT and Schism Tracker save extended version information here.
	uint8le  chnpan[64];       // Initial Channel Panning
	uint8le  chnvol[64];       // Initial Channel Volume
};

MPT_BINARY_STRUCT(ITFileHeader, 192)


struct ITEnvelope
{
	// Envelope Flags
	enum ITEnvelopeFlags
	{
		envEnabled = 0x01,
		envLoop    = 0x02,
		envSustain = 0x04,
		envCarry   = 0x08,
		envFilter  = 0x80,
	};

	struct Node
	{
		int8le   value;
		uint16le tick;
	};

	uint8 flags;     // Envelope Flags
	uint8 num;       // Number of Envelope Nodes
	uint8 lpb;       // Loop Start
	uint8 lpe;       // Loop End
	uint8 slb;       // Sustain Start
	uint8 sle;       // Sustain End
	Node  data[25];  // Envelope Node Positions / Values
	uint8 reserved;  // Reserved

	// Convert OpenMPT's internal envelope format to an IT/MPTM envelope.
	void ConvertToIT(const InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 envDefault);
	// Convert IT/MPTM envelope data into OpenMPT's internal envelope format - To be used by ITInstrToMPT()
	void ConvertToMPT(InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 maxNodes) const;
};

MPT_BINARY_STRUCT(ITEnvelope::Node, 3)
MPT_BINARY_STRUCT(ITEnvelope, 82)


// Old Impulse Instrument Format (cmwt < 0x200)
struct ITOldInstrument
{
	enum ITOldInstrFlags
	{
		envEnabled = 0x01,
		envLoop    = 0x02,
		envSustain = 0x04,
	};

	char     id[4];          // Magic Bytes (IMPI)
	char     filename[13];   // DOS Filename, null-terminated
	uint8le  flags;          // Volume Envelope Flags
	uint8le  vls;            // Envelope Loop Start
	uint8le  vle;            // Envelope Loop End
	uint8le  sls;            // Envelope Sustain Start
	uint8le  sle;            // Envelope Sustain End
	char     reserved1[2];   // Reserved
	uint16le fadeout;        // Instrument Fadeout (0...128)
	uint8le  nna;            // New Note Action
	uint8le  dnc;            // Duplicate Note Check Type
	uint16le trkvers;        // Tracker ID
	uint8le  nos;            // Number of embedded samples
	char     reserved2;      // Reserved
	char     name[26];       // Instrument Name, null-terminated (but may also contain nulls)
	char     reserved3[6];   // Even more reserved bytes
	uint8le  keyboard[240];  // Sample / Transpose map
	uint8le  volenv[200];    // This appears to be a pre-computed (interpolated) version of the volume envelope data found below.
	uint8le  nodes[25 * 2];  // Volume Envelope Node Positions / Values

	// Convert an ITOldInstrument to OpenMPT's internal instrument representation.
	void ConvertToMPT(ModInstrument &mptIns) const;
};

MPT_BINARY_STRUCT(ITOldInstrument, 554)


// Impulse Instrument Format
struct ITInstrument
{
	enum ITInstrumentFlags : uint8
	{
		ignorePanning   = 0x80,
		enableCutoff    = 0x80,
		enableResonance = 0x80,
	};

	char     id[4];          // Magic Bytes (IMPI)
	char     filename[13];   // DOS Filename, null-terminated
	uint8le  nna;            // New Note Action
	uint8le  dct;            // Duplicate Note Check Type
	uint8le  dca;            // Duplicate Note Check Action
	uint16le fadeout;        // Instrument Fadeout (0...256, although values up to 1024 would be sensible. Up to IT2.07, the limit was 0...128)
	int8le   pps;            // Pitch/Pan Separatation
	uint8le  ppc;            // Pitch/Pan Centre
	uint8le  gbv;            // Global Volume
	uint8le  dfp;            // Panning
	uint8le  rv;             // Vol Swing
	uint8le  rp;             // Pan Swing
	uint16le trkvers;        // Tracker ID
	uint8le  nos;            // Number of embedded samples
	char     reserved1;      // Reserved
	char     name[26];       // Instrument Name, null-terminated (but may also contain nulls)
	uint8le  ifc;            // Filter Cutoff
	uint8le  ifr;            // Filter Resonance
	uint8le  mch;            // MIDI Channel
	uint8le  mpr;            // MIDI Program
	uint8le  mbank[2];       // MIDI Bank
	uint8le  keyboard[240];  // Sample / Transpose map
	ITEnvelope volenv;       // Volume Envelope
	ITEnvelope panenv;       // Pan Envelope
	ITEnvelope pitchenv;     // Pitch / Filter Envelope
	char       dummy[4];     // IT saves some additional padding bytes to match the size of the old instrument format for simplified loading. We use them for some hacks.

	// Convert OpenMPT's internal instrument representation to an ITInstrument. Returns amount of bytes that need to be written.
	uint32 ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile);
	// Convert an ITInstrument to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read.
	uint32 ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const;
};

MPT_BINARY_STRUCT(ITInstrument, 554)


// MPT IT Instrument Extension
struct ITInstrumentEx
{
	ITInstrument iti;       // Normal IT Instrument
	uint8 keyboardhi[120];  // High Byte of Sample map
	
	// Convert OpenMPT's internal instrument representation to an ITInstrumentEx. Returns amount of bytes that need to be written.
	uint32 ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile);
	// Convert an ITInstrumentEx to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read.
	uint32 ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const;
};

MPT_BINARY_STRUCT(ITInstrumentEx, sizeof(ITInstrument) + 120)


// IT Sample Format
struct ITSample
{
	enum ITSampleFlags : uint8
	{
		sampleDataPresent   = 0x01,
		sample16Bit         = 0x02,
		sampleStereo        = 0x04,
		sampleCompressed    = 0x08,
		sampleLoop          = 0x10,
		sampleSustain       = 0x20,
		sampleBidiLoop      = 0x40,
		sampleBidiSustain   = 0x80,

		enablePanning       = 0x80,

		cvtSignedSample     = 0x01,
		cvtOPLInstrument    = 0x40,  // FM instrument in MPTM
		cvtExternalSample   = 0x80,  // Keep MPTM sample on disk
		cvtADPCMSample      = 0xFF,  // MODPlugin :(

		// ITTECH.TXT says these convert flags are "safe to ignore". IT doesn't ignore them, though, so why should we? :)
		cvtBigEndian        = 0x02,
		cvtDelta            = 0x04,
		cvtPTM8to16         = 0x08,
	};

	char     id[4];          // Magic Bytes (IMPS)
	char     filename[13];   // DOS Filename, null-terminated
	uint8le  gvl;            // Global Volume
	uint8le  flags;          // Sample Flags
	uint8le  vol;            // Default Volume
	char     name[26];       // Sample Name, null-terminated (but may also contain nulls)
	uint8le  cvt;            // Sample Import Format
	uint8le  dfp;            // Sample Panning
	uint32le length;         // Sample Length (in samples)
	uint32le loopbegin;      // Sample Loop Begin (in samples)
	uint32le loopend;        // Sample Loop End (in samples)
	uint32le C5Speed;        // C-5 frequency
	uint32le susloopbegin;   // Sample Sustain Begin (in samples)
	uint32le susloopend;     // Sample Sustain End (in samples)
	uint32le samplepointer;  // Pointer to sample data
	uint8le  vis;            // Auto-Vibrato Rate (called Sweep in IT)
	uint8le  vid;            // Auto-Vibrato Depth
	uint8le  vir;            // Auto-Vibrato Sweep (called Rate in IT)
	uint8le  vit;            // Auto-Vibrato Type

	// Convert OpenMPT's internal sample representation to an ITSample.
	void ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compress, bool compressIT215, bool allowExternal);
	// Convert an ITSample to OpenMPT's internal sample representation.
	uint32 ConvertToMPT(ModSample &mptSmp) const;
	// Retrieve the internal sample format flags for this instrument.
	SampleIO GetSampleFormat(uint16 cwtv = 0x214) const;
};

MPT_BINARY_STRUCT(ITSample, 80)


struct FileHistory;

// IT Header extension: Save history
struct ITHistoryStruct
{
	uint16le fatdate;  // DOS / FAT date when the file was opened / created in the editor. For details, read https://docs.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime
	uint16le fattime;  // DOS / FAT time when the file was opened / created in the editor.
	uint32le runtime;  // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer)

	// Convert an ITHistoryStruct to OpenMPT's internal edit history representation
	void ConvertToMPT(FileHistory &mptHistory) const;
	// Convert OpenMPT's internal edit history representation to an ITHistoryStruct
	void ConvertToIT(const FileHistory &mptHistory);

};

MPT_BINARY_STRUCT(ITHistoryStruct, 8)


enum IT_ReaderBitMasks
{
	// pattern row parsing, the channel data is read to obtain
	// number of channels active in the pattern. These bit masks are
	// to blank out sections of the byte of data being read.

	IT_bitmask_patternChanField_c   = 0x7f,
	IT_bitmask_patternChanMask_c    = 0x3f,
	IT_bitmask_patternChanEnabled_c = 0x80,
	IT_bitmask_patternChanUsed_c    = 0x0f
};


// Calculate Schism Tracker version field for IT / S3M header based on specified release date
// Date calculation derived from https://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
template<int32 y, int32 m, int32 d>
struct SchismVersionFromDate
{
private:
	static constexpr int32 mm = (m + 9) % 12;
	static constexpr int32 yy = y - mm / 10;

public:
	static constexpr int32 date = yy * 365 + yy / 4 - yy / 100 + yy / 400 + (mm * 306 + 5) / 10 + (d - 1);
};

inline constexpr int32 SchismTrackerEpoch = SchismVersionFromDate<2009, 10, 31>::date;


uint32 DecodeITEditTimer(uint16 cwtv, uint32 editTime);

OPENMPT_NAMESPACE_END
