#pragma once

#include "ScriptRunner.h"
#include "Debugger.h"
#include "Config.h"
#include "MSFManager.h"

#include "CPU.h"
#include "MainFrm.h"

#include "MediaProc.h"
#include "VideoProc.h"
#include "PeriodicProc.h"

// Целевой объект команды CPlatform
class CBKMPIDevice;
class CBK001001;
class CFDDStd326;
class CBKMSTD10;
class CIRPS;

#include <thread>
// #include <mutex>
// #include <condition_variable>

class CPlatform
{

// /------------------------------------------------
		CMediaProc         *m_pMediaProc;
		CVideoProc         *m_pVideoProc;
		CPeriodicProc      *m_pPeriodicProc;
// /------------------------------------------------


		CScriptRunner      *m_pScript;          // объект обработчика скриптов
		// Эмулятор
		CDebugger          *m_pDebugger;        // отладчик.
		CCPU               *m_pCPU;             // Процессор 1801ВМ1

		std::vector<CBKMPIDevice *> m_pMPIDevices;   // список объектов БК

		CMainFrame         *m_pMainFrame;       // класс главное окно.

		// имена файлов, получаемых через ДнД или командную строку
		CString             m_strBinFileName;
		CString             m_strMemFileName;
		CString             m_strTapeFileName;
		CString             m_strScriptFileName;

		volatile bool       m_bBreaked;         // флаг состояния - процессор приостановлен для отладки
		volatile bool       m_bRunning;         // флаг состояния - процессор работает/стоит

		int                 m_nLowBound;        // нижняя граница, меньше которой не может быть частота
		int                 m_nHighBound;       // верхняя граница, больше которой не может быть частота

		int                 m_nCPUFreq;         // текущая частота работы.
		int                 m_nCPUFreq_prev;
		int                 m_nBaseCPUFreq;     // базовая частота работы, по которой вычисляется множитель

		int                 m_nCPUTicks;        // текущий счётчик тактов процессора

		// Debug section
		int                 m_nGotoAddress;     // адрес, на котором будет сделан отладочный останов

		bool                m_bBoardFound;      // флаг, что поиск проводили
		CBK001001          *m_pBoard;           // то, что нашлось, или nullptr, если такого устройства нет
		bool                m_bMSTDFound;       // флаг, что поиск проводили
		CBKMSTD10          *m_pMSTD;            // то, что нашлось, или nullptr, если такого устройства нет
		bool                m_bFDDFound;        // флаг, что поиск проводили
		CFDDStd326         *m_pFDD;             // то, что нашлось, или nullptr, если такого устройства нет
		bool                m_bIRPSFound;       // флаг, что поиск проводили
		CIRPS              *m_pIRPS;            // то, что нашлось, или nullptr, если такого устройства нет

		uint16_t            m_nFDDCatchAddr, m_nFDDExitCatchAddr;

	public:
		CPlatform();
		virtual ~CPlatform();
		enum BREAK_ADDRESS : int { ADDRESS_NONE = -1, GO_INTO = -2, GO_OUT = -3 };

		// выдача указателей на объекты всем желающим
		inline CBkSound    *GetBKSoundPtr()
		{
			return m_pMediaProc->GetBKSoundPtr();
		}
		inline CSpeaker    *GetSpeakerPtr()
		{
			return m_pMediaProc->GetSpeakerPtr();
		}
		inline CCovox      *GetCovoxPtr()
		{
			return m_pMediaProc->GetCovoxPtr();
		}
		inline CMenestrel  *GetMenestrelPtr()
		{
			return m_pMediaProc->GetMenestrelPtr();
		}
		inline CEMU2149    *GetAY8910Ptr()
		{
			return m_pMediaProc->GetAY8910Ptr();
		}
		inline CTape       *GetTapePtr()
		{
			return m_pMediaProc->GetTapePtr();
		}
		inline CScriptRunner *GetScriptRunnerPtr()
		{
			return m_pScript;
		}
		inline CCPU        *GetCPUPtr()
		{
			return m_pCPU;
		}

		void                ClearDevicesFounds();
		void                ClearDevicesVector();
		CBK001001          *GetBoardPtr();
		CFDDStd326         *GetFDDPtr();
		CBKMSTD10          *GetMSTDPtr();
		CIRPS              *GetIRPSPtr();

		inline CDebugger   *GetDebuggerPtr()
		{
			return m_pDebugger;
		}
		inline void         SetScreen(CScreen *scr)
		{
			m_pVideoProc->SetScreen(scr);   // задание объекта экранной подсистемы. Он создаётся в другом месте
		}
		inline CScreen     *GetScreenPtr()
		{
			return m_pVideoProc->GetScreenPtr();
		}

		inline void         SetMainPtr(CMainFrame *ptr)
		{
			m_pMainFrame = ptr;
		}
		inline CMainFrame  *GetMainPtr()
		{
			return m_pMainFrame;
		}

		inline CString     *GetStrBinFileName()
		{
			return &m_strBinFileName;
		}
		inline CString     *GetStrMemFileName()
		{
			return &m_strMemFileName;
		}
		inline CString     *GetStrTapeFileName()
		{
			return &m_strTapeFileName;
		}
		inline CString     *GetScriptFileName()
		{
			return &m_strScriptFileName;
		}

		void                InitEmulator();
		void                InitObjects();
		bool                StartPlayTape();
		bool                StartPlayTape(const CString &strPath);
		void                ResetBoard(bool &bLongreset, bool bIsSuPress);
		bool                InitPlatform();
		void                ResetPlatform();

		bool                DropFiles(CString *str);
		bool                ParseCommandLineParameters(CString strCommands);
		bool                SetCmdLParameters(TCHAR command, CString &strParam);
		void                SetScriptPath(bool bXlat);
		void                StopAll();          // остановить всё - процессор, BKTIMER_UI_REFRESH
		void                StartAll();         // запустить всё - процессор, BKTIMER_UI_REFRESH
		bool                SaveMemoryState(CString &strPath);
		bool                RestoreState(CMSFManager *pMsf);
		bool                LoadMemoryState(CMSFManager *pMsf);

		bool                CreateBoard(CONF_BKMODEL nConf);
		void                DeleteBoard();
		void                RunBoard();

		void                RunInto();
		void                RunOver();
		void                RunOut();
		void                RunToAddr(uint16_t addr);
		void                RunCPU(bool bUnbreak = true); // запуск. по умолчанию сбрасывается отладочный приостанов
		void                StopCPU(bool bUnbreak = true); // остановка. по умолчанию сбрасывается отладочный приостанов
		inline bool         IsCPURun();
		void                BreakCPU();
		void                UnbreakCPU(int nGoto);
		inline bool         IsCPUBreaked();

		uint8_t             GetByteT(uint16_t addr, int &nTC);
		uint16_t            GetWordT(uint16_t addr, int &nTC);
		void                SetByteT(uint16_t addr, uint8_t b, int &nTC);
		void                SetWordT(uint16_t addr, uint16_t w, int &nTC);
		uint8_t             GetByte(uint16_t addr);
		uint16_t            GetWord(uint16_t addr);
		void                SetByte(uint16_t addr, uint8_t b);
		void                SetWord(uint16_t addr, uint16_t w);

		uint8_t             GetByteIndirect(uint16_t addr);
		uint16_t            GetWordIndirect(uint16_t addr);
		void                SetByteIndirect(uint16_t addr, uint8_t b);
		void                SetWordIndirect(uint16_t addr, uint16_t w);

		void                FrameParam();
		void                KeyboardInterrupt(uint16_t interrupt);

		void                AccelerateCPU();
		void                SlowdownCPU();
		void                NormalizeCPU();
		bool                CanAccelerate();
		bool                CanSlowDown();
		int                 GetLowBound();
		int                 GetHighBound();
		void                SetCPUBaseFreq(int frq); // установка базовой частоты
		void                SetCPUFreq(int frq); // установка текущей частоты
		int                 GetCPUFreq(); // выдача текущей частоты
		int                 GetCPUSpeed();   // выдача текущей частоты для конфигурации

		bool                EmulateLoadTape();
		bool                EmulateSaveTape();

		void                SetInterseptAddress(uint16_t nBegin, uint16_t nEnd);
		void                EmulateFDD();

	protected:
		int                 CalcStep();
		void                StopBoard();
		void                StartBoard();

		CBKMPIDevice       *CreateDevice(BK_DEV_MPI device);

		bool                RestorePlatform(CMSFManager *pMsf, MSF_PLATFORM_DATA **pPD);
		bool                RestorePreview(CMSFManager *pMsf);
		bool                RestoreConfig(CMSFManager *pMsf);
		bool                RestoreCPURegisters(CMSFManager *pMsf);

		void                ClearProcessingFiles();

		void                DrawDebugScreen();

		// поток с точным таймером
		void                TimerThreadFunc();  // собственно функция
		std::thread         m_TimerThread;
		volatile bool       m_bKillTimerEvent;  // флаг для остановки потока.
		void                StopTimerThread();
		bool                StartTimerThread();

};

extern CPlatform g_Platform;

