#pragma once

#pragma pack(push)
#pragma pack(1) // envelope period (envT, R13-R14) has invalid word alignment
struct TAPE_FILE_INFO
{
	DWORD               start_tuning{ 0xffffffff };
	DWORD               synchro_start{ 0xffffffff };
	DWORD               marker1{ 0xffffffff };
	DWORD               synchro_header{ 0xffffffff };
	DWORD               header{ 0xffffffff };
	uint16_t            address{ 0xffff };
	uint16_t            length{ 0xffff };
	uint8_t             name[16] { 0xff,};
	DWORD               marker2{ 0xffffffff };
	DWORD               synchro_data{ 0xffffffff };
	DWORD               data{ 0xffffffff };
	uint16_t            crc{ 0xffff };
	DWORD               synchro6{ 0xffffffff };
	DWORD               end_tuning{ 0xffffffff };
	DWORD               end{ 0xffffffff };
};
#pragma pack(pop)

constexpr auto TUNESEQ_LENGTH = 2048;

// макросы
#ifndef SAFE_DELETE
#define SAFE_DELETE(p)       { if (p) { delete (p);     (p)=nullptr; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p);   (p)=nullptr; } }
#endif
#ifndef SAFE_DELETE_MEMORY
#define SAFE_DELETE_MEMORY(p) { if (p) { free(p);   (p)=nullptr; } }
#endif
#ifndef SAFE_DELETE_OBJECT
#define SAFE_DELETE_OBJECT(p)   { if (p) { DeleteObject(p); (p)=nullptr; } }
#endif
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p)      { if (p) { (p)->Release(); (p)=nullptr; } }
#endif
#ifndef SAFE_RELEASE_C
#define SAFE_RELEASE_C(p)      { if (p) { (p).Release(); } }
#endif

using WaveData = std::vector<double>;

constexpr auto AVGTBL_SIZE = 32; // размер таблицы последних значений синхроимпульсов

class CBKTapeReaderDlg;

class CWaveParserProto
{
		struct ImpScatter_t
		{
			int nImpScatter{ 0 };
			int nWeight{ 0 };
			ImpScatter_t()
			    = default;
			ImpScatter_t(int l)
				: nImpScatter(l)
			{}
		};

	protected:

		CBKTapeReaderDlg *m_pParent{ nullptr };
		std::unique_ptr<uint8_t[]> m_pBin;
		int                     m_nPos{ 0 };    // текущая позиция в ваве данных
		bool                    m_bInverse{ false }; // флаг инверсного сигнала
		double                  m_dAvgLength{ 0.0 }; // средняя длина синхроимпульса
		double                  m_dAverage{ 0.0 }; // среднее нулевое значение уровня импульса

		//////////////////////////////////////////////////////////////////////////
		int                     m_AvgImpTbl[AVGTBL_SIZE] { 0 };
		int                     m_AvgImpPos{ 0 };
		//////////////////////////////////////////////////////////////////////////
		int                     m_nLastImpLenTune{ 0 };
		virtual int             CalcImpLength(WaveData &pWave, int &pos) = NULL;
		virtual bool            FindMarker1(WaveData &pWave) = NULL;

		virtual bool            FindTuning(bool bAutoTune, bool bRollback, WaveData &pWave, int nLength, DWORD &wave_pos, int &wave_length);
		virtual bool            FindMarker(WaveData &pWave);
		virtual bool            FindSyncro6(WaveData &pWave);
		virtual bool            ReadBit(WaveData &pWave, bool &bBit);
		virtual bool            ReadByte(WaveData &pWave, uint8_t &byte);

		int                     DefineLength(int nLength);

		void                    CalcAverage(WaveData &pWave);

		double                  MinusDelta(double x);
		double                  PlusDelta(double x);

		std::vector<ImpScatter_t>       m_vScatters;
		void                    PutImpScatter(int len);
		int                     GetBestImpScatter();

	public:
		enum { BAD_LENGTH = -1, BAD_TUNING = -2, BAD_MARKER = -3};
		CWaveParserProto();
		virtual ~CWaveParserProto();

		void                    bindDialog(CBKTapeReaderDlg *pDlg)
		{
			m_pParent = pDlg;
		}

		bool                    ParseWaveFile(TAPE_FILE_INFO *pTfi, WaveData &pWave, const fs::path &name, CString strID);

		uint8_t                *getBin()
		{
			return m_pBin.get();
		}

		uint16_t                CalcCRC(TAPE_FILE_INFO *pTfi);

		int                     GetLastImpLength() const
		{
			return m_AvgImpTbl[m_AvgImpPos];
		}

		void                    SetLastImpLength(int imp)
		{
			int old = m_AvgImpTbl[m_AvgImpPos]; // это старое значение, которое было в массиве
			m_AvgImpTbl[m_AvgImpPos] = imp; // заносим туда новое

			if (old)
			{
				m_dAvgLength -= double(old) / double(AVGTBL_SIZE);    // от среднего вычитаем удаляемое значение
			}

			if (imp)
			{
				m_dAvgLength += double(imp) / double(AVGTBL_SIZE);    // и прибавляем добавляемое
			}

			if (++m_AvgImpPos >= AVGTBL_SIZE)
			{
				m_AvgImpPos = 0;
			}
		}

		void                    InitAvgImp()
		{
			m_dAvgLength = 0.0;
			m_AvgImpPos = 0;

			for (int &i : m_AvgImpTbl)
			{
				i = 0;
			}
		}

		double                  GetAvgImpLen() const
		{
			return m_dAvgLength;
		}

};

