#pragma once

//#include <VersionHelpers.h> //нужен windows kit 8.1 в нём функция IsWindowsVistaOrGreater
#include "Ini.h"
#include "MSF.h"

#include "SafeReleaseDefines.h"
#include "ScreenColors_Shared.h"
#include "BKSound_Defines.h"

// Timers
#define BKTIMER_UI_REFRESH      1

//сколько фреймов в секунду. оказалось, что 25 всё же лучше, чем 50.
#define CPU_FRAMERATE           50

//тактовая частота
#define CPU_SPEED_BK11          4000000
#define CPU_SPEED_BK10          3000000

#define SCRIPT_START_DELAY      5

//адрес с которого начинаются порты, если к МПИ ничего не подключено
#define BK_PURE_PORTSIO_AREA    0177600
//адрес с которого начинаются порты, если к МПИ что-нибудь подключено
#define BK_PORTSIO_AREA         0177000

enum PANE_STATUS : int
{
	STATUS_PANE_SYSTEM = 0,
	STATUS_PANE_KBD_XLAT,
	STATUS_PANE_KBD_CAPS,
	STATUS_PANE_KBD_AR2,
	STATUS_PANE_KBD_SU,
	STATUS_PANE_INFO,
	STATUS_PANE_KBD_SHIFT //фейковая панель.
};

//определения для звука
enum : int { MODEL_AY_3_8910 = 0, MODEL_YM2149 };
//#define DEFAULT_EMU_SOUNDCHIP_FREQUENCY 1774400 //zx spectrum
#define DEFAULT_EMU_SOUNDCHIP_FREQUENCY 1714286 //BK: (12Mhz / 7)
//#define DEFAULT_EMU_SOUNDCHIP_FREQUENCY 1789772 //непонятно что, но звук такой, как надо.

enum SCREEN_TYPE : int
{
	CONF_SCREEN_RENDER_OGL = 0,
	CONF_SCREEN_RENDER_D2D,
	CONF_SCREEN_RENDER_DIB,
	CONF_SCREEN_RENDER_D3D
};
enum OSCILLOSCOPE_TYPE : int
{
	CONF_OSCILL_RENDER_OGL = 0,
	CONF_OSCILL_RENDER_D2D,
	CONF_OSCILL_RENDER_GDI
};

//определения для типа дисковода и типа бкшки
enum BKFDD_MODEL : int
{
	DEV_FDD_NONE = -1, DEV_FDD_STD = 0, DEV_FDD_A16M, DEV_FDD_SMK512, DEV_FDD_SAMARA
};
enum BOARD_MODEL : int
{
	BK_0010 = 10, BK_0011, BK_0011M
};

enum CONFIG_BKMODEL_NUMBER : int
{
	CONFIG_BKMODEL_NUMBER_BK_0010_01 = 0,
	CONFIG_BKMODEL_NUMBER_BK_0010_01_MSTD,
	CONFIG_BKMODEL_NUMBER_BK_0010_01_EXT32RAM,
	CONFIG_BKMODEL_NUMBER_BK_0010_01_FDD,
	CONFIG_BKMODEL_NUMBER_BK_0010_01_A16M,
	CONFIG_BKMODEL_NUMBER_BK_0010_01_SMK512,
	CONFIG_BKMODEL_NUMBER_BK_0010_01_SAMARA,
	CONFIG_BKMODEL_NUMBER_BK_0011,
	CONFIG_BKMODEL_NUMBER_BK_0011_FDD,
	CONFIG_BKMODEL_NUMBER_BK_0011_A16M, //заглушка
	CONFIG_BKMODEL_NUMBER_BK_0011_SMK512, //заглушка
	CONFIG_BKMODEL_NUMBER_BK_0011_SAMARA,
	CONFIG_BKMODEL_NUMBER_BK_0011M,
	CONFIG_BKMODEL_NUMBER_BK_0011M_FDD,
	CONFIG_BKMODEL_NUMBER_BK_0011M_A16M,
	CONFIG_BKMODEL_NUMBER_BK_0011M_SMK512,
	CONFIG_BKMODEL_NUMBER_BK_0011M_SAMARA,
	CONFIG_BKMODEL_NUMBERS_NUMBER
};

struct BK_MODEL_PARAMETERS
{
	CString strBKModelConfigName;
	CString strBKModelName;
	MSF_CONF nBKModel;
	BKFDD_MODEL nBKFDDModel;
};

//тут надо бы придумать более человеческий способ
//а то и енум CONFIG_BKMODEL_NUMBER и массив должны совпадать.
extern const BK_MODEL_PARAMETERS g_mstrConfigBKModelParameters[CONFIG_BKMODEL_NUMBERS_NUMBER];


//струкутра строковых параметров
struct confStringParam
{
	int nID; //ид ресурса
	CString *pstrValue; //указатель на переменную
	CString defValue; //значение по умолчанию
};


#define SSR_LIST_SIZE 4
//список допустимых частот, чтобы что попало вручную не вводили.
extern const int g_EnabledSSR[SSR_LIST_SIZE];

struct SoundChipModelList
{
	int nModelNum;
	CString strModel;
};
#define SCHM_LIST_SIZE 2
//список допустимых моделей звукового процессора, чтобы что попало вручную не вводили.
extern const SoundChipModelList g_EnabledSoundChipModels[SCHM_LIST_SIZE];

struct SoundChipFreqlList
{
	int nFreq;
	CString strFreq;
};
#define SCHFRQ_LIST_SIZE 4
//список предустановленных частот звукового процессора, чтобы вручную не вводить.
extern const SoundChipFreqlList g_SoundChipFrequences[SCHFRQ_LIST_SIZE];


extern const TCHAR koi8tbl[128];

extern const char bk_codes[];

//идентификатор. означает, что к данному приводу/в данный слот/прочее ничего не подключено.
extern const CString g_strEmptyUnit;
extern const CString g_mstrDrives[4];
extern const int g_mnDrivesIndx[4];

#pragma pack(push)
#pragma pack(1)
//это дело сохраняется в файле состояния, поэтому, для определённости
//типы переменных должны быть строго определёнными
struct BKMEMBank_t
{
	BOOL bReadable; //флаг, что память доступна для чтения
	BOOL bWritable; //флаг, что память доступна для записи
	DWORD nBank; //номер банка памяти 4kb
	DWORD nPage; //страница памяти БК11 == nBank >> 2
	DWORD nOffset; //смещение в массиве == nBank << 12
};

struct ConfBKModel_t
{
	DWORD nAltProMemBank; //код подключения страницы памяти контроллера
	/* для простоты пзу бейсика будем включать только в режиме 020. в остальных режимах не имеет смысла, хотя на реальной железке технически возможно */
	WORD  nExtCodes; //доп коды контроллера, типа 10 - подкл. бейсика и 4 - блокировка регистров 177130 и 177132 по чтению
	/*Потому что на БК11 в страницы 10. и 11. можно опцинально подключать свои ПЗУ
	*Формат такой:
	*бит 0 - наличие ПЗУ в странице 8 по адресам 100000
	*бит 1 - наличие ПЗУ в странице 8 по адресам 120000
	*бит 2 - наличие ПЗУ в странице 9 по адресам 100000
	*бит 3 - наличие ПЗУ в странице 9 по адресам 120000
	*и т.д.
	**/
	WORD  nROMPresent; //битовая маска присутствия ПЗУ БК11 на своих местах.
	DWORD nAltProMode; //код режима контроллера
};
#pragma pack(pop)

class CConfig
{
	public:
		//Rom модули
		static CString m_strBK10_017_Monitor,
		       m_strBK10_018_Focal,
		       m_strBK10_019_MSTD,
		       m_strBK10_106_basic1,
		       m_strBK10_107_basic2,
		       m_strBK10_108_basic3,
		       m_strBK11_201_bos,
		       m_strBK11_202_ext,
		       m_strBK11_203_mstd,
		       m_strBK11_198_basic1,
		       m_strBK11_199_basic2,
		       m_strBK11_200_basic3,
		       m_strBK11m_324_bos,
		       m_strBK11m_325_ext,
		       m_strBK11m_330_mstd,
		       m_strBK11m_327_basic1,
		       m_strBK11m_328_basic2,
		       m_strBK11m_329_basic3,
		       m_strFDD_Std,
		       m_strFDD_Std253,
		       m_strFDD_A16M,
		       m_strFDD_SMK512,
		       m_strFDD_Samara,
		       m_strBK11Pg121,
		       m_strBK11Pg122,
		       m_strBK11Pg131,
		       m_strBK11Pg132;
		//Директории
		static CString m_strBinPath,
		       m_strToolsPath,
		       m_strMemPath,
		       m_strSavesPath,
		       m_strTapePath,
		       m_strScriptsPath,
		       m_strROMPath,
		       m_strIMGPath,
		       m_strScreenShotsPath;

		//Параметры
		CString m_strBKBoardType;
		WORD    m_nCPURunAddr;
		int     m_nCPUFrequency,
		        m_nRegistersDumpInterval,
		        m_nScreenshotNumber,
		        m_nSoundVolume,
		        m_nScreenRenderType,
		        m_nOscRenderType,
		        m_nSoundSampleRate, //чтобы можно было менять частоту дискретизации звука на ходу.
		        m_nSoundChipFrequency,
		        m_nSoundChipModel;

		//Опции
		bool    m_bSavesDefault,
		        m_bSpeaker,
		        m_bSpeakerFilter,
		        m_bCovox,
		        m_bCovoxFilter,
		        m_bStereoCovox,
		        m_bAY8910,
		        m_bAY8910Filter,
		        m_bBKKeyboard,
		        m_bJoystick,
		        m_bICLBlock,

		        m_bColorMode,
		        m_bAdaptBWMode,
		        m_bLuminoforeEmulMode,
		        m_bFullscreenMode,

		        m_bPauseCPUAfterStart,
		        m_bAskForBreak, /*при чтении/записи из несуществующего адреса не прерываться.
                                    (полезно для отладки и не полезно для работы эмулятора)
                                    */
		        m_bEmulateLoadTape,
		        m_bEmulateSaveTape,
		        m_bTapeAutoBeginDetection,
		        m_bTapeAutoEnsDetection,

		        m_bShowPerformance,
		        m_bEmulateFDDIO;

		WORD    m_nAdrDump,
		        m_nAdrAsm;

		int     m_nVKBDType;

		//Приводы
		CString m_strDrives[4];
		CString m_strHDDrives[2];

//остальные параметры, которые желательно должны быть доступны глобально.
		OSVERSIONINFOEX m_osvi; //структура с версией винды
		CONFIG_BKMODEL_NUMBER m_BKConfigurationModelNumber; //номер конфигурации
		MSF_CONF        m_BKBoardModel;     //тип эмулируемой модели БК (для конструктора конфигураций)
		BKFDD_MODEL     m_BKFDDModel;       //переменная-посредник, сюда помещается номер модели дисковода перед созданием
		// конфигурации и используется во время создания конфигурации, в остальное время - не нужно.
	protected:
		static confStringParam m_romModules[];
		static confStringParam m_Directories[];

		CIni            iniFile; //наш распарсенный ини файл
		CString         m_strCurrentPath; //путь к проге
		CString         m_strIniFilePath; //полное имя ини файла, с путём

		CString         _GetCurrentPath();
		void            DefaultConfig();
		void            _intLoadConfig(bool bLoadMain);

	public:
		CConfig();
		virtual ~CConfig();
		void            InitConfig(const CString &strIniName);
		void            UnInitConfig();
		inline BOOL     IsWindowsVistaOrGreater()
		{
			return m_osvi.dwMajorVersion >= 6;
		}

		inline CIni    *GetIniObj()
		{
			return &iniFile;
		}

		CString         GetDriveImgName(int nDrive);
		void            SetDriveImgName(int nDrive, const CString &strPathName);
		CString         GetHDDriveImgName(int nDrive);
		void            SetHDDriveImgName(int nDrive, const CString &strPathName);
		const CString   &GetCurrentPath();
		CString         &NormalizePath(CString &strPath);
		CString         GetFilePath(const CString &strPath);
		CString         GetFileTitle(const CString &strPath);
		CString         GetFileName(const CString &strPath);
		CString         GetFileExt(const CString &strPath);
		void            LoadConfig(bool bLoadMain = true);
		void            LoadConfig_FromMemory(BYTE *pBuff = nullptr, UINT nSize = 0);
		void            SaveConfig();
		ULONGLONG       SaveConfig_ToMemory(BYTE *pBuff = nullptr, UINT nSize = 0);
		bool            VerifyRoms();
		void            SetBKModelNumber(CONFIG_BKMODEL_NUMBER n);
		CONFIG_BKMODEL_NUMBER GetBKModelNumber();

		void			CheckRenders();
};

extern CConfig g_Config;

CString     WordToOctString(WORD word);
CString     ByteToOctString(BYTE byte);
void        WordToOctString(WORD word, CString &str);
void        ByteToOctString(BYTE byte, CString &str);
WORD        OctStringToWord(const CString &str);
CString     IntToString(int iInt, int radix = 10);
CString     IntToFileLengthString(int iInt);
CString     MsTimeToTimeString(int msTime);

CString     BKToUNICODE(BYTE *pBuff, int size);
TCHAR       BKToUNICODE_Byte(BYTE b);
void        UNICODEtoBK(CString &ustr, BYTE *pBuff, int bufSize, bool bFillBuf);
void        ANSIToBK(BYTE &ch);

#ifdef _DEBUG
void        GetLastErrorOut(LPTSTR lpszFunction);
#endif

inline int GetDigit(register WORD word, register int pos)
{
	return (pos ? (word >> ((pos << 1) + pos)) : word) & 7;
}

static CString getCompileDate(const LPCTSTR &p_format = _T("%Y-%m-%d"))
{
	COleDateTime tCompileDate;
	tCompileDate.ParseDateTime(_T(__DATE__), LOCALE_NOUSEROVERRIDE, 1033);
	return tCompileDate.Format(p_format).GetString();
}

static CString getCompileTime(const LPCTSTR &p_format = _T("%H-%M-%S"))
{
	COleDateTime tCompileDate;
	tCompileDate.ParseDateTime(_T(__TIME__), LOCALE_NOUSEROVERRIDE, 1033);
	return tCompileDate.Format(p_format).GetString();
}

//#define WM_BREAKPOINT             (WM_USER + 100)
#define WM_DEBUG_ADDRESS_UPDATE     (WM_USER + 101)
#define WM_ENTEREDIT_VALUE_UPDATE   (WM_USER + 102)
#define WM_RECEIVE_CMD_STRING       (WM_USER + 103)
#define WM_TAPE_FILE_LOAD           (WM_USER + 104)
#define WM_CPU_DEBUGBREAK           (WM_USER + 105)
#define WM_MEMDUMP_NEED_UPDATE      (WM_USER + 106)
//TapeManagerDlg
#define WM_BUFFER_READY             (WM_USER + 107)
#define WM_INFO_READY               (WM_USER + 108)
//Карта памяти
#define WM_MEMMAP_CLOSE             (WM_USER + 109)
//Droptarget
#define WM_DROP_TARGET              (WM_USER + 110)
//Виртуальная клавиатура
#define WM_VKBD_DOWN                (WM_USER + 111)
#define WM_VKBD_UP                  (WM_USER + 112)
#define WM_VKBD_DN_CALLBACK         (WM_USER + 113)
#define WM_VKBD_UP_CALLBACK         (WM_USER + 114)
#define WM_OUTKBDSTATUS             (WM_USER + 115)
//Экран
#define WM_SCREENSIZE_CHANGED       (WM_USER + 116)
//
#define WM_START_PLATFORM           (WM_USER + 117)


// Interrups
#define NO_INTERRUPT    0000
#define INTERRUPT_4     0004
#define INTERRUPT_10    0010
#define INTERRUPT_14    0014
#define INTERRUPT_20    0020
#define INTERRUPT_24    0024
#define INTERRUPT_30    0030
#define INTERRUPT_34    0034
#define INTERRUPT_60    0060
#define INTERRUPT_100   0100
#define INTERRUPT_270   0270
#define INTERRUPT_274   0274

// Commands

// No fields
#define PI_HALT         0000000
#define PI_WAIT         0000001
#define PI_RTI          0000002
#define PI_BPT          0000003
#define PI_IOT          0000004
#define PI_RESET        0000005
#define PI_RTT          0000006
#define PI_MFPT         0000007
#define PI_START10      0000010
#define PI_START11      0000011
#define PI_START12      0000012
#define PI_START13      0000013
#define PI_STEP14       0000014
#define PI_STEP15       0000015
#define PI_STEP16       0000016
#define PI_STEP17       0000017
#define PI_NOP          0000240
#define PI_CLC          0000241
#define PI_CLV          0000242
#define PI_CLVC         0000243
#define PI_CLZ          0000244
#define PI_CLZC         0000245
#define PI_CLZV         0000246
#define PI_CLZVC        0000247
#define PI_CLN          0000250
#define PI_CLNC         0000251
#define PI_CLNV         0000252
#define PI_CLNVC        0000253
#define PI_CLNZ         0000254
#define PI_CLNZC        0000255
#define PI_CLNZV        0000256
#define PI_CCC          0000257
#define PI_NOP260       0000260
#define PI_SEC          0000261
#define PI_SEV          0000262
#define PI_SEVC         0000263
#define PI_SEZ          0000264
#define PI_SEZC         0000265
#define PI_SEZV         0000266
#define PI_SEZVC        0000267
#define PI_SEN          0000270
#define PI_SENC         0000271
#define PI_SENV         0000272
#define PI_SENVC        0000273
#define PI_SENZ         0000274
#define PI_SENZC        0000275
#define PI_SENZV        0000276
#define PI_SCC          0000277

// One field
#define PI_RTS          0000200
#define PI_FADD         0075000
#define PI_FSUB         0075010
#define PI_FMUL         0075020
#define PI_FDIV         0075030

// Two fields
#define PI_JMP          0000100
#define PI_SWAB         0000300
#define PI_CLR          0005000
#define PI_COM          0005100
#define PI_INC          0005200
#define PI_DEC          0005300
#define PI_NEG          0005400
#define PI_ADC          0005500
#define PI_SBC          0005600
#define PI_TST          0005700
#define PI_ROR          0006000
#define PI_ROL          0006100
#define PI_ASR          0006200
#define PI_ASL          0006300
#define PI_MARK         0006400
#define PI_MFPI         0006500
#define PI_MTPI         0006600
#define PI_SXT          0006700
#define PI_MTPS         0106400
#define PI_MFPD         0106500
#define PI_MTPD         0106600
#define PI_MFPS         0106700
// Branchs & interrupts
#define PI_BR           0000400
#define PI_BNE          0001000
#define PI_BEQ          0001400
#define PI_BGE          0002000
#define PI_BLT          0002400
#define PI_BGT          0003000
#define PI_BLE          0003400
#define PI_BPL          0100000
#define PI_BMI          0100400
#define PI_BHI          0101000
#define PI_BLOS         0101400
#define PI_BVC          0102000
#define PI_BVS          0102400
#define PI_BHIS         0103000
#define PI_BLO          0103400

#define PI_EMT          0104000
#define PI_TRAP         0104400

// Three fields
#define PI_JSR          0004000
#define PI_XOR          0074000
#define PI_SOB          0077000
#define PI_MUL          0070000
#define PI_DIV          0071000
#define PI_ASH          0072000
#define PI_ASHC         0073000

// Four fields
#define PI_MOV          0010000
#define PI_CMP          0020000
#define PI_BIT          0030000
#define PI_BIC          0040000
#define PI_BIS          0050000
#define PI_ADD          0060000
#define PI_SUB          0160000

// BK Key codes
#define BKKEY_SHAG      0000000
#define BKKEY_POVT      0000001
#define BKKEY_INDSU     0000002
#define BKKEY_KT        0000003
#define BKKEY_BLOKRED   0000004
#define BKKEY_GRAF      0000005
#define BKKEY_ZAP       0000006
#define BKKEY_STIR      0000007
#define BKKEY_L_ARROW   0000010
#define BKKEY_TAB       0000011
#define BKKEY_ENTER     0000012
#define BKKEY_RGTDEL    0000013
#define BKKEY_SBR       0000014
#define BKKEY_USTTAB    0000015
#define BKKEY_RUS       0000016
#define BKKEY_LAT       0000017
#define BKKEY_SBRTAB    0000020
#define BKKEY_VS        0000023
#define BKKEY_GT        0000024
#define BKKEY_SDVIG     0000026
#define BKKEY_RAZDVIG   0000027
#define BKKEY_ZAB       0000030
#define BKKEY_R_ARROW   0000031
#define BKKEY_U_ARROW   0000032
#define BKKEY_D_ARROW   0000033
#define BKKEY_PROBEL    0000040

