// wwcommDlg.cpp : インプリメンテーション ファイル
//

#include "stdafx.h"
#include "wwcomm.h"
#include "wwcommDlg.h"

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

/////////////////////////////////////////////////////////////////////////////
// アプリケーションのバージョン情報で使われている CAboutDlg ダイアログ

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

// ダイアログ データ
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard は仮想関数のオーバーライドを生成します
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV のサポート
	//}}AFX_VIRTUAL

// インプリメンテーション
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)
		// メッセージ ハンドラがありません。
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWwcommDlg ダイアログ

CWwcommDlg::CWwcommDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CWwcommDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CWwcommDlg)
	m_sEdit02 = _T("");
	//}}AFX_DATA_INIT
	// メモ: LoadIcon は Win32 の DestroyIcon のサブシーケンスを要求しません。
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CWwcommDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CWwcommDlg)
	DDX_Control(pDX, IDC_EDIT1, m_wndEdit01);
	DDX_Text(pDX, IDC_EDIT2, m_sEdit02);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CWwcommDlg, CDialog)
	//{{AFX_MSG_MAP(CWwcommDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_COMMAND(IDM_EXIT, OnExit)
	ON_COMMAND_RANGE(IDM_COM1, IDM_COM3, OnMenucom)
	ON_COMMAND(IDM_PORTCONFIG, OnPortconfig)
	ON_COMMAND(IDM_CLEAR, OnClear)
	ON_COMMAND(IDM_HELP, OnHelp)
	ON_MESSAGE(PM_USER_RECV, OnRecv)
	ON_EN_CHANGE(IDC_EDIT2, OnChangeEdit2)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWwcommDlg メッセージ ハンドラ

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

	// "バージョン情報..." メニュー項目をシステム メニューへ追加します。

	// IDM_ABOUTBOX はコマンド メニューの範囲でなければなりません。
	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);
			pSysMenu->EnableMenuItem(SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
			pSysMenu->EnableMenuItem(SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
		}
	}

	// このダイアログ用のアイコンを設定します。フレームワークはアプリケーションのメイン
	// ウィンドウがダイアログでない時は自動的に設定しません。
	SetIcon(m_hIcon, TRUE);			// 大きいアイコンを設定
	SetIcon(m_hIcon, FALSE);		// 小さいアイコンを設定
	
	// TODO: 特別な初期化を行う時はこの場所に追加してください。
	static char	ss[64];

	((CButton*)GetDlgItem(IDOK))->EnableWindow(FALSE);
	CMenu*	pMenu = GetMenu();
	pMenu->CheckMenuItem(IDM_COM1, MF_CHECKED);
	pMenu->EnableMenuItem(IDM_PORTCONFIG, MF_BYCOMMAND | MF_GRAYED);
	if((m_rs.buffer = (char*)malloc(BSIZE)) == NULL){
		AfxMessageBox(_T("Memory Allocation Error."), MB_OK, NULL);
		return TRUE;
	}
	m_nComport = 0;
	m_rs.bCont= false;
	m_rs.hCom= INVALID_HANDLE_VALUE;
	m_sCom= _T("COM1");
	OpenCommPort(m_sCom);
	m_iCount= 0;
	wsprintf(ss, "WonderWitch (%s)", m_sCom);
	AfxGetMainWnd()->SetWindowText(ss);

	return TRUE;  // TRUE を返すとコントロールに設定したフォーカスは失われません。
}

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

// もしダイアログボックスに最小化ボタンを追加するならば、アイコンを描画する
// コードを以下に記述する必要があります。MFC アプリケーションは document/view
// モデルを使っているので、この処理はフレームワークにより自動的に処理されます。

void CWwcommDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 描画用のデバイス コンテキスト

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

		// クライアントの矩形領域内の中央
		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;

		// アイコンを描画します。
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// システムは、ユーザーが最小化ウィンドウをドラッグしている間、
// カーソルを表示するためにここを呼び出します。
HCURSOR CWwcommDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CWwcommDlg::OnExit() 
{
	// TODO: この位置にコマンド ハンドラ用のコードを追加してください
	OnCancel() ;
}

void CWwcommDlg::OnCancel() 
{
	// TODO: この位置に特別な後処理を追加してください。
	if(m_rs.hCom != INVALID_HANDLE_VALUE){
		ThreadOff();
		::PurgeComm(m_rs.hCom, PURGE_RXCLEAR);
		::CloseHandle(m_rs.hCom);
	}
	CDialog::OnCancel();
}

void CWwcommDlg::OnHelp() 
{
	// TODO: この位置にコマンド ハンドラ用のコードを追加してください
	CAboutDlg aboutDlg;
	aboutDlg.DoModal();
}

void CWwcommDlg::OnOK() 
{
	// TODO: この位置にその他の検証用のコードを追加してください
	char	bu[256], *bx, c;
	DWORD	tp= 0, l, i, count;

	if(MkMsgline()) RSSend();

	for(i=0; i<256; i++) bu[i]= 0;
	bx= bu;
	*bx++= '<';
	count= strlen(m_strMLine);
	do {
		c= m_strMLine[tp];
		if(isprint(c)){
			*bx= c;
		}
		else if(c == 0x0d){
			sprintf(bx, "%c\x0d\x0a\x0d\x0a", c);
			bx += 5;
		}
		else{
			wsprintf(bx, "[%02x]", c);
			bx += 3;
		}
		bx++;
		if(++tp >= 256) tp= 0;
		count--;
	} while(count) ;
	l= m_wndEdit01.GetWindowTextLength();
	m_wndEdit01.SetSel(l, -1, FALSE);
	m_wndEdit01.ReplaceSel(bu, FALSE);
	UpdateData(FALSE);
//	CDialog::OnOK();
}

void CWwcommDlg::OpenCommPort(LPCTSTR com)
{
	static DCB	dcb;
	SECURITY_ATTRIBUTES	sa;
	COMMTIMEOUTS		cto;
	CMenu*	pMenu = GetMenu();

	if(m_rs.hCom != INVALID_HANDLE_VALUE){
		ThreadOff();
		::CloseHandle(m_rs.hCom);
	}
	sa.nLength= sizeof(sa);
	sa.lpSecurityDescriptor= NULL;
	sa.bInheritHandle= TRUE;
	m_rs.hCom= ::CreateFile(com, GENERIC_READ | GENERIC_WRITE,
						0, &sa, OPEN_EXISTING,
						FILE_FLAG_OVERLAPPED, NULL);
	if(m_rs.hCom == INVALID_HANDLE_VALUE){
		char	s[255];
		((CButton*)GetDlgItem(IDOK))->EnableWindow(FALSE);
		wsprintf(s, "Cannot open communication port.(%s)", com);
		MessageBox(s, com, MB_OK);

		return ;
	}
	pMenu->EnableMenuItem(IDM_PORTCONFIG, MF_BYCOMMAND | MF_ENABLED);
	dcb.DCBlength= sizeof(DCB);
	::GetCommState(m_rs.hCom, &dcb);
	dcb.BaudRate= 38400;
	dcb.fOutxCtsFlow= 1;
	dcb.fOutxDsrFlow= 0;
	dcb.fDtrControl= 1;
	dcb.fRtsControl= 2;
	dcb.ByteSize= 8;
	dcb.Parity= NOPARITY;
	dcb.StopBits= ONESTOPBIT;
	::SetCommState(m_rs.hCom, &dcb);
	::EscapeCommFunction(m_rs.hCom, SETDTR | CLRRTS);
	::GetCommTimeouts(m_rs.hCom, &cto);
	cto.ReadTotalTimeoutMultiplier= 5;
	cto.ReadTotalTimeoutConstant= 3000;
	cto.WriteTotalTimeoutMultiplier= 5;
	cto.WriteTotalTimeoutConstant= 5000;
	::SetCommTimeouts(m_rs.hCom, &cto);
	m_rs.dwMask= NULL;
	ThreadOn();

	return ;
}

void CWwcommDlg::ThreadOn() 
{
	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
	m_rs.bCont= true;
	m_rs.dwTop= m_rs.lCounts= 0;
	m_pThread= AfxBeginThread(ThreadFunc, &m_rs);
	((CButton*)GetDlgItem(IDOK))->EnableWindow(TRUE);
	CMenu*	pMenu = GetMenu();
	pMenu->EnableMenuItem(IDM_PORTCONFIG, MF_BYCOMMAND | MF_ENABLED);
}

void CWwcommDlg::ThreadOff() 
{
	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
	::PurgeComm(m_rs.hCom, PURGE_RXCLEAR);
	if(m_rs.dwMask){
		m_rs.bCont= false;
		::SetCommMask(m_rs.hCom, 0);
		::WaitForSingleObject(m_pThread->m_hThread, 5000);
	}
	((CButton*)GetDlgItem(IDOK))->EnableWindow(FALSE);
	CMenu*	pMenu = GetMenu();
	pMenu->EnableMenuItem(IDM_PORTCONFIG, MF_BYCOMMAND | MF_GRAYED);
}

void CWwcommDlg::OnMenucom(UINT uID)
{
	static char s[8], ss[64];
	CMenu*	pMenu = GetMenu();
	pMenu->CheckMenuItem(IDM_COM1 +m_nComport, MF_UNCHECKED);
	pMenu->CheckMenuItem(uID, MF_CHECKED);
	m_nComport = uID - IDM_COM1;
	wsprintf(s, "COM%d", m_nComport + 1);
	m_sCom= s;
	wsprintf(ss, "WonderWitch (%s)", m_sCom);
	AfxGetMainWnd()->SetWindowText(ss);
	OpenCommPort(m_sCom);
}

void CWwcommDlg::OnPortconfig() 
{
	// TODO: この位置にコマンド ハンドラ用のコードを追加してください
	static COMMCONFIG	ccf;
	ccf.dwSize= sizeof(COMMCONFIG);
	ccf.wVersion= 0x100;
	ccf.dcb.DCBlength= sizeof(ccf.dcb);
	::GetCommState(m_rs.hCom, &(ccf.dcb));
	if(::CommConfigDialog(m_sCom, CWnd::m_hWnd, &ccf)){
		::SetCommState(m_rs.hCom, &(ccf.dcb));
	}
}

void CWwcommDlg::OnClear() 
{
	// TODO: この位置にコマンド ハンドラ用のコードを追加してください
	m_wndEdit01.SetWindowText(_T("")) ;
	m_sEdit02 = _T("");
	UpdateData(FALSE);
	m_iCount= 0;
	m_rs.csCSection.Lock();
	m_rs.dwTop= m_rs.lCounts= 0;
	m_rs.csCSection.Unlock();
	return ;
}

UINT CWwcommDlg::ThreadFunc(LPVOID pParam)
{
	struct tagComm	*p_rs= (struct tagComm*)pParam;
	DWORD	dwEvent, dwErrorMask, dwReadCount;
	DWORD	sz, s, btm, lest;
	char	b[1024];
	char	*ch;
	COMSTAT	cst;

	::EscapeCommFunction(p_rs->hCom, SETRTS);
	dwErrorMask= dwReadCount= 0;
	if((p_rs->dwMask) != EV_RXCHAR){
		p_rs->dwMask= EV_RXCHAR;
		::SetCommMask(p_rs->hCom, EV_RXCHAR);
	}
	while(p_rs->bCont){
		if(::WaitCommEvent(p_rs->hCom, &dwEvent, NULL)){
			::ClearCommError(p_rs->hCom, &dwErrorMask, &cst);
			if(!(dwEvent & EV_RXCHAR)  || cst.cbInQue == 0) continue;
			sz = cst.cbInQue;
			while(sz){
				if(sz > 1024) s= 1024; else s= sz;
				::ReadFile(p_rs->hCom, b, s, &dwReadCount, NULL);
				p_rs->csCSection.Lock();
				if((btm= p_rs->dwTop +p_rs->lCounts) >= BSIZE) btm -= BSIZE;
				ch= (char*)(p_rs->buffer + btm);
				lest= BSIZE - btm;
				if(lest > dwReadCount){
					memcpy(ch, b, dwReadCount);
					btm += dwReadCount;
				}
				else{
					memcpy(ch, b, lest);
					memcpy(p_rs->buffer, b +lest, dwReadCount -lest);
					btm= dwReadCount -lest;
				}
				if((p_rs->lCounts += dwReadCount) > BSIZE){
					p_rs->lCounts= BSIZE;
					if((p_rs->dwTop = btm +1) >= BSIZE) p_rs->dwTop= 0;
				}
				p_rs->csCSection.Unlock();
				sz -= dwReadCount;
			}
		}
		AfxGetMainWnd()->PostMessage(PM_USER_RECV, 0, 0);
	}
	::PurgeComm(p_rs->hCom, PURGE_RXCLEAR);
	p_rs->dwMask= NULL;
	::EscapeCommFunction(p_rs->hCom, CLRRTS);
	return	0;
}

void CWwcommDlg::OnRecv(WPARAM wParam, LPARAM lParam)
{
	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
	static char	bu[1024], *bx, c;
	static bool		f= true;
	DWORD		tp, l, i;
	CString		s1;
/*
	m_rs.csCSection.Lock();
	while(m_rs.lCounts > 0 && m_rs.buffer[m_rs.dwTop] != 0x02){
		if(++(m_rs.dwTop) >= BSIZE) m_rs.dwTop= 0;
		m_rs.lCounts--;
	}
	m_rs.csCSection.Unlock();
*/
	if(m_rs.lCounts <= 0) return ;

	for(i=0; i<1024; i++) bu[i]= 0;
	bx= bu;
	tp= m_rs.dwTop;
	m_rs.csCSection.Lock();
	do {
		c= m_rs.buffer[tp];
		if(f){
			*bx++= '>';
			f= false;
		}
		if(isprint(c)){
			*bx= c;
		}
		else if(c == 0x0d){
			*bx++= c;
			*bx= 0x0a;
			f= true;
		}
		else{
			wsprintf(bx, "[%02x]", (unsigned char)c);
			bx += 3;
		}
		bx++;
		if(++tp >= BSIZE) tp= 0;
		m_rs.lCounts--;
	} while(m_rs.lCounts) ;
	m_rs.csCSection.Unlock();
	l= m_wndEdit01.GetWindowTextLength();
	m_wndEdit01.SetSel(l, -1, FALSE);
	m_wndEdit01.ReplaceSel(bu, FALSE);

	UpdateData(FALSE);
	::Sleep(0);
	return ;
}

void CWwcommDlg::RSSend()
{
	unsigned long	c= 0;
	char		b[256], *bx;
	bx= m_strMLine;
	if(!WriteFile(m_rs.hCom, bx, strlen(bx), &c, NULL)){
		wsprintf(b, "Errorode <%d>", GetLastError());
		MessageBox(b, "error occurd.", MB_OK);
	}
	return ;
}

int CWwcommDlg::MkMsgline()
{
	char	*bx, n[3];
	bool	fl= false, b= true;
	int		c, k= 0, t= 1;

	UpdateData(TRUE);
	bx= m_sEdit02.GetBuffer(800);
	c= m_sEdit02.GetLength();
	if(!c) return 0;
	while(c){
		if(*bx == '\\' && c > 1){
			b= false;
			bx++;
			c--;
			continue;
		}
		if(*bx == '[' && b){
			bx++;
			c--;
			fl= true;
			continue;
		}
		b= true;
		if(fl){
			if(*bx == ']'){
				if(!t) sscanf(n, "%x", &(m_strMLine[k++]));
				fl= false; bx++;
				t= 1;
				c--;
				continue;
			}
			if(t--){
				strncpy(n, "\x0\x0\x0", 3);
				if(isxdigit(*bx)){
					n[0]= *(bx++);
					c--;
					continue;
				}
			}
			else{
				n[1]= *(bx++);
				sscanf(n, "%x", &(m_strMLine[k++]));
				t= 1;
			}
		}
		else{
			m_strMLine[k++]= *(bx++);
		}
		c--;
		m_strMLine[k]= 0;
	}
	return 1;
}

void CWwcommDlg::OnChangeEdit2() 
{
	// TODO: これが RICHEDIT コントロールの場合、コントロールは、 lParam マスク
	// 内での論理和の ENM_CHANGE フラグ付きで CRichEditCrtl().SetEventMask()
	// メッセージをコントロールへ送るために CDialog::OnInitDialog() 関数をオーバー
	// ライドしない限りこの通知を送りません。
	
	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
	UpdateData(TRUE);
}
