// Tape.h: interface for the CTape class.
//


#pragma once

#include "BKSound_Defines.h"
#include <mmsystem.h>

// "RIFF"
constexpr uint32_t RIFF_TAG = 0x46464952;
// "fmt "
constexpr uint32_t FMT_TAG = 0x20746d66;
// "data"
constexpr uint32_t DATA_TAG = 0x61746164;
// "WAVE"
constexpr uint32_t WAVE_TAG = 0x45564157;

struct TAPE_FILE_INFO
{
	size_t              start_tuning;
	size_t              synchro_start;
	size_t              marker1;
	size_t              synchro_header;
	size_t              header;
	uint16_t            address;
	uint16_t            length;
	std::array<uint8_t, 16> name;
	size_t              marker2;
	size_t              synchro_data;
	size_t              data;
	uint16_t            crc;
	size_t              synchro6;
	size_t              end_tuning;
	size_t              end;
};

struct WaveHeader
{
	uint32_t               riffTag;
	uint32_t               size;
	uint32_t               waveTag;
	uint32_t               fmtTag;
	uint32_t               fmtSize;
};

struct DataHeader
{
	uint32_t               dataTag;
	uint32_t               dataSize;
};

class CTape
{
		WAVEFORMATEX    m_WorkingWFX{ WAVE_FORMAT_PCM,  /*wFormatTag;      format type */
			                BUFFER_CHANNELS,            /*nChannels;       number of channels (i.e. mono, stereo...) */
			                DEFAULT_SOUND_SAMPLE_RATE,  /*nSamplesPerSec;  sample rate */
			                0,                          /*nAvgBytesPerSec; for buffer estimation */
			                0,                          /*nBlockAlign;     block size of data */
			                SAMPLE_INT_BPS,             /*wBitsPerSample;  number of bits per sample of mono data */
			                0 }; // текущие рабочие параметры wave

		bool            m_bPlay{ false };
		bool            m_bWaveLoaded{ false };
		SAMPLE_INT     *m_pWave{ nullptr };         // собственно массив wave.
		size_t          m_nWaveMaxLen{ 0 };         // размер массива m_pWave в сэмплах
		size_t          m_nWaveLength{ 0 };         // размер данных в массиве m_pWave в сэмплах (может быть меньше m_nWaveMaxLen)
		size_t          m_nPlayPos{ 0 };            // текущая позиция воспроизведения в сэмплах

		bool            m_bRecord{ false };         // флаг записи
		SAMPLE_INT     *m_pRecord{ nullptr };       // массив, куда записывается звук
		size_t          m_nRecordPos{ 0 };          // текущая позиция в массиве в сэмплах
		size_t          m_nRecordLength{ 0 };       // длина массива в сэмплах

		bool            m_bAutoBeginRecord{ false };
		bool            m_bAutoEndRecord{ false };

		std::unique_ptr<uint8_t[]> m_pBin;			// массив бинарных данных, в которые преобразуются wav и tap
		size_t          m_nPos{ 0 };
		SAMPLE_INT      m_nAverage{ 0.0 };
		int             m_nAvgLength{ 0 };
		double          m_dAvgLength{ 0.0 };
		bool            m_bInverse{ false };

		// функции преобразования модулированного wav в bin
		bool            FindTuning(const int nLength, size_t &wave_pos, size_t &wave_length);
		bool            FindMarker();
		bool            FindMarker1();
		bool            FindSyncro6();
		int             CalcImpLength(const SAMPLE_INT *pWave, size_t &pos, const size_t length) const;
		inline SAMPLE_INT GetCurrentSampleMono(const SAMPLE_INT *pWave, const size_t pos) const;
		void            SetCurrentSampleMono(SAMPLE_INT *pWave, const size_t pos, const SAMPLE_INT sample) const;
		int             DefineLength(const int length) const;
		bool            ReadBit(bool &bBit);
		bool            ReadByte(uint8_t &byte);
		// функции преобразования bin в модулированный wav
		bool            SaveTuning(const int length);
		bool            SaveSyncro6();
		bool            SaveImp(const int size);
		bool            SaveBit(const bool bBit);
		bool            SaveByte(const uint8_t byte);

		size_t          ConvertSamples(const WAVEFORMATEX &wfx_in, const void *inBuf, const size_t nBufSize);
		void            ResampleBuffer(const int nSrcSSR, const int nDstSSR);
		bool            FindRecordBegin(const size_t nSmplBufLen);
		bool            FindRecordEnd(const size_t nSmplBufLen);
		void            CalculateAverage();

	public:
		CTape();
		virtual ~CTape();
		void            SetWaveParam(const int nWorkingSSR = DEFAULT_SOUND_SAMPLE_RATE, const int nWorkingChn = BUFFER_CHANNELS);
		int             GetWorkingSSR() const
		{
			return static_cast<int>(m_WorkingWFX.nSamplesPerSec);
		}
		int             GetWorkingChannels() const
		{
			return m_WorkingWFX.nChannels;
		}
		// Wave pack/unpack methods
		void            PackBits(std::vector<uint8_t> &pPackBits);
		void            UnpackBits(std::vector<uint8_t> const &pPackBits);

		// Wave read/write methods
		bool            SetWaveFile(TAPE_FILE_INFO *pTfi);
		bool            GetWaveFile(TAPE_FILE_INFO *pTfi, const bool bHeaderOnly = false);
		uint16_t        CalcCRC(const TAPE_FILE_INFO *pTfi) const;

		bool            LoadWaveFile(const fs::path &strPath);
		bool            SaveWaveFile(const fs::path &strPath) const;

		bool            LoadBinFile(const fs::path &strPath, TAPE_FILE_INFO *pTfi);
		bool            SaveBinFile(const fs::path &strPath, const TAPE_FILE_INFO *pTfi) const;

		bool            LoadMSFFile(const fs::path &strPath, bool bSilent = false);
		bool            SaveMSFFile(const fs::path &strPath);

		bool            LoadTmpFile(const fs::path &strPath);

		// Wave buffer methods
		bool            AllocWaveBuffer(const size_t nLenInSamples);
		bool            LoadBuffer(const SAMPLE_INT *pBuff, const size_t nLenInSamples);
		inline size_t   GetWaveLength() const
		{
			return m_nWaveLength;
		}
		inline SAMPLE_INT *GetWaveBuffer() const
		{
			return m_pWave;
		}

		// Playing methods
		inline size_t   GetPlayWavePos() const
		{
			return m_nPlayPos;
		}

		void            PlayWaveGetBuffer(SAMPLE_INT *pBuff, const size_t nBufSampleLen);

		inline void     ResetPlayWavePos()
		{
			m_nPlayPos = 0;
		}
		inline void     StartPlay()
		{
			m_bPlay = true;
		}
		inline void     StopPlay()
		{
			m_bPlay = false;
		}
		inline bool     IsPlaying() const
		{
			return m_bPlay;
		}
		inline void     SetWaveLoaded(const bool b)
		{
			m_bWaveLoaded = b;
		}

		inline bool     IsWaveLoaded() const
		{
			return m_bWaveLoaded;
		}

		void            StartRecord(const bool bAutoBeginRecord, const bool bAutoEndRecord);
		void            RecordWaveGetBuffer(SAMPLE_INT *pBuff, const size_t nBufSampleLen);
		void            StopRecord();

		inline bool     IsRecording() const
		{
			return m_bRecord;
		}

};
