
#pragma once

#include "BKSoundDevice.h"
#include "Config.h"

constexpr auto VOL_LEVELS = 32;

class CEMU2149 : public CBKSoundDevice
{
		int m_nChip{ 0 }; // номер чипа

#pragma pack(push)
#pragma pack(1) // всё должно быть именно так, несмотря на предупреждения всяких анализаторов. Я лучше знаю.
		struct AYREGS
		{
			uint16_t fA{ 0 }, fB{ 0 }, fC{ 0 }; // 0,1; 2,3; 4,5;
			uint8_t noise{ 0 }, mix{ 0 };       // 6; 7;
			uint8_t vA{ 0 }, vB{ 0 }, vC{ 0 };  // 8; 9; 10;
			uint16_t envT{ 0 };                 // 11,12;
			uint8_t env{ 0 };                   // 13;
			uint8_t portA{ 0 }, portB{ 0 };     // 14; 15;
		};

		union
		{
			struct AYREGS m_r; // обозначения регистров для более удобного доступа к их значениям.
			std::array<uint8_t, 16> m_reg{}; // содержимое регистров
		};

#pragma pack(pop)

		static const std::array<std::array<int, VOL_LEVELS>, 2> m_voltbl;
		std::array<double, VOL_LEVELS> m_vols;
		std::array < double, AY_PAN_BASE + 1 > m_dPanKoeff;

		int                 m_nClock{ 0 }, m_nRate{ 0 };
		uint32_t            m_nBaseIncrement{ 0 };
		uint32_t            m_nBaseCount{ 0 };

		/* m_nRate converter*/
		double              m_dRealStep{ 0.0 };
		double              m_dPSGTime{ 0.0 };
		double              m_dPSGStep{ 0.0 };

		bool                m_bHQ{ true };      // флаг работы в высоком качестве (всегда true)
		uint8_t             m_nSynthReg{0xff};  // номер последнего выбранного регистра для обмена данными

		struct SoundChannel
		{
			int     nCount{ 0 };
			int     nVolume{ 0 };        // громкость звучания канала
			int     nFreq{ 0 };          // частота ноты канала
			bool    bEnv{ false };       // флаг, что включена огибающая на канале
			bool    bToneToggle{ false };
			bool    bToneMask{ false };  // флаг звучания ноты
			bool    bNoiseMask{ false }; // флаг звучания шума
			// указатели на переменные из конфига, для того, чтобы на лету всё это менялось
			double *pVolume{ nullptr };  // громкость канала
			int    *pPanL{ nullptr };    // панорамирование влево
			int    *pPanR{ nullptr };    // панорамирование вправо
		};

		std::array<SoundChannel, AY_CHANS> m_Channel;

		int                 m_nEnvPtr{ 0 };
		int                 m_nDEnv{ 0 };
		bool                m_bEnvLValue{ false };
		bool                m_bEnvAttack{ false };
		bool                m_bEnvHold{ false };
		bool                m_bEnvAlt{ false };
		int                 m_nEnvFreq{ 0 };
		int                 m_nEnvCount{ 0 };

		uint32_t            m_nNoiseSeed{ 0 };
		int                 m_nNoiseCount{ 0 };
		int                 m_nNoiseFreq{ 0 };

		/* I/O Ctrl*/
		uint8_t             m_nAddr{ 0 };

		double              m_dMixL{ 0.0 }, m_dMixR{ 0.0 };
		void                calc();


	public:
		CEMU2149();
		void                setChip(const int nChip);
		virtual ~CEMU2149() override;
		virtual void        ReInit() override;
		virtual void        Reset() override;

		virtual void        GetSample(sOneSample *pSm) override;

		void                PSG_reset();
		void                PSG_init(const int c, const int r);
		void                PSG_set_quality(const bool q);
		void                PSG_setVolumeMode();
		void                PSG_writeReg(uint8_t reg, uint8_t val);
		void                PSG_writeIO(const uint8_t nAddr, const uint8_t val);
		uint8_t             PSG_readReg(const uint8_t reg) const;
		uint8_t             PSG_readIO() const;
		void                PSG_calc(SAMPLE_INT &L, SAMPLE_INT &R);

		// Writing address
		void                synth_write_address(const uint8_t addr);

		// Writing data
		void                synth_write_data(const uint8_t byte);

};

