/******************************************************************************
 * Name:        scd_telnet.cpp
 * Purpose:     add SSH / telnet ability to scd_terminal.cpp
 * Author:      Xie, Chun-Da (jakky1)
 * E-mail:      jakky1@gmail.com
 * Created:     2004.7
 * Copyright:   (C) 2004 Xie, Chun-Da
 * Licence:     GPL : http://www.gnu.org/licenses/gpl.html
 * Modified by:
 ******************************************************************************/


#ifndef SCD_TELNET_CPP
#define SCD_TELNET_CPP
#include "scd_telnet.h"

#include "scd_terminal.h"
//#include "main.h"
bool debugging = false;
// ============================================================================
#ifdef DEBUG_NEGOTIATION
//#undef DEBUG_NEGOTIATION
#endif
BEGIN_EVENT_TABLE(SCD_Telnet, SCD_Telnet)
	//EVT_SOCKET(0 , SCD_Telnet::OnSocketEvent)
END_EVENT_TABLE()

SCD_Telnet::SCD_Telnet(wxWindow *win,int sockid,wxScrollBar *sb) 
	: SCD_Terminal(win,sb)
{
//	local_buf_len = 0;
	ticks =0;
	powner=win;
	connect_state = 0;
	bluserquery = 0;
	blMultibyteWordDecetionEnabled = true;
	blSupportNAWS = false;
	negoted = false;
	InitSocket(sockid);
}
// ----------------------------------------------------------------------------
SCD_Telnet::~SCD_Telnet()
{
//	close();

	//不可以直接 delete sock
 	//因為 sock.Destroy 可以防止 sock 在 destroy 之後又接收到 socket event
	DestroySocket();
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------

void SCD_Telnet::InitSocket(int sockid)
{
/*
	sock.SetTimeout(0);
	sock.SetFlags(wxSOCKET_NOWAIT);
	sock.SetClientData(this);

	sock.SetNotify( wxSOCKET_INPUT_FLAG | wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG );
	sock.SetEventHandler(*this, 0);
*/
	sock.SetClientData(powner);
	sock.SetEventHandler(*powner, sockid);
}
// ----------------------------------------------------------------------------
bool SCD_Telnet::SocketConnect()
{
	ResetTerminal();
	bluserquery = false;
	ssh_login_try_times = -1;
	return sock.Connect(site_info);
}

bool SCD_Telnet::SocketConnect(wxIPV4address& address, bool wait, wxString _username)
{
//	sock.Notify(true);
	ResetTerminal();
	bluserquery = false;
	ssh_login_try_times = -1;
	return sock.Connect(address, wait, _username);
}
// ----------------------------------------------------------------------------
void SCD_Telnet::CloseSocket()
{
	sock.Close();
//	sock.Notify(false);

	//由於 sock.close() 並不會產生 SocketEvent, 所以要手動產生
	wxSocketEvent e(0);
//	e.SetEventObject( &sock );
	e.m_event = wxSOCKET_LOST;
	e.m_clientData = SocketGetClientData();
	OnSocketEvent(e);
}
// ----------------------------------------------------------------------------
inline void SCD_Telnet::DestroySocket()
{
	CloseSocket();
	//不可以直接 delete sock
 	//因為 sock.Destroy 可以防止 sock 在 destroy 之後又接收到 socket event
	sock.Destroy();
}
// ----------------------------------------------------------------------------
bool SCD_Telnet::SocketIsConnected()
{	return sock.IsConnected();	}
// ----------------------------------------------------------------------------
void* SCD_Telnet::SocketGetClientData()
{	return sock.GetClientData();	}
// ----------------------------------------------------------------------------
int SCD_Telnet::SocketRead(void *buf, int len)
{
/*
	int c = 0;	//填入了多少資料進入 buf 了 ?

	if( local_buf_len >= len )
	{
		sock.Read( buf, len );
		local_buf_len -= len;
		c = len;
	}
	else
 	{
 		sock.Read( buf, local_buf_len );
 		len -= local_buf_len;
 		sock.Read( (char*)buf + local_buf_len , len );
  		c = local_buf_len + sock.LastCount();
  		local_buf_len = 0;
	}

	return c;
*/
	return sock.Read(buf, len);
}
// ----------------------------------------------------------------------------
void SCD_Telnet::SocketUnread(void *buf, int len)
{
	sock.Unread( buf, len );
//	local_buf_len += len;
}
// ----------------------------------------------------------------------------
void SCD_Telnet::SocketWrite(void *buf, int len)
{
	sock.Write(buf, len);
	UnIdleServer();
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
int SCD_Telnet::getServerIdleTime()	//取得閒置的時間
{
	if( ! IsConnected() )	return -1;
	return wxGetLocalTime() - server_idle_time;
}
// ----------------------------------------------------------------------------
int SCD_Telnet::getUserIdleTime()	//取得閒置的時間
{
	if( ! IsConnected() )	return -1;
	return wxGetLocalTime() - user_idle_time;
}
// ----------------------------------------------------------------------------
void SCD_Telnet::UnIdleServer()	//解除閒置過久的狀態
{
	server_idle_time = wxGetLocalTime();	//紀錄開始閒置的時間
}
// ----------------------------------------------------------------------------
void SCD_Telnet::UnIdleUser()	//解除閒置過久的狀態
{
	user_idle_time = wxGetLocalTime();	//紀錄開始閒置的時間
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_Telnet::OnServerSupportNAWS()	//在得知 server 有支援長螢幕時
{	blSupportNAWS = true;	/*wxMessageBox( name );*/	OnResize();	}
// ----------------------------------------------------------------------------
void SCD_Telnet::OnLogined()	//在使用者剛登入時
{	if( ! blSupportNAWS )	setColumnRow(80,24);	}
// ----------------------------------------------------------------------------
bool SCD_Telnet::SetNAWS(int _c, int _r)	//改變 telnet 連線的行列數
{
//	if( ! blSupportNAWS )	return false;

	int col_count , row_count;
	getColumnRowCount( &col_count , &row_count );
	if( _c == col_count && _r == row_count && sock.GetType() != SOCK_EXECMD)
			return true;

	if ( sock.GetType() == SOCK_SSH ) {
			(sock.GetSSH())->ReSize(_c,_r);
	}else if ( sock.GetType() == SOCK_TELNET ) {//SOCK_TELNET
			(sock.GetTelnet())->ReSize(_c,_r);
			/*
		unsigned char buf[10];
		buf[0] = '\xff';	buf[1] = '\xfa';	buf[2] = '\x1f';
		buf[3] = '\x00';	buf[4] = (char)_c;	buf[5] = '\x00';
		buf[6] = (char)_r;	buf[7] = '\xff';	buf[8] = '\xf0';
	
		UserSend( (char*)buf, 9);
		*/
	}else if ( sock.GetType() == SOCK_EXECMD ){
		(sock.GetEXEC())->ReSize(_c,_r);
	}

	setColumnRow( _c , _r );
	repaint(true);

	return true;
}
// ----------------------------------------------------------------------------
void SCD_Telnet::Show()
{
	OnResize();
	SCD_Terminal::Show();
}
// ----------------------------------------------------------------------------
void SCD_Telnet::OnResize()
{
	wxFont fnt = GetCurrentFont();

	if( blSupportNAWS && isEnableNaws() )
	{
		SetFont( fnt );

		wxSize cs = getCharSize();
		//wxSize new_winsize = getParentWindow()->GetClientSize();
		wxSize new_winsize = getParentWindow()->GetSize();
		int nc = new_winsize.GetWidth() / cs.GetWidth();
		int nr = new_winsize.GetHeight() / cs.GetHeight();
		//if( nc < 80 ) {	nc = 80;}
		//if( nr < 24 ) { nr = 24;}

		//getParentWindow()->SetClientSize(x_len,y_len);
		int y_len = nr * cs.GetHeight();
		int x_len = nc * cs.GetWidth();
#ifdef __WXDEBUG__
		wxLogMessage("client size row %d col %d, x %d y %d",nr,nc,x_len,y_len);
#endif
		SetNAWS(nc, nr);
	}
	else
	{
		fnt.SetPointSize( GetVarFontSize() );
		SetFont(fnt);

		SetNAWS(80, 24);
	}
	repaint(true);
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_Telnet::UserSend(char *buf, int len = -1)
{
	if( len < 0 )	len = strlen( (char*)buf);

#ifdef __WXDEBUG__
	{
		wxString tmp_str;
		for(int i=0;i<len;i++)
		 if ( isprint(buf[i]) || isspace(buf[i]) ||
			isalnum(buf[i]) || iscntrl(buf[i]) ) {
		 	tmp_str += wxString::Format("%d %c", buf[i], buf[i]);
		 }else {
		 	tmp_str += wxString::Format("(%0x)", buf[i]);
		 }
		//input[] = '\0';
		//wxLogMessage("UserSend:" + tmp_str);
		//wxLogMessage(wxString(buf));
	}
	//wxLogMessage( wxString::Format("UserSend : ", (unsigned int)*buf, (char)key) );
#endif
	SocketWrite( buf, len );
}
// ----------------------------------------------------------------------------
void SCD_Telnet::UserSend(wxString buf)
{
    UserSend( wxStringToCharPtr(buf) );
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_Telnet::UserSend_spacial(wxString msg)	//送出格式化字串
{
	/*
		\n		enter
		\^R		ctrl+R
		\#{u,d,l,r}		上下左右
		\p1		pause
		\\		\
	*/

	wxString ret;
	ret.Empty();

	while(true)
	{
		ret += msg.BeforeFirst('\\');
		msg = msg.AfterFirst('\\');

		if( msg.IsEmpty() )  break;
		else //如果有控制字元
		{
	  		wxChar ch = msg.GetChar(0);
	  		switch(ch)
	  		{
         		case '#':
					{
                   		char b = (char) msg.GetChar(1);
						b = tolower(b);
                   		if( b == 'u' )
                   		{
							UserSend(ret);	ret.Empty();
							keyUp();
	    	           		msg = msg.Right( msg.Length() - 2 );
						}
                   		else if( b == 'd' )
                   		{
							UserSend(ret);	ret.Empty();
							keyDown();
	    	           		msg = msg.Right( msg.Length() - 2 );
						}
                   		else if( b == 'l' )
                   		{
							UserSend(ret);	ret.Empty();
							keyLeft();
	    	           		msg = msg.Right( msg.Length() - 2 );
						}
                   		else if( b == 'r' )
                   		{
							UserSend(ret);	ret.Empty();
							keyRight();
	    	           		msg = msg.Right( msg.Length() - 2 );
						}
						else
						{
          					ret += _T("\\");
						}
					}
               		break;
         		case 'n':
					UserSend(ret);	ret.Empty();
					keyEnter();
               		msg = msg.Right( msg.Length() - 1 );
               		break;
         		case '^':
					{
                   		char b = (char) msg.GetChar(1);
						b = tolower(b);
                   		if( b >= 'a' && b <= 'z' )
                   		{
							UserSend(ret);	ret.Empty();
							keyControl( b );
	    	           		msg = msg.Right( msg.Length() - 2 );
						}
						else
						{
		               		ret += _T("\\^");
        		       		msg = msg.Right( msg.Length() - 1 );
						}
					}
               		break;
         		case '\\':
               		ret += _T("\\");
               		msg = msg.Right( msg.Length() - 1 );
               		break;
         		case 'p':
               		{
                     	wxChar sleep_sec_ch = msg.GetChar(1);
                     	int sleep_sec = 0;
                     	if( sleep_sec_ch >= '0' && sleep_sec_ch <= '9' )
                     	{
                     		sleep_sec = sleep_sec_ch - '0';
		               		msg = msg.Right( msg.Length() - 2 );
						}
						else
						{
		               		msg = msg.Right( msg.Length() - 1 );
		               		ret += _T("\\p");
          					break;
						}

                     	UserSend(ret);	ret.Empty();

      					long et = wxGetLocalTime();
						while(true)
						{
          					if( wxGetLocalTime() - et >= sleep_sec )	break;
//							if( GetApp()->Pending() )	GetApp()->Dispatch();
//							GetApp()->Yield();
							AppProcessOtherEvents();
						}
					}
               		break;
			}
		}
	}

	UserSend(ret);
}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyEnter()
{
    char ch = (char)13;
    UserSend( &ch, 1);
}
// ----------------------------------------------------------------------------
#if 0
{
	    char xkey = 0;
	    switch (wParam) {
	      case VK_UP:
		xkey = 'A';
		break;
	      case VK_DOWN:
		xkey = 'B';
		break;
	      case VK_RIGHT:
		xkey = 'C';
		break;
	      case VK_LEFT:
		xkey = 'D';
		break;
	      case VK_CLEAR:
		xkey = 'G';
		break;
	    }
	    if (xkey) {
		if (term->vt52_mode)
		    p += sprintf((char *) p, "\x1B%c", xkey);
		else {
		    int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
#if 0
		    /*
		     * RDB: VT100 & VT102 manuals both state the
		     * app cursor keys only work if the app keypad
		     * is on.
		     * 
		     * SGT: That may well be true, but xterm
		     * disagrees and so does at least one
		     * application, so I've #if'ed this out and the
		     * behaviour is back to PuTTY's original: app
		     * cursor and app keypad are independently
		     * switchable modes. If anyone complains about
		     * _this_ I'll have to put in a configurable
		     * option.
		     */
		    if (!term->app_keypad_keys)
			app_flg = 0;
#endif
		    /* Useful mapping of Ctrl-arrows */
		    if (shift_state == 2)
			app_flg = !app_flg;

		    if (app_flg)
			p += sprintf((char *) p, "\x1BO%c", xkey);
		    else
			p += sprintf((char *) p, "\x1B[%c", xkey);
		}
		return p - output;
	    }
#endif	

void SCD_Telnet::keyUp()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;

	if (app_keypad_keys ) {
		UserSend("\x1B\x4F\x41");//\x1BOD
	}else{
		UserSend("\x1b\x5b\x41");	//\x1B[D
	}
}
//{	UserSend("\x1b\x5b\x41");	}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyDown()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;
	if (app_keypad_keys ) {
		UserSend("\x1B\x4F\x42");//\x1BOD
	}else{
		UserSend("\x1b\x5b\x42");	//\x1B[D
	}
}
//{	UserSend("\x1b\x5b\x42");	}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyLeft()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;
	if (app_keypad_keys ) {
		UserSend("\x1B\x4F\x44");//\x1BOD
	}else{
		UserSend("\x1b\x5b\x44");	//\x1B[D
	}
}

// ----------------------------------------------------------------------------
void SCD_Telnet::keyRight()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;
	if (app_keypad_keys ) {
		UserSend("\x1B\x4F\x43");//\x1BOD
	}else{
		UserSend("\x1b\x5b\x43");	//\x1B[D
	}
//		UserSend("\x1b\x5b\x43");	
}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyPageUp()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;
		UserSend("\x1b\x5b\x35\x7e");	
}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyPageDown()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;
		UserSend("\x1b\x5b\x36\x7e");	
}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyHome()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;
		UserSend("\x1b\x5b\x31\x7e");	
}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyEnd()
{	
	//if ( site_info.protocol == SOCK_EXECMD ) return;
		UserSend("\x1b\x5b\x34\x7e");	
}
// ----------------------------------------------------------------------------
void SCD_Telnet::keyControl(char ch)
{
	ch = tolower(ch);
	if( ch >= 'a' && ch <= 'z' )
	{
	    ch = ch - 0x60;
    	UserSend( &ch , 1 );
	}
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_Telnet::Paste(char *txt, bool withANSI)
{
	if (sock.GetType() == SOCK_EXECMD){
		sock.SendTextToConsole(txt);
		return;
	}
	//txt = wxStringToCharPtr( GetLineWrapedString(txt) );
	char *w = txt;

	if( withANSI )	//如果要複製彩色 ANSI code
	{
		while( *w != '\0' )
		{
			if( *w == '\x1b' )	*w = '\x15';
			w++;
		}
	}
	else
	{
		if (!app_keypad_keys) {
			char *r = txt;
			while( *r != '\0' ) {
				if ( *r == '\r' ) r++;
				if ( *r == '\n' ) r++;
				*w = *r;
				w++;	r++;
			}
			*w = '\0';
		}
		/*
		char *r = txt;
		while( *r != '\0' )
		{
			if( *r == '\x1b' && r[1] == '[' )	//控制碼開頭
			{
				r++;
				while(1)
				{
					r++;
					if( isalpha(*r) )	{	r++;	break;	}
					else if( *r == '\0' )	break;
				}
			}
			else
			{
				*w = *r;
				w++;	r++;
			}
		}
		*w = '\0';
		*/
	}

	UserSend(txt);
}
// ----------------------------------------------------------------------------
void SCD_Telnet::PasteFromClipboard(bool withANSI)	//從剪貼簿上讀取字串, 並且傳送給 server
{
	if( ! IsConnected() )	return;

	wxString text = GetTextFromClipboard();
#ifdef __WXDEBUG__
	wxLogMessage("PasteFromClipboard %s",text.c_str());
#endif
	if( text.IsEmpty() )	return;
	repaintFull();
	Paste( wxStringToCharPtr(text) , withANSI );
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_Telnet::StartUserQuery(char *prompt, bool _showchar)
{
//wxMessageBox("startuserquery " + wxString(prompt) );
	bluserquery = true;
	bluserquery_showchar = _showchar;
	userquery_len = 0;
	parse(prompt);
}
// ----------------------------------------------------------------------------
void SCD_Telnet::EndUserQuery()
{
	bluserquery = false;
	userquery_buf[ userquery_len ] = '\0';
	parse("\r\n", 2);
}
// ----------------------------------------------------------------------------
bool SCD_Telnet::IsUserQuerying()
{	return bluserquery;	}
// ----------------------------------------------------------------------------
void SCD_Telnet::UserQuery_OnChar(char ch)
{
	if( userquery_len < USERQUERY_BUF_LEN - 1 && isascii(ch) )
	{
		if(bluserquery_showchar)	parse( &ch , 1 );
		userquery_buf[ userquery_len ++ ] = ch;
	}
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_Telnet::OnChar(wxKeyEvent& event)
{
	char key = event.GetKeyCode();
	if(IsUserQuerying())
	{
		if( ! event.ShiftDown() ) key = tolower(key);
		UserQuery_OnChar(key);
		return;
	}

	UnIdleUser();

	if( event.AltDown() )	event.Skip();
	else
	{
		UserSend( &key, 1 );
	}
	//UserSend( &key, 1 );
}
// ----------------------------------------------------------------------------
void SCD_Telnet::OnKeyDown(wxKeyEvent& event)
{
	int key = event.GetKeyCode();
	//OnResize();
	if ( sock.GetType() != SOCK_EXECMD ) {
		if ( key != WXK_SHIFT && key != WXK_CONTROL && key != WXK_ALT ) {
			if ( sbar->GetThumbPosition() != (sbar->GetRange() -sbar->GetThumbSize())){
				repaintFull();
			}
		}
	}
	//repaint();
	if(key==WXK_F3 && ! event.ControlDown() ) debugging = ! debugging;	//除錯機制

	//if( event.ControlDown() && (key < 'A' || key > 'Z') )
	//{	/*event.Skip();*/	return;	}	//不能加上 event.Skip() 不然還是會被 OnChar 處理
	//else 
	if( event.AltDown() )
	{	
			//wxLogMessage("AltDown Skipped");
			event.Skip();	
			return;	
	}

	if(IsUserQuerying())
	{
		if( key == WXK_RETURN )
		{
			EndUserQuery();
#ifndef BBMAN_NO_SSH
			sock.Login( wxEmptyString , userquery_buf );
#endif
		}
		else if( key == WXK_BACK && userquery_len > 0 )
		{
			userquery_len --;
			if(bluserquery_showchar)	parse("\b \b", 3);
		}
		else if(isascii(key))	event.Skip();

		return;
	}

	if( ! IsConnected() ) return;	//如果沒有連線, 就不要傳資料
	UnIdleUser();

	char *output = NULL;
	char num_buf[] = "s";	//為 [鍵盤右邊的數字鍵] 保留緩衝區

	if( isascii(key) && ! iscntrl(key) )	//for 一般 ascii 文字
	{
#if defined(__WXGTK__)
/*
		if( event.ControlDown() )
		{
//wxMessageBox( wxString::Format("OnChar : %%%02x -> %c", (unsigned int)key, (char)key) );
//			if( ! isalpha(key) )	return;
//			key = tolower(key) - 0x60;
			keyControl(key);
			return;
		}
		if( ! event.ShiftDown() )	key = tolower(key);
		UserSend( (char*)&key, 1 );
		*/
		event.Skip();	//交給 OnChar event 處理
#else
		event.Skip();	//交給 OnChar event 處理
#endif
//wxMessageBox(wxString::Format("%d", key) );
	}
	else
	{
			/*
		if ( site_info.protocol == SOCK_EXECMD) {
			int so=event.GetKeyCode();
			UserSend( (char *)&so);
			return;
//			event.Skip();
		}else
		*/
		if( key >= 326 && key <= 335 )	//如果是鍵盤右邊的數字鍵
		{
			num_buf[0] = (char)(key - 326) + '0';
			output = num_buf;
		}
		else
		{
			if( ! event.ControlDown() && ! event.AltDown() )
			switch(key)
			{
				case 372 :	//for NumPad [Enter] in Linux/FreeBSD
				case WXK_RETURN :	keyEnter();	break;
			    case WXK_DELETE :
					if( isMultibyteWordDecetionEnabled() && isCurrentAWord() )	output = "\x1b[3~\x1b[3~";
					else	output = "\x1b[3~";
					break;
				//case WXK_DELETE:
				case WXK_BACK :	//back space
					if( isMultibyteWordDecetionEnabled() && isLeftAWord() )	output = "\x7f\x7f";
					else	output = "\x7f";
					break;
				case WXK_TAB :	output = "\t";	break;
				case WXK_HOME :
				case WXK_NUMPAD_HOME :	keyHome();	break;
				case WXK_END :
				case WXK_NUMPAD_END :	keyEnd();	break;
				case WXK_INSERT :	output = "\x1b\x40";	break;
				case WXK_NUMPAD_PRIOR :
				case WXK_NUMPAD_DECIMAL :	output = ".";	break;	//鍵盤右邊, 數字鍵附近的 [小數點] 鍵

				case WXK_UP :
				case WXK_NUMPAD_UP :		keyUp();		break;
				case WXK_DOWN :
				case WXK_NUMPAD_DOWN :		keyDown();		break;
				case WXK_LEFT :
				case WXK_NUMPAD_LEFT :
        			keyLeft();
					if( isMultibyteWordDecetionEnabled() && isLeftAWord() )	keyLeft();
					//event.Skip();
        			break;
				case WXK_RIGHT :
				case WXK_NUMPAD_RIGHT :
        			keyRight();
					if( isMultibyteWordDecetionEnabled() && isCurrentAWord() )	keyRight();
        			break;

				case 312 :	keyPageUp();	break;	//page up
				case 313 :	keyPageDown();	break;	//page down
				case WXK_ESCAPE :	
					output = "\x1b";	break;

				default :
//wxMessageBox(wxString::Format("%d", key) );
					if ( site_info.protocol == SOCK_EXECMD ) {
							wxString sendbuf ;
							if ( key >= WXK_F1 && key <=WXK_F24){
								sock.WriteSpecialKey(key);
								//char key1=key - WXK_F1;
								//sendbuf = wxString::Format("\x1bWXK_F%d",key1);
								//UserSend((char *)sendbuf.c_str(),sendbuf.Len());
							}else{
								event.Skip();
							}
							/*
							if ( key ==  WXK_F8 ) {
						   		sendbuf = "\x1bWXK_F8";
								UserSend((char *)sendbuf.c_str(),sendbuf.Len());
							}else if ( key == WXK_F7 ) {
						   		sendbuf = "\x1bWXK_F7";
								UserSend((char *)sendbuf.c_str(),sendbuf.Len());
							}else {
								UserSend((char*) &key,1);
								//event.Skip();
							}
							*/
					}else if( key < 0xff ){
							event.Skip();	//for 雙位元字
					}
					break;
			}
		}
	}

	if( output != NULL )	UserSend( output );
}
// ----------------------------------------------------------------------------

void SCD_Telnet::OnSocketEvent_Input()
{
		/*
		if ( sock.GetType() == SOCK_EXECMD ) {
			if ( updated ){
				int row_count,col_count;
				getColumnRowCount(&col_count,&row_count);
				repaintDispTerm(row_count,1);
				updated = 0;
			}
		}
		*/
		unsigned char buf[30000];
//		int newline_count = 0;
		int scroll_count = 0;


//     	while(1)
		for(int kk=0;kk<1;kk++)
		{
			int buf_len = SocketRead(buf, 10000);
			if(buf_len == 0)	break;
			ticks = 0;
			wxDC *dc = NULL;
			if( getVisible() ) {
			//	wxLogMessage("buf_len  %d",buf_len);
//				if ( buf_len > 7000) getParentWindow()->Freeze();
				dc = new wxClientDC( getParentWindow() );
				BeginDrawing(dc);
		   	}
if(debugging)
{
wxString tmp_str;
for(int i=0;i<buf_len;i++)
 tmp_str += wxString::Format("%02x(%c) ", buf[i] , isalnum(buf[i])?buf[i]:' ');
buf[buf_len] = '\0';
wxLogMessage(tmp_str.c_str());
wxLogMessage(wxString(buf).c_str());
}

			unsigned char *e, *end;
			e = buf;
			end = buf + buf_len;
			//wxLogMessage("%s",wxString(buf,buf_len));
			/*
			if ( 1 ) { 
				while( e < end )	//先檢查看看 buf 裡面有沒有 IAC (0xff)
				{
					if( *e == 0xff )	//IAC ( need to negotiation )
				   	{
							SocketUnread( e , end - e );
#ifdef __WXDEBUG__
							wxLogMessage("Get IAC need negotiation");
#endif
      						break;
						}
//						else if( *e == '\n' )	newline_count ++;
						e++;
				}
			//	if ( e > buf ) negoted = true;
			}else
			*/
			{
				e = buf+buf_len;
			}

//			if( newline_count > 3 )	EnableDrawing(false);

			int unread_data_len;
	
			unread_data_len = parse( (char*)buf , e - buf , &scroll_count );
			//repaint();
			if( unread_data_len > 0 )	//如果有資料尚未分析, 則 Unread
			{
#ifdef __WXDEBUG__
				wxLogMessage("unread_data_len > 0 %d",unread_data_len);
#endif
				if( e < end && *e == 0xff );	//這行必須要加，不然遇到某些資料會進入無限迴圈 ( 例如資料 "\x1b[qqq\xff" )
				else SocketUnread( e - unread_data_len , unread_data_len );
			}
/*
			if( scroll_count > 1 )
			{
				EnableDrawing(false);
			}
			*/
/*
wxString str = "";
for(unsigned char *r=buf;r<e;r++)	str += wxString::Format("%c", *r);
wxMessageBox(str);
*/

/* since PuTTY engine handle, never need negotion
			if( e < end && *e == 0xff )	{//如果 server 要求 negotiation
				negotiation();
				//negoted = true;
			}
*/
#if 0
			else
			{
				if( need_to_autologin	//自動登入
					&& sock.GetType() == SOCK_TELNET	//這是專為 telnet 設計的自動登入, ssh 不適用
					&& buf_len > 100 )	
				{
	    	  		need_to_autologin = false;
					if( ! username.IsEmpty() )
					{
						keyUp();	keyUp(); 	//兩次 [Up]
    	  				UserSend( (char*)( username.c_str()) ); keyEnter();
	      				UserSend( (char*)( password.c_str()) ); keyEnter();
					}
					if( ! message.IsEmpty() )	UserSend_spacial( message );

					OnLogined();
				}
//				break;
			}
#endif

			if( getVisible() ){
					EndDrawing(true);
					//if ( buf_len > 7000) getParentWindow()->Thaw();
			}
		}

/*
		if( scroll_count > 1 )	{	
			EnableDrawing(true);	repaint();	
		}
		*/
}

		/*
		if ( !SocketIsConnected() && sock.GetType() == SOCK_TELNET) {
			wxSocketClient *telsock=sock.getSock();
			int i= telsock->LastError(); 
			switch ( telsock->LastError() ){
					case wxSOCKET_NOERROR:
						  wxLogMessage("	No error happened. "); 
						  break;
					case wxSOCKET_INVOP:
						  wxLogMessage("  Invalid operation. ");
						  break;
					case wxSOCKET_NOHOST:
						wxLogMessage("  No corresponding host. ");
						break;
					case wxSOCKET_TIMEDOUT:
					  wxLogMessage("	The timeout for this operation expired. "); 
					  break;
					case wxSOCKET_IOERR:
					wxLogMessage("  Input/Output error. ");
					break;
					case wxSOCKET_INVADDR:
				  	wxLogMessage("Invalid address passed to wxSocket. "); 
							break;
					case wxSOCKET_INVSOCK  :
							wxLogMessage("Invalid socket (uninitialized). "); 
							break;
					default:	
			wxString str="Unable to open connection to "+ ip +"\r\n";
			//wxLogMessage("%s %d",str,i);
			parse((char *)str.c_str());
						break;
wxSOCKET_INVPORT  Invalid port.  
wxSOCKET_WOULDBLOCK  The socket is non-blocking and the operation would block.  
wxSOCKET_MEMERR  Memory exhausted.  
			
			}
		}
						*/

//wxMessageBox( SocketIsConnected() ? "connected!!!" : "lost" );
void SCD_Telnet::OnSocketEvent( wxSocketEvent &event )
{
	//return;
	if( event.GetSocketEvent() == wxSOCKET_INPUT )
	{
		OnSocketEvent_Input();
	} else if( event.GetSocketEvent() == wxSOCKET_CONNECTION
			|| event.GetSocketEvent() == wxSOCKET_LOST )
	{
		connect_state = ( SocketIsConnected() ? 2 : 0 );
		OnServerSupportNAWS();

		/*
		if( SocketIsConnected() && sock.GetType() == SOCK_SSH )	//如果是 ssh 連線
		{
			OnServerSupportNAWS();
			//parse("done\r\nLogging in ...\r\n");
		}
		if( SocketIsConnected() && sock.GetType() == SOCK_EXECMD )	//如果是 ssh 連線
		{
			OnServerSupportNAWS();
			//parse("done\r\nLogging in ...\r\n");
		}
		*/
	}

#if 0
	else if( event.GetSocketEvent() == (wxSocketNotify)WXSSH_AUTH_FAIL )
	{
//		wxMessageBox("auth fail");

		if( sock.GetType() == SOCK_SSH )	//如果是 ssh 連線
		{
			if( ssh_login_try_times == -1 )
			{
				ssh_login_try_times = 0;
				if( ! site_info.password.IsEmpty() )
					sock.Login(wxEmptyString, site_info.password );
				else
					StartUserQuery("Password : ", false);
			}
			else
			{
				ssh_login_try_times ++;
				parse("Login Failed\r\n");
				StartUserQuery("Password : ", false);
			}
		}
		return;
	}
	else if( event.GetSocketEvent() == (wxSocketNotify)WXSSH_AUTH_SUCCESS )
	{
//		wxMessageBox("auth success");
		parse("Login Success\r\n");
		return;
	}
#endif
	else
	{
		wxMessageBox(_T("unknown socket event"));
		return;
 	}


	//也讓 parent window 處理這個 socket event
//	event.m_clientData = this;	//讓 EvtHandler 可以透過 e.GetClientData() 取得 this
	//getParentWindow()->ProcessEvent(event);
}

void SCD_Telnet::close()
{
	if( IsDisconnected() ) return;
	user_closed = true;
	CloseSocket();
}

void SendResizeEvent(void *_w,int _c,int _r)
{
	wxSizeEvent sz(wxSize(_c,_r));
	wxWindow *parent = (wxWindow *)_w;//getParentWindow();
	parent->AddPendingEvent(sz);
}

bool SCD_Telnet::reconnect()
{
	if( ! IsDisconnected() ) { close(); }
	int col_count , row_count;
	getColumnRowCount( &col_count , &row_count );
	wxWindow *parent = getParentWindow();
	SendResizeEvent(parent,col_count,row_count);
	/*
	wxSizeEvent sz(wxSize(col_count,row_count));
	wxWindow *parent = getParentWindow();
	parent->AddPendingEvent(sz);
	*/
	return connect(site_info);
	//return	connect( sock.GetType(), getIP(), getPort(), getName() , username , password , message );
}

bool SCD_Telnet::connect(int _protocol, wxString _ip , int _port , wxString _name
	, wxString _user, wxString _passwd, wxString _message )
{
	SiteInfo _si;
	_si.ip = _ip;
	_si.port = _port;
	_si.name = _name;
	_si.username = _user;
	_si.password = _passwd;
	_si.message = _message;
	_si.protocol = _protocol;

	return connect(_si);
}

bool SCD_Telnet::connect(SiteInfo& _si)
{
	if( ! IsDisconnected() )	return false;
	UnIdleServer();
	UnIdleUser();

	//清除螢幕
	wxWindow *parent = getParentWindow();
	cleanScreen();
	if( parent && getVisible() )	repaint();
	setColumnRow( 80 , 24 );

	//設定自動登入
	site_info = _si;
 	username = _si.username;
 	password = _si.password;
	message = _si.message;
	need_to_autologin = true;
	blSupportNAWS = false;
	user_closed = false;

	//開始連線
	ip = _si.ip;
	port = _si.port;
	name = _si.name;

//	_si.protocol = SOCK_SSH;

#ifndef BBMAN_NO_SSH
	if( _si.protocol == SOCK_SSH ) {
		sock.SetType( SOCK_SSH );
		connect_state = 1;
		SocketConnect();
		//parse("Connecting ... ");
	} else if ( _si.protocol == SOCK_EXECMD ) {
		sock.SetType(SOCK_EXECMD);
		connect_state = 1;
		sock.SetCMDParent(this);
		SocketConnect();
		OnServerSupportNAWS();
	}else if ( _si.protocol == SOCK_TELNET ) {
		sock.SetType( SOCK_TELNET );
		//sock.SetType( SOCK_SSH );
		connect_state = 1;
		SocketConnect();
		
		/*
		wxIPV4address addr;
		addr.Hostname(ip);
		addr.Service(port);
		connect_state = 1;
		SocketConnect(addr, false, _si.username);
		*/
	}
#else
	if( _si.protocol == SOCK_SSH )
	{
		parse("\x1b[1;33m");
		parse( wxStringToCharPtr(gettext("Sorry, this BBMan is a no-SSH version.\nIf you want to connect SSH server, Please download the full version of BBMan.")) );
		parse("\x1b[0m");
//		CloseSocket();
		return false;
	}
	else{
		//parse("Connecting ... ");
	}
#endif


/*
	if( ! SocketConnect(addr, true) )
		wxMessageBox("無法連線");
	else
		start_time = wxGetLocalTime();
*/

#if 0//ndef BBMAN_NO_SSH
	//如果是 ssh 連線必須先詢問登入帳號
	if( sock.GetType() == SOCK_SSH && _si.username.IsEmpty() )
	{
		wxString caption = gettext("Please Enter your account.");
		caption += _T("\n\nip : ") + ip;
		if( ! name.IsEmpty() )	caption += _T("\nname : ") + name;
		_si.username = wxGetTextFromUser( caption );
	}
#endif


	start_time = wxGetLocalTime();


	return true;
}


// **************************************************** //
// **************************************************** //
// *************** 實做 telnet protocol *************** //
// **************************************************** //
// **************************************************** //



bool SCD_Telnet::negotiation()
{

//定義在 telnet protocol 中會用到的指令

#define IAC 	255     // interpret as command:
#define DONT    254     // you are not to use option
#define DO  	253     // please, you use option
#define WONT    252     // I won't use option
#define WILL    251     // I will use option
#define SB  	250     // interpret as subnegotiation
#define SE  	240

#define GA      249		       /* you may reverse the line */
#define EL      248		       /* erase the current line */
#define EC      247		       /* erase the current character */
#define	AYT	246		       /* are you there */
#define	AO	245		       /* abort output--but let prog finish */
#define	IP	244		       /* interrupt process--permanently */
#define	BREAK	243		       /* break */
#define DM      242		       /* data mark--for connect. cleaning */
#define NOP     241		       /* nop */
#define EOR     239		       /* end of record (transparent mode) */
#define ABORT   238		       /* Abort process */
#define SUSP    237		       /* Suspend process */
#define xEOF    236		       /* End of file: EOF is already used... */

/*
#define TELOPT_ECHO 1   // echo 
#define TELOPT_SGA  3   // suppress go ahead 

#define TELOPT_TTYPE    24  // terminal type
#define TELQUAL_IS  	0   // option is...
#define TELQUAL_SEND    1   // send option
#define TELOPT_SGA  	3   // suppress go ahead
#define TELOPT_NAWS 	31	// window size
*/

#define	TELQUAL_IS	0	       /* option is... */
#define	TELQUAL_SEND	1	       /* send option */
#define	TELQUAL_INFO	2	       /* ENVIRON: informational version of IS */

#define TELOPT_BINARY	0	       /* 8-bit data path */
#define TELOPT_ECHO	1	       /* echo */
#define	TELOPT_RCP	2	       /* prepare to reconnect */
#define	TELOPT_SGA	3	       /* suppress go ahead */
#define	TELOPT_NAMS	4	       /* approximate message size */
#define	TELOPT_STATUS	5	       /* give status */
#define	TELOPT_TM	6	       /* timing mark */
#define	TELOPT_RCTE	7	       /* remote controlled transmission and echo */
#define TELOPT_NAOL 	8	       /* negotiate about output line width */
#define TELOPT_NAOP 	9	       /* negotiate about output page size */
#define TELOPT_NAOCRD	10	       /* negotiate about CR disposition */
#define TELOPT_NAOHTS	11	       /* negotiate about horizontal tabstops */
#define TELOPT_NAOHTD	12	       /* negotiate about horizontal tab disposition */
#define TELOPT_NAOFFD	13	       /* negotiate about formfeed disposition */
#define TELOPT_NAOVTS	14	       /* negotiate about vertical tab stops */
#define TELOPT_NAOVTD	15	       /* negotiate about vertical tab disposition */
#define TELOPT_NAOLFD	16	       /* negotiate about output LF disposition */
#define TELOPT_XASCII	17	       /* extended ascic character set */
#define	TELOPT_LOGOUT	18	       /* force logout */
#define	TELOPT_BM	19	       /* byte macro */
#define	TELOPT_DET	20	       /* data entry terminal */
#define	TELOPT_SUPDUP	21	       /* supdup protocol */
#define	TELOPT_SUPDUPOUTPUT 22	       /* supdup output */
#define	TELOPT_SNDLOC	23	       /* send location */
#define	TELOPT_TTYPE	24	       /* terminal type */
#define	TELOPT_EOR	25	       /* end or record */
#define	TELOPT_TUID	26	       /* TACACS user identification */
#define	TELOPT_OUTMRK	27	       /* output marking */
#define	TELOPT_TTYLOC	28	       /* terminal location number */
#define	TELOPT_3270REGIME 29	       /* 3270 regime */
#define	TELOPT_X3PAD	30	       /* X.3 PAD */
#define	TELOPT_NAWS	31	       /* window size */
#define	TELOPT_TSPEED	32	       /* terminal speed */
#define	TELOPT_LFLOW	33	       /* remote flow control */
#define TELOPT_LINEMODE	34	       /* Linemode option */
#define TELOPT_XDISPLOC	35	       /* X Display Location */
#define TELOPT_OLD_ENVIRON 36	       /* Old - Environment variables */
#define	TELOPT_AUTHENTICATION 37       /* Authenticate */
#define	TELOPT_ENCRYPT	38	       /* Encryption option */
#define TELOPT_NEW_ENVIRON 39	       /* New - Environment variables */
#define TELOPT_TN3270E	40	       /* TN3270 enhancements */
#define TELOPT_XAUTH	41
#define TELOPT_CHARSET	42	       /* Character set */
#define TELOPT_RSP	43	       /* Remote serial port */
#define TELOPT_COM_PORT_OPTION 44      /* Com port control */
#define TELOPT_SLE	45	       /* Suppress local echo */
#define TELOPT_STARTTLS	46	       /* Start TLS */
#define TELOPT_KERMIT	47	       /* Automatic Kermit file transfer */
#define TELOPT_SEND_URL	48
#define TELOPT_FORWARD_X 49
#define TELOPT_PRAGMA_LOGON	138
#define TELOPT_SSPI_LOGON	139
#define TELOPT_PRAGMA_HEARTBEAT	140
#define	TELOPT_EXOPL	255	       /* extended-options-list */

#define ANY 128


#define BSD_VAR 1
#define BSD_VALUE 0
#define RFC_VAR 0
#define RFC_VALUE 1

#define CR 13
#define LF 10
#define NUL 0

enum telnet_state
{	TSDATA, TSIAC, TSDO, TSDONT, TSWILL, TSWONT, TSSB_START, TSSBIAC, TSSB_END, SB_TERMTYPE	, SB_NEW_ENV};

enum telnet_func
{	FUNC_NOOP, FUNC_PUTC, FUNC_DO, FUNC_WILL, FUNC_WONT, FUNC_DONT, FUNC_END, FUNC_SB_TERMTYPE , FUNC_WILL_NAWS , FUNC_SB_NEW_ENV};

//定義 state table
struct FSM_table
{
	telnet_state	now_state;		//目前狀態
	unsigned char	input;			//目前的 input 字元
	telnet_state	next_state;		//下一個狀態
	telnet_func		func;			//要執行的動作
};

static struct FSM_table neg_FSM[] =
{
	//	目前狀態	input	下一個狀態	要執行的動作

	{	TSDATA,		IAC,	TSIAC,		FUNC_NOOP	},	//start negotiation
	{	TSDATA,		ANY,	TSDATA,		FUNC_END	},	//stop  negotiation

	//DO, DONT, WILL, WONT, SB
	{	TSIAC,		DO,		TSDO,		FUNC_NOOP	},
	{	TSIAC,		DONT,	TSDONT,		FUNC_NOOP	},
	{	TSIAC,		WILL,	TSWILL,		FUNC_NOOP	},
	{	TSIAC,		WONT,	TSWONT,		FUNC_NOOP	},
	{	TSIAC,		SB,		TSSB_START,	FUNC_NOOP	},
	{	TSIAC,		IAC,	TSDATA,		FUNC_PUTC	},
	{	TSIAC,		ANY,	TSDATA,		FUNC_NOOP	},

	{	TSDO,		TELOPT_TTYPE,	TSDATA,		FUNC_WILL		},	//IAC WILL TERMTYPE
	{	TSDO,		TELOPT_NAWS,	TSDATA,		FUNC_WILL_NAWS		},	//開啟長螢幕機制
	{	TSDO,		TELOPT_NEW_ENVIRON,	TSDATA,		FUNC_WILL		},
	{	TSDO,		ANY,	TSDATA,		FUNC_WONT		},
	{	TSDONT,		ANY,	TSDATA,		FUNC_NOOP		},

	{	TSWILL,  	TELOPT_SGA,		TSDATA,		FUNC_DO	},	//suppress go ahead
	{	TSWILL,  	TELOPT_ECHO,	TSDATA,		FUNC_DO	},	//echo
	{	TSWILL,  	ANY,	TSDATA,		FUNC_DONT	},
	{	TSWONT,		ANY,	TSDATA,		FUNC_NOOP	},

	//註冊 sub negotiation
	{	TSSB_START,	TELOPT_TTYPE,	SB_TERMTYPE,	FUNC_NOOP	},
	{	TSSB_START,	TELOPT_NEW_ENVIRON,	SB_NEW_ENV,	FUNC_NOOP	},

	//sub negotiation common input
	{	TSSB_START,	IAC,	TSSBIAC,	FUNC_NOOP	},
	{	TSSB_START,	ANY,	TSSB_END,	FUNC_NOOP	},

	{	TSSB_END,	IAC,	TSSBIAC,	FUNC_NOOP	},
	{	TSSB_END,	ANY,	TSSB_END,	FUNC_NOOP	},

	{	TSSBIAC,	SE,		TSDATA,		FUNC_NOOP	},
	{	TSSBIAC,	ANY,	TSSB_END,	FUNC_NOOP	},

	//TERM_TYPE sub neg
	{	SB_TERMTYPE,	TELQUAL_SEND,	TSSB_END,	FUNC_SB_TERMTYPE	},
	{	SB_TERMTYPE,	ANY,			TSSB_END,	FUNC_NOOP	},
	{	SB_NEW_ENV,		TELQUAL_SEND,	TSSB_END,	FUNC_SB_NEW_ENV	},
	{	SB_NEW_ENV,		ANY,			TSSB_END,	FUNC_NOOP	},

};



	//*** negotiation with server (telnet protocol) ***//




#ifdef DEBUG_NEGOTIATION

#include <stdio.h>
FILE *fp = NULL;

//fp = fopen("log.txt", "w+");
//fclose(fp);

fp = fopen("log.txt", "a+");
//fprintf(fp, buf);
fclose(fp);

char map[256][30];
for(int i=0;i<256;i++)
	sprintf(map[i], "%02x", (unsigned char)i);
sprintf( map[IAC], "IAC" );
sprintf( map[DONT], "DONT" );
sprintf( map[DO], "DO  " );
sprintf( map[WONT], "WONT" );
sprintf( map[WILL], "WILL" );
sprintf( map[SB], "SB  " );
sprintf( map[SE], "SE  " );
sprintf( map[TELOPT_TTYPE], "TELOPT_TTYPE" );
sprintf( map[TELQUAL_IS], "TELQUAL_IS" );
sprintf( map[TELQUAL_SEND], "TELQUAL_SEND" );
sprintf( map[TELOPT_SGA], "TELOPT_SGA" );
sprintf( map[TELOPT_NAWS], "TELOPT_NAWS" );
sprintf( map[TELOPT_NEW_ENVIRON], "TELOPT_NEW_ENVIRON" );

char *state_name[] = {	"TSDATA", "TSIAC", "TSDO", "TSWILL", "TSSB_START", "TSSBIAC", "TSSB_END", "SB_TERMTYPE" ,"SB_NEW_ENV"	};
char *func_name[] = {	"FUNC_NOOP", "FUNC_PUTC", "FUNC_DO", "FUNC_WILL", "FUNC_WONT", "FUNC_DONT", "FUNC_END", "FUNC_SB_TERMTYPE","FUNC_WILL_NAWS"	,"FUNC_SB_NEW_ENV"};

wxLongLong  time_now = wxGetLocalTimeMillis(), time_diff;
fp = fopen("log.txt", "a+");
fclose(fp);

#endif

	//start negotiation

	enum telnet_state	now_state, next_state;
	enum telnet_func	func;
	unsigned char ch;

	now_state = TSDATA;


	while(1)
	{
		if( SocketRead( &ch, 1 ) != 1 )	//有可能是因為 non-blocking 而且目前沒有資料可讀取而造成的 error
    		return false;

		//從 neg_FSM 中尋找對應的資料
		for(int i=0;;i++)
		{
			if( neg_FSM[i].now_state == now_state )
				if( neg_FSM[i].input == ANY
    				|| neg_FSM[i].input == ch )
				{
     				next_state = neg_FSM[i].next_state;
     				func = neg_FSM[i].func;
					break;
				}
		}

#ifdef DEBUG_NEGOTIATION
fp = fopen("log.txt", "a+");

time_diff = wxGetLocalTimeMillis() - time_now;
fprintf(fp, "time=%-6s , ", time_diff.ToString().c_str() );
time_now += time_diff;

fprintf(fp, "now=%-13s , in=%-13s , next=%-13s , func=%-13s\n", state_name[now_state], map[ch], state_name[next_state], func_name[func] );
fclose(fp);
#endif
		now_state = next_state;

		//執行指定的動作

		if( func == FUNC_NOOP );
		else if( func == FUNC_WILL )
		{
			unsigned char tmp[3];
			tmp[0] = IAC;
			tmp[1] = WILL;
			tmp[2] = ch;
			SocketWrite(tmp, 3);
#ifdef DEBUG_NEGOTIATION
fp = fopen("log.txt", "a+");
fprintf(fp, "\nclient : ");
fprintf(fp, "%s ", map[ (unsigned char)tmp[0]]);
fprintf(fp, "%s ", map[ (unsigned char)tmp[1]]);
fprintf(fp, "%s\n\n", map[ (unsigned char)tmp[2]]);
fclose(fp);
#endif
  		}
		else if( func == FUNC_DO )
		{
			unsigned char tmp[3];
			tmp[0] = IAC;
			tmp[1] = DO;
			tmp[2] = ch;
			SocketWrite(tmp, 3);
  		}
		else if( func == FUNC_WONT )
		{
			unsigned char tmp[3];
			tmp[0] = IAC;
			tmp[1] = WONT;
			tmp[2] = ch;
			SocketWrite(tmp, 3);
#ifdef DEBUG_NEGOTIATION
fp = fopen("log.txt", "a+");
fprintf(fp, "\nclient : ");
fprintf(fp, "%s ", map[ (unsigned char)tmp[0]]);
fprintf(fp, "%s ", map[ (unsigned char)tmp[1]]);
fprintf(fp, "%s\n\n", map[ (unsigned char)tmp[2]]);
fclose(fp);
#endif
		}
		else if( func == FUNC_DONT )
		{
			unsigned char tmp[3];
			tmp[0] = IAC;
			tmp[1] = DONT;
			tmp[2] = ch;
			SocketWrite(tmp, 3);
#ifdef DEBUG_NEGOTIATION
fp = fopen("log.txt", "a+");
fprintf(fp, "\nclient : ");
fprintf(fp, "%s ", map[ (unsigned char)tmp[0]]);
fprintf(fp, "%s ", map[ (unsigned char)tmp[1]]);
fprintf(fp, "%s\n\n", map[ (unsigned char)tmp[2]]);
fclose(fp);
#endif
		}
		else if( func == FUNC_SB_TERMTYPE )
		{
			unsigned char tmp[15];
			int i = 0;
			tmp[i++] = IAC;
			tmp[i++] = SB;
			tmp[i++] = TELOPT_TTYPE;
			tmp[i++] = TELQUAL_IS;
			if ( 0 ) {
			tmp[i++] = 'v';
			tmp[i++] = 't';
			tmp[i++] = '1';
			tmp[i++] = '0';
			tmp[i++] = '0';
			}else{
			//yjxu add
			
			tmp[i++] = 'x';
			tmp[i++] = 't';
			tmp[i++] = 'e';
			tmp[i++] = 'r';
			tmp[i++] = 'm';
			}
			
			tmp[i++] = IAC;
			tmp[i++] = SE;
			SocketWrite(tmp, i);
#ifdef DEBUG_NEGOTIATION
fp = fopen("log.txt", "a+");
fprintf(fp, "\nclient : ");
int t=0;
while(t<i)
	fprintf(fp, "%s ", map[ (unsigned char)tmp[t++]]);
fputs("\n", fp);
fclose(fp);
#endif
		}
		else if( func == FUNC_WILL_NAWS )
		{
			unsigned char tmp[3];
			tmp[0] = IAC;
			tmp[1] = WILL;
			tmp[2] = TELOPT_NAWS;
			SocketWrite(tmp, 3);
			OnServerSupportNAWS();
		}else if ( func == FUNC_SB_NEW_ENV )
		{
			wxLogMessage("set DISPLAY");
			unsigned char tmp[256];
			int i = 0;
			tmp[i++] = IAC;
			tmp[i++] = SB;
			tmp[i++] = TELOPT_NEW_ENVIRON;
			tmp[i++] = TELQUAL_IS;
			tmp[i++] = 0;//RFC_VAR
			tmp[i++] = 'D';
			tmp[i++] = 'I';
			tmp[i++] = 'S';
			tmp[i++] = 'P';
			tmp[i++] = 'L';
			tmp[i++] = 'A';
			tmp[i++] = 'Y';
			tmp[i++] = 1;//RFC_VALUE
#ifdef __WXGTK__
			char *dis_p=getenv("DISPLAY");
			sprintf((char *)&tmp[i],"%s",dis_p);
			i+=strlen(dis_p);
#else
			sprintf((char *)&tmp[i],":0.0");
			i+=4;
			/*
			tmp[i++] = ':';	
			tmp[i++] = '0';	
			tmp[i++] = '.';	
			tmp[i++] = '0';	
			*/
#endif

			tmp[i++] = IAC;
			tmp[i++] = SE;
			SocketWrite(tmp, i);
		} else if( func == FUNC_END )
  		{
  			SocketUnread( &ch , 1 );
    		break;
		}

	}
#ifdef DEBUG_NEGOTIATION
fp = fopen("log.txt", "a+");
fprintf(fp, "\ntelnet negotiation end\n");
fclose(fp);
#endif
	// ************************************************* //


#ifdef DEBUG_NEGOTIATION
/*while(1)
{
SocketRead( &ch , 1 );
fp = fopen("log.txt", "a+");
fprintf(fp, "%s ", map[ch]);
//fprintf(fp, "%c", ch);
fclose(fp);
}*/
#endif


	return true;
}
void SCD_Telnet::Scrolled(wxScrollEvent &event)
{
	if ( event.GetPosition() != _sbar_pos ){
		//wxLogMessage("scrollbar %d",event.GetPosition());
		int old_pos =_sbar_pos; 
		int c_pos = event.GetPosition();
		if ( sock.GetType() == SOCK_EXECMD ){
			sock.SetScrollBarPos(c_pos-_sbar_pos);
		}else{
			_sbar_pos = event.GetPosition();
			int col_count,row_count;
			getColumnRowCount(&col_count,&row_count);
			//if ( _sbar_pos != sbar->GetRange()-sbar->GetThumbSize()){
			assignDispTerm(col_count,row_count);
			repaintDispTerm(abs(old_pos-_sbar_pos),old_pos>_sbar_pos);
			//}
		}
	}
	
//	ScrolledInt(event);
}

void SCD_Telnet::ScrollLines(int orient,int count)
{
	int col_count,row_count;
	getColumnRowCount(&col_count,&row_count);
	int c_pos = orient?_sbar_pos-count:_sbar_pos+count;
	if ( c_pos > _sbar_bottom_pos ){
		c_pos = _sbar_bottom_pos;
		count = _sbar_bottom_pos - _sbar_pos;
	}else if ( c_pos < 0 ) {
		c_pos = 0;
		count = _sbar_pos - 0;
	}
	if ( sock.GetType() == SOCK_EXECMD ){
		sock.SetScrollBarPos(c_pos-_sbar_pos);
	}else{
		sbar->SetThumbPosition(c_pos);	
		_sbar_pos = c_pos;
		assignDispTerm(col_count,row_count);
		repaintDispTerm(count,orient);
	}
}

void SCD_Telnet::ScrollHalfPage(int orient)
{
	int col_count,row_count;
	getColumnRowCount(&col_count,&row_count);
	int count=row_count/2;
	ScrollLines(orient,count);
//	int _old_pos = _sbar_pos;
/*
	int c_pos = orient?_sbar_pos-count:_sbar_pos+count;
	if ( c_pos > _sbar_bottom_pos ){
		c_pos = _sbar_bottom_pos;
		count = _sbar_bottom_pos - _sbar_pos;
	}else if ( c_pos < 0 ) {
		c_pos = 0;
		count = _sbar_pos - 0;
	}
	if ( sock.GetType() == SOCK_EXECMD ){
		sock.SetScrollBarPos(c_pos-_sbar_pos);
	}else{
		sbar->SetThumbPosition(c_pos);	
		_sbar_pos = c_pos;
		assignDispTerm(col_count,row_count);
		repaintDispTerm(count,orient);
	}
	*/
	
//	ScrollHalfPageInt(orient);
}
// ============================================================================

/*
FOREGROUND_BLUE 0x0001 Text color contains blue. 
FOREGROUND_GREEN 0x0002 Text color contains green. 
FOREGROUND_RED 0x0004 Text color contains red. 
FOREGROUND_INTENSITY 0x0008 Text color is intensified. 

BACKGROUND_BLUE 0x0010 Background color contains blue. 
BACKGROUND_GREEN 0x0020 Background color contains green. 
BACKGROUND_RED 0x0040 Background color contains red. 
BACKGROUND_INTENSITY 0x0080 Background color is intensified. 

COMMON_LVB_LEADING_BYTE 0x0100 Leading byte. 
COMMON_LVB_TRAILING_BYTE 0x0200 Trailing byte. 

COMMON_LVB_GRID_HORIZONTAL 0x0400 Top horizontal 
COMMON_LVB_GRID_LVERTICAL 0x0800 Left vertical. 
COMMON_LVB_GRID_RVERTICAL 0x1000 Right vertical. 
COMMON_LVB_REVERSE_VIDEO 0x4000 Reverse foreground and background attribute. 

COMMON_LVB_UNDERSCORE 0x8000 Underscore. 
*/
#define TEXT_COLOR_MASK		0x000F
#define BACKGROUND_COLOR_MASK	0x00F0

void enjCMDUpdateTerm(void *frontend,PCHAR_INFO m_consoleBuffer,PCONSOLE_SCREEN_BUFFER_INFO m_consoleInfo)
{
	SCD_Telnet *telnet=(SCD_Telnet*)frontend;
	int col_count, row_count;	//terminal 行列數
	telnet->getColumnRowCount(&col_count,&row_count);
	if ( row_count < 5 ) return;
	
	COORD dwSize	= m_consoleInfo->dwSize;
	//link_list_t *save_head = telnet->cur_head;
	DWORD dwColumns	= m_consoleInfo->srWindow.Right - m_consoleInfo->srWindow.Left + 1;
	DWORD dwRows	= m_consoleInfo->srWindow.Bottom - m_consoleInfo->srWindow.Top + 1;
	DWORD	top_y = m_consoleInfo->srWindow.Top;
	DWORD	bottom_y = m_consoleInfo->srWindow.Bottom;
	
	//if ( pos == -1 ) pos =0;
	//if ( bottom_y > telnet->_cur_lines || ( top_y == 0 && m_consoleInfo->dwCursorPosition.Y < row_count) || telnet->sbar->GetRange() != m_consoleInfo->dwCursorPosition.Y -row_count ) {
	DWORD temp_y = m_consoleInfo->dwCursorPosition.Y;
	
	//if ( pos < 0 ) return;
	//temp_y = top_y + row_count;
	temp_y = (bottom_y > temp_y?bottom_y:temp_y)+ 1;
	/*
	if ( temp_y < row_count ) {
			temp_y = top_y + row_count ;
	} else {
	}
	*/
	int pos = temp_y - row_count;
	int old_sbar_pos = telnet->_sbar_pos;
	if ( dwRows > row_count || dwColumns > col_count ){
#ifdef __WXDEBUG__
		wxLogMessage("dismatch row %d col %d",row_count,col_count);
#endif
		extern void AdjustConsoleSize(void *handle,int _c,int _r);
		AdjustConsoleSize(telnet->GetSocket()->GetEXEC()->m_handler,col_count,row_count);
		return;
	}


	if ( telnet->sbar->GetRange() != temp_y ) {
	
		//telnet->_cur_lines = dwSize.Y;
		telnet->_cur_lines = temp_y ; 
		int range=telnet->_cur_lines>telnet->_max_line?
				telnet->_max_line:telnet->_cur_lines;
		telnet->sbar->SetScrollbar(pos,row_count,range,row_count);
		telnet->_sbar_bottom_pos = temp_y - row_count;
		//adjust cur_head position;
		telnet->cur_head = telnet->head;
		for ( int i=0;i<telnet->_sbar_bottom_pos;i++){
			telnet->cur_head = telnet->cur_head->_n;
		}
		telnet->assignDataTerm(col_count,row_count);
		telnet->_sbar_pos=pos;
		telnet->assignDispTerm(col_count,row_count);
		//telnet->disp_term = telnet->term_data;
		//telnet->_sbar_pos =0;
	}
//		int pos = m_consoleInfo->srWindow.Bottom - row_count+1;
		//telnet->_sbar_pos=pos;
	pos = m_consoleInfo->srWindow.Bottom - row_count +1;
	int changed = 0;
	if ( telnet->_sbar_pos != pos ) {
		//if ( pos == -1 ) pos =0;
		changed = 1;
#ifdef __WXDEBUG__
		wxLogMessage("pos changed %d %d",telnet->_sbar_pos,pos);
#endif
		telnet->sbar->SetThumbPosition(pos);
		telnet->_sbar_pos=pos;	
		telnet->assignDispTerm(col_count,row_count);
	}
//return;
	/*
	//hack, since error disp_term point
	if ( telnet->_sbar_pos == telnet->_sbar_bottom_pos
		&& telnet->disp_term[0][0].ch != telnet->term_data[0][0].ch ){
		telnet->assignDispTerm(col_count,row_count);
	}
	*/
	//int distance = m_consoleInfo->srWindow.Top - telnet->pre_top;
	wxString buffer ;//="\x1b[1;1H";
	//int isjump = 0;
	/*
	int changed = 0;
	
	telnet->repaintDispTerm(abs(old_sbar_pos-telnet->_sbar_pos),old_sbar_pos>telnet->_sbar_pos);
	wxDC *dc = NULL;
	if( telnet->getVisible() && telnet->getDC() == NULL )
	{
		dc = new wxClientDC( telnet->getParentWindow() );
		telnet->BeginDrawing(dc);
	}
*/
	DWORD i, j;
	for (i=0;i<dwRows;i++){
	//for (i=0;i<row_count;i++){
		for ( j=0;j<dwColumns;j++){
			WORD attr =  m_consoleBuffer[i*dwColumns+j].Attributes;
			CHAR achar = m_consoleBuffer[i*dwColumns+j].Char.AsciiChar;
			int fg_color = 30;
			int bg_color = 40;

//			int bgc = (attr * BACKGROUND_COLOR_MASK );
			
			/*
FOREGROUND_BLUE 0x0001 Text color contains blue. 
FOREGROUND_GREEN 0x0002 Text color contains green. 
FOREGROUND_RED 0x0004 Text color contains red. 
FOREGROUND_INTENSITY 0x0008 Text color is intensified. 
*/
//0		1	2	  3      4    5
//black red green yellow blue purple dian white
			//WORD tmp = attr & TEXT_COLOR_MASK;
			if ( attr & FOREGROUND_BLUE ){
				fg_color += 4 ;
			}
			if ( attr & FOREGROUND_GREEN) {
				fg_color += 2;
			}
			if ( attr & FOREGROUND_RED) {
				fg_color += 1;
			}
					/*
BACKGROUND_BLUE 0x0010 Background color contains blue. 
BACKGROUND_GREEN 0x0020 Background color contains green. 
BACKGROUND_RED 0x0040 Background color contains red. 
BACKGROUND_INTENSITY 0x0080 Background color is intensified. 
*/
			if ( attr & BACKGROUND_BLUE){
				bg_color += 4;
			}
			if ( attr & BACKGROUND_GREEN){
				bg_color += 2;
			}
			if ( attr & BACKGROUND_RED) {
				bg_color += 1;
			}

			int lead_byte = 0;
			if ( attr & COMMON_LVB_LEADING_BYTE ){
				lead_byte = 1;
			}
			int trail_byte = 0;
			if ( attr & COMMON_LVB_TRAILING_BYTE){
				trail_byte = 1;
			}
			TerminalChar &now_ch = telnet->disp_term[i][j];
			//char cch = telnet->d
			if ( now_ch.ch == achar
				&& now_ch.getTextColorCode() == fg_color
				&& now_ch.getBgColorCode() == bg_color
				){
					//isjump = 0;
				   	continue;
			}
			now_ch.ch= achar; 
			now_ch.setTextColor(fg_color);
			now_ch.setBgColor(bg_color);
			if ( lead_byte ) {
				now_ch.setCharType(TerminalChar::CH_WORDFIRST);
			}else if ( trail_byte ) {
				now_ch.setCharType(TerminalChar::CH_WORDLAST);
			}else{
				now_ch.setCharType(TerminalChar::CH_CHAR);
			}
			
			//now_ch.setHighlight(true);
			//telnet->repaintChar(i,j);
			changed = 1;
			/*
			if (!isjump) {
				char tempbuf[1024];
				sprintf(tempbuf,"\x1b[%d;%dH",i+1,j+1);
				buffer+=tempbuf;
				isjump = 1;
			}
			buffer += m_consoleBuffer[i*dwColumns+j].Char.AsciiChar;
			*/
		}
	}
	/*
	if ( row_count > i ){
		for (i;i<row_count;i++){
			for ( j=0;j<dwColumns;j++){
				TerminalChar &now_ch = telnet->disp_term[i][j];
				now_ch.ch= '\0'; 
				now_ch.setTextColor(37);
				now_ch.setBgColor(40);
				now_ch.setCharType(TerminalChar::CH_CHAR);
			}
		}
	}
	*/
#ifdef __WXDEBUG__
	if ( changed ) {
		wxLogMessage("content changed");
	}
#endif
/*
	if( telnet->getVisible() && dc != NULL )
 	{
		//repaint();
		//parentWindow->GetCaret()->Show();
		//updateCaret();
  		telnet->EndDrawing(true);
		//delete dc;
	}
	*/
//return;
	int cursor_y = m_consoleInfo->dwCursorPosition.X - 
			m_consoleInfo->srWindow.Left;
	int cursor_x = m_consoleInfo->dwCursorPosition.Y - 
			m_consoleInfo->srWindow.Top;
	if ( cursor_x < row_count && cursor_y < col_count ){
		int last_x,last_y;
		telnet->getXY(&last_x,&last_y);
		if ( last_y != cursor_x || last_x != cursor_y ) {
#ifdef __WXDEBUG__
			wxLogMessage("cursor changed %d %d %d %d %d %d", m_consoleInfo->dwCursorPosition.X,
							m_consoleInfo->dwCursorPosition.Y,last_y,last_x,
							cursor_x,cursor_y);
#endif
			changed =1;
		}
	}
	if (changed){
		//wxPaintEvent et;
		//TerminalChar  **saved_term_data=telnet->term_data;
		//telnet->term_data=telnet->disp_term;
		telnet->gotoXY(cursor_y,cursor_x);
		//telnet->updated = 1;
		telnet->repaintDispTerm(row_count,1);
//wxMessageDialog(NULL,wxString::Format("chnged row_count %d",row_count)).ShowModal();
		//wxLogMessage("changed row_count %d",row_count);
#ifdef __WXDEBUG__
		wxLogMessage("changed");
#endif
		//telnet->getParentWindow()->Refresh(false);
		//char tempbuf[1024];
		//sprintf(tempbuf,"\x1b[%d;%dH",cursor_x+1,cursor_y+1);
		//buffer=tempbuf;
		//int scroll_count = 0;
		//telnet->parse((char*)(buffer.c_str()),buffer.length(),&scroll_count);
		//telnet->term_data = saved_term_data;
	}
	/*
	if ( buffer.length()>0)
	{
		TerminalChar  **saved_term_data=telnet->term_data;
		telnet->term_data=telnet->disp_term;
		telnet->repaintDispTerm(abs(old_sbar_pos-telnet->_sbar_pos),old_sbar_pos>telnet->_sbar_pos);
		int cursor_y = m_consoleInfo->dwCursorPosition.X - 
				m_consoleInfo->srWindow.Left;
		int cursor_x = m_consoleInfo->dwCursorPosition.Y - 
				m_consoleInfo->srWindow.Top;
		//calce cursor position;
		char tempbuf[1024];
		sprintf(tempbuf,"\x1b[%d;%dH",cursor_x+1,cursor_y+1);
		buffer+=tempbuf;
		int scroll_count = 0;
		telnet->parse((char*)(buffer.c_str()),buffer.length(),&scroll_count);
		telnet->term_data = saved_term_data;
	}
	*/
	//adjust term_data;
	//telnet->repaint();
}

void InitCMDTerm(void *frontend,int buflines)
{
}

void DisconnectFrontend(void *frontend)
{
	SCD_Telnet *telnet=(SCD_Telnet*)frontend;
	telnet->close();
	//delete telnet
}

#endif
