// demoDlg.cpp : implementation file
//

#include "stdafx.h"
#include "demo.h"
#include "demoDlg.h"
#include "TTYSettingsDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDemoDlg dialog

CDemoDlg::CDemoDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CDemoDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CDemoDlg)
	m_radio_control = -1;
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDemoDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDemoDlg)
	DDX_Control(pDX, IDC_SLIDER4, m_controlD);
	DDX_Control(pDX, IDC_SLIDER3, m_controlC);
	DDX_Control(pDX, IDC_SLIDER2, m_controlB);
	DDX_Control(pDX, IDC_SLIDER1, m_controlA);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDemoDlg, CDialog)
	//{{AFX_MSG_MAP(CDemoDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_DESTROY()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_VSCROLL()
	ON_BN_CLICKED(IDC_BUTTON_ABOUT, OnButtonAbout)
	ON_BN_CLICKED(IDC_COM_SETTINGS, OnComSettings)
	ON_BN_CLICKED(IDC_RADIO_NONE, OnRadioNone)
	ON_BN_CLICKED(IDC_RADIO_PLAYBACK_0, OnRadioPlayback0)
	ON_BN_CLICKED(IDC_RADIO_PLAYBACK_1, OnRadioPlayback1)
	ON_BN_CLICKED(IDC_RADIO_SLIDER_LOOP, OnRadioSliderLoop)
	ON_BN_CLICKED(IDC_RADIO_SLIDER_MEMORY, OnRadioSliderMemory)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDemoDlg message handlers

BOOL CDemoDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
    // initialize all the slider controls

    // tty defaluts
    m_bConnected = FALSE;
	m_nBaud = 9600;
	m_nDataBits = 8;
	m_bDTRDSR = FALSE;
	m_nParity = 0;
	m_sPort = "COM1";
	m_bRTSCTS = TRUE;
	m_nStopBits = 0;
	m_bXONXOFF = FALSE;

    // for com port
    memset(&m_osRead, 0, sizeof(OVERLAPPED));
	memset(&m_osWrite, 0, sizeof(OVERLAPPED));
	m_pThread = NULL;
	
    // Set the slider control data
    m_controlA.SetRange(-255,0);
    m_controlA.SetTicFreq(16);
	m_controlA.SetPos(0);

    m_controlB.SetRange(-255,0);
    m_controlB.SetTicFreq(16);
	m_controlB.SetPos(0);


    m_controlC.SetRange(-255,0);
    m_controlC.SetTicFreq(16);
	m_controlC.SetPos(0);

    m_controlD.SetRange(-255,0);
    m_controlD.SetTicFreq(16);
	m_controlD.SetPos(0);

    // Create events for overlapped I/O and WM_COMMNOTIFY synchronization
	if ((m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))
		== NULL) return FALSE;
	if ((m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))
		== NULL) return FALSE;

    // Check None to begin with
    ((CButton *)GetDlgItem(IDC_RADIO_NONE))->SetCheck(1);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CDemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

void CDemoDlg::OnDestroy()
{
    CloseConnection();
	WinHelp(0L, HELP_QUIT);
	CDialog::OnDestroy();
}

void CDemoDlg::OnOK() 
{
	// TODO: Add extra validation here
	CDialog::OnOK();
    CloseConnection();
}
// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CDemoDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CDemoDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CDemoDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default
	
	CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
    ((CButton *)GetDlgItem(IDC_RADIO_SLIDER_MEMORY))->SetCheck(0);
    ((CButton *)GetDlgItem(IDC_RADIO_PLAYBACK_0))->SetCheck(0);
    ((CButton *)GetDlgItem(IDC_RADIO_PLAYBACK_1))->SetCheck(0);
    
    if (3 != m_radio_control)
        ((CButton *)GetDlgItem(IDC_RADIO_NONE))->SetCheck(1);
}

void CDemoDlg::OnButtonAbout() 
{
    CAboutDlg dlgAbout;
    dlgAbout.DoModal();
	// TODO: Add your control notification handler code here
}

void CDemoDlg::OnComSettings() 
{
	// TODO: Add your control notification handler code here
    char buf[34];
    CTTYSettingsDlg TTYSettingsDlg;

    TTYSettingsDlg.m_sBaud = itoa(m_nBaud, buf, 10);
	TTYSettingsDlg.m_sDataBits = itoa(m_nDataBits, buf, 10);
	TTYSettingsDlg.m_bDTRDSR = m_bDTRDSR;
	TTYSettingsDlg.m_nParity = m_nParity;
	TTYSettingsDlg.m_sPort = m_sPort;
	TTYSettingsDlg.m_bRTSCTS = m_bRTSCTS;
	TTYSettingsDlg.m_nStopBits = m_nStopBits;
	TTYSettingsDlg.m_bXONXOFF = m_bXONXOFF;
	TTYSettingsDlg.m_bConnected = m_bConnected;

    if (TTYSettingsDlg.DoModal() == IDOK) {
		m_nBaud = atoi(TTYSettingsDlg.m_sBaud);
		m_nDataBits = atoi(TTYSettingsDlg.m_sDataBits);
		m_bDTRDSR = TTYSettingsDlg.m_bDTRDSR;
		m_nParity = TTYSettingsDlg.m_nParity;
	    if (!m_bConnected) m_sPort = TTYSettingsDlg.m_sPort;
		m_bRTSCTS = TTYSettingsDlg.m_bRTSCTS;
		m_nStopBits = TTYSettingsDlg.m_nStopBits;
		m_bXONXOFF = TTYSettingsDlg.m_bXONXOFF;

        if (!OpenConnection()) {
		    AfxMessageBox(IDS_CONNECTION_FAILED);
            CloseConnection();
		}
	}
}

// Com Port setup
BOOL CDemoDlg::SetupConnection()
{
	BOOL fRetVal;
	DCB dcb;

	dcb.DCBlength = sizeof(DCB);
	GetCommState(m_idComDev, &dcb);
	dcb.BaudRate = m_nBaud;
	dcb.ByteSize = m_nDataBits;
	switch (m_nParity)
	{
		case 0: dcb.Parity = NOPARITY; break;
		case 1: dcb.Parity = EVENPARITY; break;
		case 2: dcb.Parity = ODDPARITY; break;
		case 3: dcb.Parity = MARKPARITY; break;
		case 4: dcb.Parity = SPACEPARITY; break;
		default: ASSERT(FALSE);
	}
	switch (m_nStopBits)
	{
		case 0: dcb.StopBits = ONESTOPBIT; break;
		case 1: dcb.StopBits = ONE5STOPBITS; break;
		case 2: dcb.StopBits = TWOSTOPBITS; break;
		default: ASSERT(FALSE);
	}
	dcb.fOutxDsrFlow = m_bDTRDSR;
	dcb.fOutxCtsFlow = m_bRTSCTS;
	dcb.fDtrControl = m_bDTRDSR ? DTR_CONTROL_HANDSHAKE : DTR_CONTROL_ENABLE;
	dcb.fRtsControl = m_bRTSCTS ? RTS_CONTROL_HANDSHAKE : RTS_CONTROL_ENABLE;
	dcb.fInX = dcb.fOutX = m_bXONXOFF;
	dcb.XonChar = ASCII_XON;
	dcb.XoffChar = ASCII_XOFF;
	dcb.XonLim = 100;
	dcb.XoffLim = 100;
	dcb.fBinary = TRUE;
	dcb.fParity = TRUE;
	fRetVal = SetCommState(m_idComDev, &dcb);
	return fRetVal;
}

// Write Byte out to port
BOOL CDemoDlg::WriteCommByte(char cChar)
{
	BOOL fWriteStat;
	DWORD dwBytesWritten;

	if (!m_bConnected) return FALSE;
	fWriteStat = WriteFile(m_idComDev, &cChar, 1, &dwBytesWritten, &m_osWrite);
	if (!fWriteStat && (GetLastError() == ERROR_IO_PENDING))
	{
		if (WaitForSingleObject(m_osWrite.hEvent, 1000)) dwBytesWritten = 0;
		else
		{
			GetOverlappedResult(m_idComDev, &m_osWrite, &dwBytesWritten, FALSE);
			m_osWrite.Offset += dwBytesWritten;
		}
	}
	return TRUE;
}

// Read Byte from the Port
int CDemoDlg::ReadCommBlock(LPSTR lpBlock, int nMaxLength)
{
	BOOL fReadStat;
	COMSTAT ComStat;
	DWORD dwErrorFlags, dwLength;

	if (!m_bConnected) return 0;
	ClearCommError(m_idComDev, &dwErrorFlags, &ComStat);
	dwLength = min((DWORD)nMaxLength, ComStat.cbInQue);
	if (dwLength > 0) {
		fReadStat = ReadFile(m_idComDev, lpBlock, dwLength, &dwLength, &m_osRead);

		if (!fReadStat) {
			if (GetLastError() == ERROR_IO_PENDING) {
				if (WaitForSingleObject(m_osRead.hEvent, 1000)) dwLength = 0;
				else {
					GetOverlappedResult(m_idComDev, &m_osRead,
											&dwLength, FALSE);
					m_osRead.Offset += dwLength;
				}
			}
			else dwLength = 0;
		}
	}
	return dwLength;
}

// Start a Comm Connection
BOOL CDemoDlg::OpenConnection()
{
	BOOL fRetVal;
	COMMTIMEOUTS CommTimeOuts;

	if (m_bConnected) return FALSE;

	// Open COM port for overlapped input and output
	if ((m_idComDev = CreateFile(m_sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL))
			== (HANDLE)-1)
		return FALSE;
	SetCommMask(m_idComDev, EV_RXCHAR);
	
    // Set up 4k buffers for both reading and writing
	SetupComm(m_idComDev, 4096, 4096);
	CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
	CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
	CommTimeOuts.ReadTotalTimeoutConstant = 0;
	CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
	CommTimeOuts.WriteTotalTimeoutConstant = 5000;
	SetCommTimeouts(m_idComDev, &CommTimeOuts);
	fRetVal = SetupConnection();

	if (fRetVal) {
    	m_bConnected = TRUE;
		EscapeCommFunction(m_idComDev, SETDTR);
	}
	else {
		m_bConnected = FALSE;
		CloseHandle(m_idComDev);
	}

	return fRetVal;
}

// All Done 
void CDemoDlg::CloseConnection()
{
	if (!m_bConnected) return;
	m_bConnected = FALSE;
	SetCommMask(m_idComDev, 0);

	// Wait for secondary thread to die -force radio control to be other that 3
    m_radio_control = 0;
	if (m_pThread) WaitForSingleObject(m_pThread->m_hThread, INFINITE);
	m_pThread = NULL;

	EscapeCommFunction(m_idComDev, CLRDTR);

	PurgeComm(m_idComDev, PURGE_TXABORT | PURGE_RXABORT |
						  PURGE_TXCLEAR | PURGE_RXCLEAR);
	CloseHandle(m_idComDev);
}

void CDemoDlg::OnRadioNone() 
{
	// TODO: Add your control notification handler code here
    m_radio_control = 5;
    if (!m_bConnected)
	{
        if (!OpenConnection())
		{
			AfxMessageBox(IDS_CONNECTION_FAILED);
            CloseConnection();
		}
	}
}

void CDemoDlg::OnRadioPlayback0() 
{
	// TODO: Add your control notification handler code here
    char buf[2];

    m_radio_control = 1;
    if (!m_bConnected)	{
        if (!OpenConnection())	{
			AfxMessageBox(IDS_CONNECTION_FAILED);
            CloseConnection();
            return;
        }
	}
    
    // Send P 0 
    WriteCommByte('\r');
    WriteCommByte('P');
    WriteCommByte('0');
    WriteCommByte('\r');
    while(ReadCommBlock(buf,1));
   
}

void CDemoDlg::OnRadioPlayback1() 
{
	// TODO: Add your control notification handler code here
    char buf[2];

	m_radio_control = 2;
    if (!m_bConnected) {
        if (!OpenConnection()) {
			AfxMessageBox(IDS_CONNECTION_FAILED);
            CloseConnection();
            return;
		}
    }
    
    // Send P 1
    WriteCommByte('\r');
    WriteCommByte('P');
    WriteCommByte('1');
    WriteCommByte('\r');
    while(ReadCommBlock(buf,1));
}

void CDemoDlg::OnRadioSliderLoop() 
{
	// TODO: Add your control notification handler code here
	m_radio_control = 3;

    if (!m_bConnected)	{
        if (!OpenConnection()) {
			AfxMessageBox(IDS_CONNECTION_FAILED);
            CloseConnection();
            return;
		}
    }
    
    // if thread already running then return
    if (m_pThread) return;

    // Start a thread to send data in a loop
    if ((m_pThread = AfxBeginThread(SendSliderDataInLoop, this)) == NULL) {
		CloseHandle(m_idComDev);
	}
    else {
		m_pThread->SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
		m_pThread->ResumeThread();
    }
    return;
}

void CDemoDlg::OnRadioSliderMemory() 
{
    char buf[60];
    int i;

	// TODO: Add your control notification handler code here
	m_radio_control = 4;
    if (!m_bConnected) {
        if (!OpenConnection())	{
			AfxMessageBox(IDS_CONNECTION_FAILED);
            CloseConnection();
            return;
		}
    }

    // Send slider data to memory
    sprintf(buf, "\rW 0 %x \rW 1 %x \rW 2 %x \rW 3 %x\r", 
                                    abs(m_controlA.GetPos()), abs(m_controlB.GetPos()), 
                                    abs(m_controlC.GetPos()), abs(m_controlD.GetPos()));    
    
    for (i=0; buf[i] != '\0'; i++)
        WriteCommByte(buf[i]);
    
    while(ReadCommBlock(buf,1));
}

// Thread to send data in a loop until user chooses another radio
UINT SendSliderDataInLoop(LPVOID lpParam)
{
    CDemoDlg *demoDlg = (CDemoDlg *)lpParam;
    char buf[60];
    int i;

    // while the user has pressed the loop radio button
    while ( 3 == demoDlg->m_radio_control) {

        sprintf(buf, "\rD %x %x %x %x\r", 
                                    abs(demoDlg->m_controlA.GetPos()), abs(demoDlg->m_controlB.GetPos()), 
                                    abs(demoDlg->m_controlC.GetPos()), abs(demoDlg->m_controlD.GetPos()));    
    
        for (i=0; buf[i] != '\0'; i++)
            demoDlg->WriteCommByte(buf[i]);

        while(demoDlg->ReadCommBlock(buf,1));
    }

    return TRUE;
}


