#pragma once

extern class CSuperSerialCard sg_SSC;

enum {COMMEVT_WAIT=0, COMMEVT_ACK, COMMEVT_TERM, COMMEVT_MAX};
enum eFWMODE {FWMODE_CIC=0, FWMODE_SIC_P8, FWMODE_PPC, FWMODE_SIC_P8A};	// NB. CIC = SSC

typedef struct
{
	//DIPSW1
	UINT	uBaudRate;
	eFWMODE	eFirmwareMode;

	//DIPSW2
	UINT	uStopBits;
	UINT	uByteSize;
	UINT	uParity;
	bool	bLinefeed;
	bool	bInterrupts;
} SSC_DIPSW;

#define TEXT_SERIAL_COM TEXT("COM")
#define TEXT_SERIAL_TCP TEXT("TCP")

class CSuperSerialCard
{
public:
	CSuperSerialCard();
	virtual ~CSuperSerialCard();

	void	CommInitialize(LPBYTE pCxRomPeripheral, UINT uSlot);
	void    CommReset();
	void    CommDestroy();
	void    CommSetSerialPort(HWND hWindow, DWORD dwNewSerialPortItem);
	void    CommUpdate(DWORD);
	void    SetSnapshot_v1(const DWORD baudrate, const BYTE bytesize, const BYTE commandbyte, const DWORD comminactivity, const BYTE controlbyte, const BYTE parity, const BYTE stopbits);
	std::string GetSnapshotCardName(void);
	void	SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
	bool	LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version);

	char*	GetSerialPortChoices();
	DWORD	GetSerialPort() { return m_dwSerialPortItem; }	// Drop-down list item
	char*	GetSerialPortName() { return m_ayCurrentSerialPortName; }
	void	SetSerialPortName(const char* pSerialPortName);
	bool	IsActive() { return (m_hCommHandle != INVALID_HANDLE_VALUE) || (m_hCommListenSocket != INVALID_SOCKET); }
	void	SupportDTR(bool bEnable, bool bInvert) { m_bCfgSupportDTR = bEnable; m_bCfgInvertDTR = bInvert; }

	void	CommTcpSerialAccept();
	void	CommTcpSerialReceive();
	void	CommTcpSerialClose();
	void	CommTcpSerialCleanup();

	static BYTE __stdcall SSC_IORead(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULONG nCyclesLeft);
	static BYTE __stdcall SSC_IOWrite(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULONG nCyclesLeft);

private:
	BYTE __stdcall CommCommand(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft);
	BYTE __stdcall CommControl(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft);
	BYTE __stdcall CommDipSw(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft);
	BYTE __stdcall CommReceive(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft);
	BYTE __stdcall CommStatus(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft);
	BYTE __stdcall CommTransmit(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft);

	void	InternalReset();
	void	GetDIPSW();
	void	SetDIPSWDefaults();
	BYTE	GenerateControl();
	UINT	BaudRateToIndex(UINT uBaudRate);
	void	UpdateCommState();
	BOOL	CheckComm();
	void	CloseComm();
	void	CheckCommEvent(DWORD dwEvtMask);
	static DWORD WINAPI	CommThread(LPVOID lpParameter);
	bool	CommThInit();
	void	CommThUninit();
	UINT	GetNumSerialPortChoices() { return m_vecSerialPortsItems.size(); }
	void	ScanCOMPorts();
	void	SaveSnapshotDIPSW(class YamlSaveHelper& yamlSaveHelper, std::string key, SSC_DIPSW& dipsw);
	void	LoadSnapshotDIPSW(class YamlLoadHelper& yamlLoadHelper, std::string key, SSC_DIPSW& dipsw);

	//

public:
	static const UINT SIZEOF_SERIALCHOICE_ITEM = 8*sizeof(char);

private:
	char	m_ayCurrentSerialPortName[SIZEOF_SERIALCHOICE_ITEM];
	DWORD	m_dwSerialPortItem;

	static const UINT SERIALPORTITEM_INVALID_COM_PORT = 0;
	std::vector<UINT> m_vecSerialPortsItems;	// Includes "None" & "TCP" items
	char*	m_aySerialPortChoices;
	UINT	m_uTCPChoiceItemIdx;

	static SSC_DIPSW	m_DIPSWDefault;
	SSC_DIPSW			m_DIPSWCurrent;

	// Derived from DIPSW1
	UINT	m_uBaudRate;

	// Derived from DIPSW2
	UINT	m_uStopBits;
	UINT	m_uByteSize;
	UINT	m_uParity;

	// SSC Registers
	BYTE   m_uControlByte;
	BYTE   m_uCommandByte;

	//

	HANDLE m_hCommHandle;
	SOCKET m_hCommListenSocket;
	SOCKET m_hCommAcceptSocket;
	DWORD  m_dwCommInactivity;

	//

	CRITICAL_SECTION	m_CriticalSection;	// To guard /g_vRecvBytes/
	std::deque<BYTE>			m_qComSerialBuffer[2];
	volatile UINT		m_vuRxCurrBuffer;	// Written to on COM recv. SSC reads from other one
	std::deque<BYTE>			m_qTcpSerialBuffer;

	//

	bool m_bTxIrqEnabled;
	bool m_bRxIrqEnabled;

	volatile bool		m_vbTxIrqPending;
	volatile bool		m_vbRxIrqPending;

	bool m_bWrittenTx;

	//

	HANDLE m_hCommThread;

	HANDLE m_hCommEvent[COMMEVT_MAX];
	OVERLAPPED m_o;

	BYTE* m_pExpansionRom;
	UINT m_uSlot;

	// DTR
	UINT	m_uDTR;
	bool m_bCfgSupportDTR;
	bool m_bCfgInvertDTR;
};
