#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>

using namespace std;

class CPlatform
{

///------------------------------------------------
		CMediaProc          m_MediaProc;
		CVideoProc          m_VideoProc;
		CPeriodicProc       m_PeriodicProc;


///------------------------------------------------

		CScriptRunner       m_Script;           //объект обработчика скриптов
		// Эмулятор
		CDebugger           m_Debugger;         //отладчик.
		CCPU                m_CPU;              // Процессор 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_MediaProc.GetBKSoundPtr();
		}
		inline CSpeaker     *GetSpeakerPtr()
		{
			return m_MediaProc.GetSpeakerPtr();
		}
		inline CCovox       *GetCovoxPtr()
		{
			return m_MediaProc.GetCovoxPtr();
		}
		inline CEMU2149     *GetAY8910Ptr()
		{
			return m_MediaProc.GetAY8910Ptr();
		}
		inline CTape        *GetTapePtr()
		{
			return m_MediaProc.GetTapePtr();
		}
		inline CScriptRunner *GetScriptRunnerPtr()
		{
			return &m_Script;
		}
		inline CCPU  *GetCPUPtr()
		{
			return &m_CPU;
		}

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

		inline CDebugger  *GetDebuggerPtr()
		{
			return &m_Debugger;
		}
		inline void SetScreen(CScreen *scr)
		{
			m_VideoProc.SetScreen(scr);   //задание объекта экранной подсистемы. Он создаётся в другом месте
		}
		inline CScreen *GetScreenPtr()
		{
			return m_VideoProc.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();
		void                StartPlayTape();
		void                StartPlayTape(const CString &strPath);
		void                ResetBoard(bool &bLongreset, bool bIsSuPress);
		bool                InitPlatform();
		void                ResetPlatform();

		bool                DropFiles(CString *str);
		bool                ParseCommandLineParameters(CString strCommands);
		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_BOARD_MODEL 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;

