// Ckeyboard.cpp : implementation file
//

#include "stdafx.h"
#include <io.h>
#include <fcntl.h>
#include "Atari800Win.h"
#include "CFileSmallDlg.h"
#include "Ckeyboard.h"
#include "winatari.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

extern "C" {
#include "atari.h"
#include "graphics.h"
#include "registry.h"
extern int ReadKeyTemplate( char *name );
extern char	szTemplateFile[];
extern char	szTemplateDescrip[];
extern int	iKBTable[];
extern ULONG ulScreenMode;
extern ULONG ulMiscStates;
extern void Aprint(char *format, ... );
}

unsigned char ucKeysCovered[256] = {
/*  0*/	0, 0, 0, 0, 0, 0, 0, 0,	// BACKSPACE
/*  8*/	1, 1, 0, 0, 0, 1, 0, 0, // TAB RET
/* 16*/	0, 0, 0, 0, 1, 0, 0, 0, // CAPS LOCK
/* 24*/	0, 0, 0, 1, 0, 0, 0, 0, // ESC SPACE
/* 32*/ 1, 0, 0, 0, 0, 1, 1, 1, // LEFT UP RIGHT DOWN
/* 40*/	1, 0, 0, 0, 0, 0, 0, 0, // 0
/* 48*/	1, 1, 1, 1, 1, 1, 1, 1, // 1 2 3 4 5 6 7 8
/* 56*/	1, 1, 0, 0, 0, 0, 0, 0, // 9 
/* 64*/	0, 1, 1, 1, 1, 1, 1, 1, // a b c d e f g h
/* 72*/	1, 1, 1, 1, 1, 1, 1, 1, // i j k l m n o p 
/* 80*/	1, 1, 1, 1, 1, 1, 1, 1, // q r s t u v w x 
/* 88*/	1, 1, 1, 0, 0, 0, 0, 0, // y z PAD0
/* 96*/	1, 1, 1, 1, 1, 1, 1, 1, // PAD1 PAD2 PAD3 PAD4 PAD5 PAD6 PAD7 PAD8
/*104*/	1, 1, 1, 1, 0, 1, 1, 1, // PAD9 PAD* PAD+ PAD- PAD. PAD/
/*112*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*120*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*128*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*136*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*144*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*152*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*160*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*168*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*176*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*184*/	0, 0, 1, 1, 1, 1, 1, 1, 
/*192*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*200*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*208*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*216*/	0, 0, 0, 1, 1, 1, 1, 0, 
/*224*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*232*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*240*/	0, 0, 0, 0, 0, 0, 0, 0, 
/*248*/	0, 0, 0, 0, 0, 0, 0, 0 };

static int	iAkeyVals[NUM_ATARI_KEY_ELEMENTS] = {
AKEY_ESCAPE,		AKEY_1,			AKEY_2,			AKEY_3,			AKEY_4,			AKEY_5,			// ESC 1 2 3 4 5
AKEY_6,				AKEY_7,			AKEY_8,			AKEY_9,			AKEY_0,			AKEY_LESS,		// 6 7 8 9 0 <
AKEY_GREATER,		AKEY_BACKSPACE,	AKEY_TAB,		AKEY_q,			AKEY_w,			AKEY_e,			// > BACKSPC TAB q w e
AKEY_r,				AKEY_t,			AKEY_y,			AKEY_u,			AKEY_i,			AKEY_o,			// r t y u i o 
AKEY_p,				AKEY_MINUS,		AKEY_EQUAL,		AKEY_RETURN,	AKEY_a,			AKEY_s,			// p - = RET a s
AKEY_d,				AKEY_f,			AKEY_g,			AKEY_h,			AKEY_j,			AKEY_k,			// d f g h j k
AKEY_l,				AKEY_SEMICOLON,	AKEY_PLUS,		AKEY_ASTERISK,	AKEY_CAPSTOGGLE,AKEY_z,			// l ; + * CAPS Z
AKEY_x,				AKEY_c,			AKEY_v,			AKEY_b,			AKEY_n,			AKEY_m,			// x c v b n m 
AKEY_COMMA,			AKEY_FULLSTOP,	AKEY_SLASH,		AKEY_ATARI,		AKEY_SPACE,		AKEY_NONE };	// , . / ATARI SPC NONE

static int	iPCKeyVals[NUM_PC_KEY_ELEMENTS] = {
VK_ESCAPE,			0xc0,			0x31,			0x32,			0x33,			0x34,			// ESC ` 1 2 3 4
0x35,				0x36,			0x37,			0x38,			0x39,			0x30,			// 5 6 7 8 9 0 
0xbd,				0xbb,			VK_BACK,		VK_TAB,			0x51,			0x57,			// - = BACKSPC TAB Q W
0x45,				0x52,			0x54,			0x59,			0x55,			0x49,			// E R T Y U I 
0x4f,				0x50,			0xdb,			0xdd,			0xdc,			VK_CAPITAL,		// O P [ ] BACKSLASH CAPSLOCK
0x41,				0x53,			0x44,			0x46,			0x47,			0x48,			// A S D F G H
0x4a,				0x4b,			0x4c,			0xba,			0xde,			VK_RETURN,		// J K L ; ' ENTER
0x5a,				0x58,			0x43,			0x56,			0x42,			0x4e,			// Z X C V B N
0x4d,				0xbc,			0xbe,			0xbf,			VK_SPACE,		VK_UP,			// M , . / SPACE UP
VK_LEFT,			VK_DOWN,		VK_RIGHT,		VK_DIVIDE,		VK_MULTIPLY,	VK_SUBTRACT,	// LEFT DOWN RIGHT / * -
VK_NUMPAD7,			VK_NUMPAD8,		VK_NUMPAD9,		VK_NUMPAD4,		VK_NUMPAD5,		VK_NUMPAD6,		// 7 8 9 4 5 6 
VK_NUMPAD1,			VK_NUMPAD2,		VK_NUMPAD3,		VK_ADD,			VK_NUMPAD0,		VK_DECIMAL };	// 1 2 3 + 0 .

static const int	iKBDefault[NUM_PC_KEY_ELEMENTS] = {
AKEY_ESCAPE,		AKEY_NONE,		AKEY_1,			AKEY_2,			AKEY_3,			AKEY_4,			// ESC ` 1 2 3 4
AKEY_5,				AKEY_6,			AKEY_7,			AKEY_8,			AKEY_9,			AKEY_0,			// 5 6 7 8 9 0 
AKEY_MINUS,			AKEY_EQUAL,		AKEY_BACKSPACE,	AKEY_TAB,		AKEY_q,			AKEY_w,			// - = BACKSPC TAB Q W
AKEY_e,				AKEY_r,			AKEY_t,			AKEY_y,			AKEY_u,			AKEY_i,			// E R T Y U I 
AKEY_o,				AKEY_p,			AKEY_PLUS,		AKEY_ASTERISK,	AKEY_NONE,		AKEY_CAPSTOGGLE,// O P [ ] BACKSLASH CAPSLOCK
AKEY_a,				AKEY_s,			AKEY_d,			AKEY_f,			AKEY_g,			AKEY_h,			// A S D F G H
AKEY_j,				AKEY_k,			AKEY_l,			AKEY_SEMICOLON,	AKEY_NONE,		AKEY_RETURN,	// J K L ; ' ENTER
AKEY_z,				AKEY_x,			AKEY_c,			AKEY_v,			AKEY_b,			AKEY_n,			// Z X C V B N
AKEY_m,				AKEY_COMMA,		AKEY_FULLSTOP,	AKEY_SLASH,		AKEY_SPACE,		AKEY_UP,		// M , . / SPACE UP
AKEY_LEFT,			AKEY_DOWN,		AKEY_RIGHT,		AKEY_SLASH,		AKEY_ASTERISK,	AKEY_MINUS,		// LEFT DOWN RIGHT / * -
AKEY_7,				AKEY_8,			AKEY_9,			AKEY_4,			AKEY_5,			AKEY_6,			// 7 8 9 4 5 6 
AKEY_1,				AKEY_2,			AKEY_3,			AKEY_PLUS,		AKEY_0,			AKEY_FULLSTOP };// 1 2 3 + 0 .

/////////////////////////////////////////////////////////////////////////////
// CKeyBoard dialog


CKeyBoard::CKeyBoard(CWnd* pParent /*=NULL*/)
	: CDialog(CKeyBoard::IDD, pParent)
{
	//{{AFX_DATA_INIT(CKeyBoard)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
}


void CKeyBoard::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CKeyBoard)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CKeyBoard, CDialog)
	//{{AFX_MSG_MAP(CKeyBoard)
	ON_CBN_SELCHANGE(IDC_PCKEYS, OnSelchangePckeys)
	ON_BN_CLICKED(IDC_CTRL, OnCtrl)
	ON_BN_CLICKED(IDC_SHIFT, OnShift)
	ON_CBN_SELCHANGE(IDC_ATARIKEYS, OnSelchangeAtarikeys)
	ON_EN_KILLFOCUS(IDC_TEMPLATE_DESCRIPTION, OnKillfocusTemplateDescription)
	ON_BN_CLICKED(IDC_SAVETEMPLATE, OnSavetemplate)
	ON_BN_CLICKED(IDC_LOADTEMPLATE, OnLoadtemplate)
	ON_BN_CLICKED(IDC_USE_TEMPLATE, OnUseTemplate)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CKeyBoard message handlers

void CKeyBoard::UpdateDisplay( void )
{
	CComboBox	*comboBox = NULL;
	CButton		*pTemp = NULL;
	char		szNum[4];
	int			temp;

	comboBox = (CComboBox *)GetDlgItem( IDC_PCKEYS );
	ASSERT( comboBox );
	comboBox->SetCurSel( m_iPCSelect );
	
	comboBox = (CComboBox *)GetDlgItem( IDC_ATARIKEYS );
	ASSERT( comboBox );
	comboBox->SetCurSel( m_iASelect );

	pTemp = (CButton *)GetDlgItem( IDC_CTRL );
	ASSERT( pTemp );

	if( (iKBTable[ iPCKeyVals[m_iPCSelect] ] != AKEY_NONE) && (iKBTable[ iPCKeyVals[m_iPCSelect] ] & AKEY_CTRL) )
		pTemp->SetCheck( 1 );
	else
		pTemp->SetCheck( 0 );

	pTemp = (CButton *)GetDlgItem( IDC_SHIFT );
	ASSERT( pTemp );
	if( (iKBTable[ iPCKeyVals[m_iPCSelect] ] != AKEY_NONE) && (iKBTable[ iPCKeyVals[m_iPCSelect] ] & AKEY_SHFT) )
		pTemp->SetCheck( 1 );
	else
		pTemp->SetCheck( 0 );

	sprintf( szNum, "%3d", iPCKeyVals[ m_iPCSelect ] );
	SetDlgItemText( IDC_VIRTKEY, szNum );

	temp = iAkeyVals[ m_iASelect ];
	
	if( iKBTable[ iPCKeyVals[m_iPCSelect] ] & AKEY_CTRL )
		temp |= AKEY_CTRL;

	if( iKBTable[ iPCKeyVals[m_iPCSelect] ] & AKEY_SHFT )
		temp |= AKEY_SHFT;

	sprintf( szNum, "%3d", temp );
	if( iKBTable[ iPCKeyVals[m_iPCSelect] ] == AKEY_NONE )
		sprintf( szNum, "%s", "N/A" );
	SetDlgItemText( IDC_ICODE, szNum );

	SetDlgItemText( IDC_TEMPLATE_DESCRIPTION, szTemplateDescrip );

	pTemp = (CButton *)GetDlgItem( IDC_USE_TEMPLATE );
	ASSERT( pTemp );
	if( ulMiscStates & ATARI_USE_KEYTEMPLATE )
		pTemp->SetCheck( 1 );
	else
		pTemp->SetCheck( 0 );
}

void CKeyBoard::SetAtariSelect( void )
{
	m_iASelect = AKEY_NONE;
	
	if( iKBTable[ iPCKeyVals[m_iPCSelect] ] == AKEY_NONE )
		m_iASelect = NUM_ATARI_KEY_ELEMENTS - 1;
	
	for( int i=0; i < NUM_ATARI_KEY_ELEMENTS; i++ )
	{
		if( iAkeyVals[i] == (iKBTable[ iPCKeyVals[m_iPCSelect] ] & ~(AKEY_SHFT|AKEY_CTRL)) )
			m_iASelect = i;
	}
}

BOOL CKeyBoard::OnInitDialog() 
{
	CDialog::OnInitDialog();

	ulKeyState = 0;
	if( !(ulMiscStates & ATARI_USE_KEYTEMPLATE) )
	{
		int i;

		for( i=0; i < 256; i++ )
			iKBTable[i] = AKEY_NONE;

		for( i=0; i < NUM_PC_KEY_ELEMENTS; i++ )
		{
			iKBTable[ iPCKeyVals[i] ] = iKBDefault[ i ];
		}

		strcpy( szTemplateDescrip, "Default Atari-style layout" );
	}
	m_iPCSelect = 0;
	SetAtariSelect( );
	UpdateDisplay( );

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CKeyBoard::OnSelchangePckeys() 
{
	CComboBox *comboBox = (CComboBox *)GetDlgItem( IDC_PCKEYS );
	ASSERT( comboBox );

	m_iPCSelect = comboBox->GetCurSel();
	if( m_iPCSelect == CB_ERR )
		m_iPCSelect = 0;

	SetAtariSelect();
	UpdateDisplay();
}

void CKeyBoard::OnCtrl() 
{
	int		nCheck;
	CButton		*pTemp = (CButton *)GetDlgItem( IDC_CTRL );
	ASSERT( pTemp );

	nCheck = pTemp->GetCheck( );
	if( nCheck )
		iKBTable[ iPCKeyVals[m_iPCSelect] ] |= AKEY_CTRL;
	else
		iKBTable[ iPCKeyVals[m_iPCSelect] ] &= ~AKEY_CTRL;
	ulKeyState |= KEY_CHANGED_CONTENTS;
	UpdateDisplay();
}

void CKeyBoard::OnShift() 
{
	int		nCheck;
	CButton		*pTemp = (CButton *)GetDlgItem( IDC_SHIFT );
	ASSERT( pTemp );

	nCheck = pTemp->GetCheck( );
	if( nCheck )
		iKBTable[ iPCKeyVals[m_iPCSelect] ] |= AKEY_SHFT;
	else
		iKBTable[ iPCKeyVals[m_iPCSelect] ] &= ~AKEY_SHFT;
	ulKeyState |= KEY_CHANGED_CONTENTS;
	UpdateDisplay();
}

void CKeyBoard::OnSelchangeAtarikeys() 
{
	CComboBox *comboBox = (CComboBox *)GetDlgItem( IDC_ATARIKEYS );
	ASSERT( comboBox );
	m_iASelect = comboBox->GetCurSel();

	if( m_iASelect < 0 || m_iASelect > NUM_ATARI_KEY_ELEMENTS - 1 )
		m_iASelect = 0;

	iKBTable[ iPCKeyVals[m_iPCSelect] ] = iAkeyVals[ m_iASelect ];
	ulKeyState |= KEY_CHANGED_CONTENTS;
	UpdateDisplay();
}

void CKeyBoard::OnKillfocusTemplateDescription() 
{
	ulKeyState |= KEY_CHANGED_CONTENTS;
	GetDlgItemText( IDC_TEMPLATE_DESCRIPTION, szTemplateDescrip, MAX_PATH );
}

void CKeyBoard::OnSavetemplate() 
{
	CString	name = "";

	if( !(ulScreenMode & DDRAW_WIND) && !(ulScreenMode & DDRAW_NONE) && ((ulScreenMode & DDRAW_320_200) || (ulScreenMode & DDRAW_320_240)) )
	{
		CFileSmallDlg fileDlg;
		if( fileDlg.DoModal() == IDOK )
			name = fileDlg.GetPathName();	
	}
	else
	{
		char BASED_CODE szFilter[] = "Atari keyboard templates (*.a8k)|*.a8k;|All Files (*.*)|*.*||";
		CFileDialog	cfKeyboardFile( FALSE, "a8k", szTemplateFile, OFN_EXPLORER | OFN_OVERWRITEPROMPT, szFilter, this );

		cfKeyboardFile.m_ofn.lpstrTitle = "Select Atari keyboard template file";
		if( cfKeyboardFile.DoModal() == IDOK )
			name = cfKeyboardFile.GetPathName();
	}
	if( name != "" )
	{
		char	szFileName[ MAX_PATH ];
		int		iFile;
		bool	bError = false;

		strncpy( szFileName, name.GetBuffer(0), MAX_PATH-1 );
		name.ReleaseBuffer();
		iFile = open( szFileName, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, 0777 );
		if( iFile != -1 )
		{
			unsigned char	cData = KEYBOARD_TEMPLATE_VERSION;

			if( write( iFile, "A8K", 3 )== -1 )
				bError = true;

			if( write( iFile, &cData, 1 )== -1 )
				bError = true;

			cData = strlen( szTemplateDescrip );
			if( write( iFile, &cData, 1 )== -1 )
				bError = true;

			if( write( iFile, szTemplateDescrip, cData)== -1 )
				bError = true;

			if( write( iFile, iKBTable, 256 * sizeof( int ))== -1 )
				bError = true;

			close( iFile );

			if( !bError )
				strcpy( szTemplateFile, szFileName );
		}
		else
			bError = true;
		
		if( bError )
		{
			char *message = "Save keyboard template failed";
			Aprint( message );
			MessageBox( message, "Atari800Win", MB_OK );
		}
		else
		{
			ulKeyState &= ~KEY_CHANGED_CONTENTS;
			ulKeyState &= ~KEY_LOADED_TEMPLATE;
			ulKeyState |= KEY_SAVED_TEMPLATE;

			WriteRegDWORD( NULL, REG_MISC_STATES, ulMiscStates);
			WriteRegString( NULL, REG_KEY_TEMPLATE, szTemplateFile );
		}
	}
}

void CKeyBoard::OnLoadtemplate() 
{
	CString	name = "";

	if( !(ulScreenMode & DDRAW_WIND) && !(ulScreenMode & DDRAW_NONE) && ((ulScreenMode & DDRAW_320_200) || (ulScreenMode & DDRAW_320_240)) )
	{
		CFileSmallDlg fileDlg;
		if( fileDlg.DoModal() == IDOK )
			name = fileDlg.GetPathName();	
	}
	else
	{
		char BASED_CODE szFilter[] = "Atari keyboard templates (*.a8k)|*.a8k;|All Files (*.*)|*.*||";
		CFileDialog	cfKeyboardFile( TRUE, "a8k", szTemplateFile, OFN_EXPLORER | OFN_FILEMUSTEXIST, szFilter, this );

		cfKeyboardFile.m_ofn.lpstrTitle = "Select Atari keyboard template file";
		if( cfKeyboardFile.DoModal() == IDOK )
			name = cfKeyboardFile.GetPathName();
	}
	if( name != "" )
	{
		char	szFileName[ MAX_PATH ];

		strncpy( szFileName, name.GetBuffer(0), MAX_PATH-1 );
		name.ReleaseBuffer();
		
		if( !ReadKeyTemplate( szFileName ) )
		{
			char *message = "Read keyboard template failed";
			Aprint( message );
			MessageBox( message, "Atari800Win", MB_OK );
		}
		else
			ulKeyState |= KEY_LOADED_TEMPLATE;
	}
	UpdateDisplay();
}

void CKeyBoard::OnUseTemplate() 
{
	CButton *pTemp = (CButton *)GetDlgItem( IDC_USE_TEMPLATE );
	ASSERT( pTemp );

	ulKeyState |= KEY_CHANGED_MODE;

	if( pTemp->GetCheck( ) )
		ulMiscStates |= ATARI_USE_KEYTEMPLATE;
	else
		ulMiscStates &= ~ATARI_USE_KEYTEMPLATE;
}

void CKeyBoard::OnCancel() 
{
	if( ulKeyState & KEY_CHANGED_CONTENTS )
	{
		int	nResult = MessageBox( "You have made changes but did not save the template. Would you like to save it now?", "Atari800Win", MB_YESNO );

		if( nResult = IDYES )
		{
			OnSavetemplate();
		}
	}

	if( (ulKeyState & KEY_CHANGED_MODE) && (ulMiscStates & ATARI_USE_KEYTEMPLATE) )
	{
		if( (ulKeyState & KEY_CHANGED_CONTENTS) && !(ulKeyState & KEY_LOADED_TEMPLATE) && !(ulKeyState & KEY_SAVED_TEMPLATE) )
		{
			int nResult = MessageBox( "You have turned on a template that isn't saved. If you want to save it and continue, hit Yes, otherwise hit No and it will be turned off", "Atari800Win", MB_YESNO );
			if( nResult == IDYES )
			{
				OnSavetemplate();
				if( !(ulKeyState & KEY_SAVED_TEMPLATE) )
					ulMiscStates &= ~ATARI_USE_KEYTEMPLATE;
				else
					WriteRegDWORD( NULL, REG_MISC_STATES, ulMiscStates);
			}
			else
			{
				ulMiscStates &= ~ATARI_USE_KEYTEMPLATE;
			}
		}
		else
			WriteRegDWORD( NULL, REG_MISC_STATES, ulMiscStates);
	}

	if( ulKeyState & KEY_LOADED_TEMPLATE )
		WriteRegString( NULL, REG_KEY_TEMPLATE, szTemplateFile );

	CDialog::OnCancel();
}
