/******************************************************************************
 * Name:        scd_wxssh.cpp
 * Purpose:     a wrapper of SSH in libssh library
 * 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_WXEXEC_CPP
#define SCD_WXEXEC_CPP
#include "scd_wxexec.h"


wxString builtin_cmds[]={
"ASSOC    "  
,"AT      "
,"ATTRIB  "
,"BREAK   "
,"CACLS   "
,"CALL    "
,"CD      "
,"CHCP    "
,"CHDIR   "
,"CHKDSK  "
,"CHKNTFS "
,"CLS     "
,"CMD     "
,"COLOR   "
,"COMP    "
,"COMPACT "
,"CONVERT "
,"COPY    "
,"DATE    "
,"DEL     "
,"DIR     "
,"DISKCOMP"
,"DISKCOPY"
,"DOSKEY  "
,"ECHO    "
,"ENDLOCAL"
,"ERASE   "
,"EXIT    "
,"FC      "
,"FIND    "
,"FINDSTR "
,"FOR     "
,"FORMAT  "
,"FTYPE   "
,"GOTO    "
,"GRAFTABL"
,"HELP    "
,"IF      "
,"LABEL   "
,"MD      "
,"MKDIR   "
,"MODE    "
,"MORE    "
,"MOVE    "
,"PATH    "
,"PAUSE   "
,"POPD    "
,"PRINT   "
,"PROMPT  "
,"PUSHD   "
,"RD      "
,"RECOVER "
,"REM     "
,"REN     "
,"RENAME  "
,"REPLACE "
,"RMDIR   "
,"SET     "
,"SETLOCAL"
,"SHIFT   "
,"SORT    "
,"START   "
,"SUBST   "
,"TIME    "
,"TITLE   "
,"TREE    "
,"TYPE    "
,"VER     "
,"VERIFY  "
,"VOL     "
,"XCOPY   "
,wxEmptyString
};
int inited_exec=false;
int countid = 0;
#define CMD_TITLE_ENJ	"__MeConsole_internal_cmd"
#define CONSOLE_CLASS_NAME "ConsoleWindowClass"

HWND enjFindWindow(const char *win_name)
{
	return FindWindow(CONSOLE_CLASS_NAME,win_name);
}

void PressControlKey(HWND mpcwnd, int key)
{
	//SendMessage(mpcwnd,WM_KEYDOWN,VK_CONTROL,'\0');
	//SendMessage(mpcwnd,WM_KEYDOWN,key,'\0');
	SendMessage(mpcwnd,WM_CHAR,VK_CANCEL,'\0');
	//SendMessage(mpcwnd,WM_KEYUP,VK_CONTROL,'\0');
	//SendMessage(mpcwnd,WM_KEYUP,key,'\0');
}

void PressControlC(HWND mpcwnd)
{
	//HWND mpcwnd = FindWindow("ConsoleWindowClass", NULL); 
	SendMessage(mpcwnd,WM_KEYDOWN,VK_CONTROL,'\0');
	SendMessage(mpcwnd,WM_KEYDOWN,'C','\0');
	SendMessage(mpcwnd,WM_CHAR,3,'\0');
	SendMessage(mpcwnd,WM_KEYUP,VK_CONTROL,'\0');
	SendMessage(mpcwnd,WM_KEYUP,'C','\0');
}

bool enjSetFocus(HWND wnd)
{
	HWND tmp = SetFocus(wnd);
	if ( !tmp) {
		DWORD le = GetLastError();
			return false;
	}
	return true;
}

void PressKey1(HWND wnd, UINT msg,WPARAM wp, LPARAM lp)
{
	SendMessage(wnd,msg,wp,lp);
}

void PressKey2(HWND wnd, UINT msg,WPARAM wp, LPARAM lp)
{
	SendMessage(wnd,msg,wp,lp);
}

void PressKey3(HWND wnd, UINT msg,WPARAM wp, LPARAM lp)
{
	SendMessage(wnd,msg,wp,lp);
}

void PressUP(HWND mpcwnd)
{
	//HWND mpcwnd = FindWindow("ConsoleWindowClass", NULL); 
	SendMessage(mpcwnd,WM_KEYDOWN,VK_UP,'\0');
	SendMessage(mpcwnd,WM_KEYUP,VK_UP,'\0');
}

void PressDOWN(HWND mpcwnd)
{
	//HWND mpcwnd = FindWindow("ConsoleWindowClass", NULL); 
	SendMessage(mpcwnd,WM_KEYDOWN,VK_DOWN,'\0');
	SendMessage(mpcwnd,WM_KEYUP,VK_DOWN,'\0');
}

void SendWMCharMessage(HWND mpcwnd, int key){
	SendMessage(mpcwnd,WM_CHAR,key,'\0');
}

void SendWMKeyDown(HWND mpcwnd, int key){
	SendMessage(mpcwnd,WM_KEYDOWN,key,'\0');
}

void SendWMKeyUp(HWND mpcwnd, int key){
	SendMessage(mpcwnd,WM_KEYUP,key,'\0');
}

void PressKey(HWND mpcwnd, int key){
	SendMessage(mpcwnd,WM_KEYDOWN,key,'\0');
	SendMessage(mpcwnd,WM_KEYUP,key,'\0');
}

// ============================================================================

SCD_wxEXEC::SCD_wxEXEC(wxSocketFlags /*flags*/)
{
		/*
	if ( !inited_exec ) {
		for ( int i=0;builtin_cmds[i]!=wxEmptyString;i++){
			builtin_cmds[i].Trim();
			wxLogMessage("[%s]",builtin_cmds[i]);
		}
		inited_exec=true;
	}
	*/
	showed = 0;
	m_handler=NULL;
	m_out = NULL;
	m_in = m_err = NULL;
	his_pos = 0;
	m_pid = 0;
	m_hwnd=NULL;
	caret_pos = 0;
	process=NULL;
	connect_state = 0;
	intLastCount = 0;
	unread_len = 0;
	input_len =0;

	evt_handler = NULL;
	m_clientdata = NULL;

	unread_len =0;
	inited=false;
}
// ----------------------------------------------------------------------------
SCD_wxEXEC::~SCD_wxEXEC()
{
	if ( process ) {
        process->SetNextHandler(NULL);
		process->CloseOutput();
		process=NULL;
	}
	//因為要刪除此物件必須從外部呼叫 Destroy() 不能直接刪除 delete sock
	//因此如果這邊還呼叫 Destroy() 會造成無限回圈
//	Destroy();
//wxMessageBox( _T("~SCD_wxEXEC") );
}

// ----------------------------------------------------------------------------
bool SCD_wxEXEC::Destroy()
{
	if ( process ) {
        process->SetNextHandler(NULL);
		process->CloseOutput();
		process=NULL;
	}
	extern void CloseConsoleHandle(void *handle);
	CloseConsoleHandle(m_handler);

	return true;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
bool SCD_wxEXEC::Connect(SiteInfo &si)
{
	if( ! IsDisconnected() )	return false;

    wxString cmd = si.ip;
		/*wxGetTextFromUser(_T("Enter the command to launch: "),
                                     DIALOG_TITLE,
                                     m_cmdLast);
									 */
    if ( cmd.empty() )
        return false;
	wxString real_cmd = cmd;	
	if ( cmd.Left(3).CmpNoCase("cmd") != 0 ) {
		real_cmd = "cmd /k " + cmd;
	}
	extern void* NewConsoleHandle(void *evt);
	if ( !m_handler ) m_handler = NewConsoleHandle(parent);
	extern bool StartShellProcess(void *handle,wchar_t *scshell,wchar_t *initdir,wchar_t *title,int rows,int cols);
	//char temp[10]="";
	wchar_t temp[10];
	mbstowcs(temp,"",10);
	wchar_t realcmd[1024];
	mbstowcs(realcmd,real_cmd.c_str(),1024);
	StartShellProcess(m_handler,realcmd,temp,temp,24,80);

#if 0
    //process = wxProcess::Open(cmd,wxEXEC_ASYNC);
	//wxWindow * win=wxTheApp->GetTopWindow();
	wxWindow *win=(wxWindow *) evt_handler;
	//win->GetHandle();
	wxString real_cmd;	
	title = wxString::Format("%s_%p_%d",CMD_TITLE_ENJ,win->GetHandle(),countid++);
	if ( cmd.Left(3).CmpNoCase("cmd") == 0 ) {
		real_cmd = cmd + " /k TITLE " + title;
	}else{
		real_cmd = cmd;
		//real_cmd = "cmd /k TITLE " + title + "\n" + cmd +"\n";
	}
	//::AllocConsole();
	//HANDLE inhandle = GetStdHandle(STD_INPUT_HANDLE);

//	HWND c_hwnd = GetConsoleWindow();
	iswincmd=1;
    process = new wxProcess();
	process->Redirect();
    //process = new wxProcess();
    //m_pid = wxExecute(real_cmd, wxEXEC_ASYNC |wxEXEC_NOHIDE , process);
    m_pid = wxExecute(real_cmd, wxEXEC_ASYNC , process);
	extern long wxExecute2(const wxString& cmd, int flags, wxProcess *handler);
    //m_pid = wxExecute2(cmd, wxEXEC_ASYNC  , process);
	
    if ( !process )
    {
        wxLogError(_T("Failed to launch the command."));
        return false;
    }
    wxOutputStream *out = process->GetOutputStream();
    if ( !out )
    {
        wxLogError(_T("Failed to connect to child stdin"));
        return false;
    }

    wxInputStream *in = process->GetInputStream();
    if ( !in )
    {
        wxLogError(_T("Failed to connect to child stdout"));
        return false;
    }
	
    m_out = (process->GetOutputStream());
    m_in= (process->GetInputStream());
    m_err=(process->GetErrorStream());

    process->SetNextHandler(evt_handler);
#endif

	connect_state = 2;	
	SendEvent(wxSOCKET_CONNECTION);
	return true;
}

bool SCD_wxEXEC::Connect(wxIPV4address& address, wxString _username)
{
	if( ! IsDisconnected() )	return false;

	SendEvent(wxSOCKET_CONNECTION);
	
	return true;
}
// ----------------------------------------------------------------------------
bool SCD_wxEXEC::isLogined()
{	return blLogined;	}
// ----------------------------------------------------------------------------
bool SCD_wxEXEC::Login(wxString /*_username*/, wxString _password)
{
//wxMessageBox("login() start");
	if(blLogined)	return true;

	//因為 ssh_userauth_password() 是 blocking fucntion
	//因此放到 Thread 中執行
	return true;
}
// ----------------------------------------------------------------------------
/*
bool SCD_wxEXEC::init_ssh_channel()	//在登入後初始化 channel
{
//	if( !session || !blLogined )	return false;

	return true;
}
*/
// ----------------------------------------------------------------------------
void SCD_wxEXEC::Close()
{
	if( IsDisconnected() ) return;
	if ( process ) {
        process->SetNextHandler(NULL);
		process->CloseOutput();
		process=NULL;
		if ( wxProcess::Exists(m_pid))
		wxKill(m_pid,wxSIGKILL,0,wxKILL_CHILDREN);
	}
	connect_state = 0;
	SendEvent(wxSOCKET_LOST);

	//connect_state = 0;
	//這裡不可以產生 wxSOCKET_LOST 事件, 因為如果此一方法是在此物件被 delete 時產生時,
	//會造成 crash

}
// ----------------------------------------------------------------------------
void SCD_wxEXEC::SendEvent(wxSocketNotify v)
{
	if( evt_handler )
	{
		wxSocketEvent e( evt_id );
		e.m_event = v;
		evt_handler->AddPendingEvent(e);
	}
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
bool SCD_wxEXEC::IsDisconnected()
{	return connect_state == 0;	}
// ----------------------------------------------------------------------------
bool SCD_wxEXEC::IsConnected()
{	return connect_state == 2 ;
	   //	&& !process->closed;	
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_wxEXEC::Read(void * buffer, wxUint32 nbytes)
{
    //buffer[in.Read(buffer, WXSIZEOF(buffer) - 1).LastRead()] = _T('\0');
	intLastCount = 0;
	extern int ReadCMDConsoleOutput(void *handle,char *buf,int len);
	input_len += ReadCMDConsoleOutput(m_handler,input_buf+input_len,nbytes);
	if ( input_len ) {
		memmove(buffer,input_buf,input_len);
		intLastCount = input_len;
		input_len=0;
	}
	return;
#if 0
	if (!m_hwnd && title != wxEmptyString ) {
		m_hwnd = enjFindWindow(title.c_str());
		//ShowWindow(m_hwnd,SW_HIDE);

		//if (!m_hwnd) wxLogMessage("NULL invalid window handle");
			//return false;
	}
	if ( !m_in || !m_err) return;
	if ( m_in->CanRead() ) {	
		m_in->Read(input_buf+input_len,nbytes);
		input_len += m_in->LastRead();
	}
	if ( m_err->CanRead()) {
		m_err->Read(input_buf+input_len,nbytes);
		input_len += m_err->LastRead();
	}
	if ( input_len ) {
		memmove(buffer,input_buf,input_len);
		intLastCount = input_len;
		input_len=0;
	}
#endif
}


void SCD_wxEXEC::ReSize(int width,int height)
{
	if ( width<5 || height< 5) return;
	extern void AdjustConsoleSize(void *handle,int _c,int _r);
	AdjustConsoleSize(m_handler,width,height);
}

void SCD_wxEXEC::SetInputBuffer(char *buf,int len)
{
}


#if 0
ASSOC    Displays or modifies file extension associations.
AT       Schedules commands and programs to run on a computer.
ATTRIB   Displays or changes file attributes.
BREAK    Sets or clears extended CTRL+C checking.
CACLS    Displays or modifies access control lists (ACLs) of files.
CALL     Calls one batch program from another.
CD       Displays the name of or changes the current directory.
CHCP     Displays or sets the active code page number.
CHDIR    Displays the name of or changes the current directory.
CHKDSK   Checks a disk and displays a status report.
CHKNTFS  Displays or modifies the checking of disk at boot time.
CLS      Clears the screen.
CMD      Starts a new instance of the Windows command interpreter.
COLOR    Sets the default console foreground and background colors.
COMP     Compares the contents of two files or sets of files.
COMPACT  Displays or alters the compression of files on NTFS partitions.
CONVERT  Converts FAT volumes to NTFS.  You cannot convert the
         current drive.
COPY     Copies one or more files to another location.
DATE     Displays or sets the date.
DEL      Deletes one or more files.
DIR      Displays a list of files and subdirectories in a directory.
DISKCOMP Compares the contents of two floppy disks.
DISKCOPY Copies the contents of one floppy disk to another.
DOSKEY   Edits command lines, recalls Windows commands, and creates macros.
ECHO     Displays messages, or turns command echoing on or off.
ENDLOCAL Ends localization of environment changes in a batch file.
ERASE    Deletes one or more files.
EXIT     Quits the CMD.EXE program (command interpreter).
FC       Compares two files or sets of files, and displays the differences
         between them.
FIND     Searches for a text string in a file or files.
FINDSTR  Searches for strings in files.
FOR      Runs a specified command for each file in a set of files.
FORMAT   Formats a disk for use with Windows.
FTYPE    Displays or modifies file types used in file extension associations.
GOTO     Directs the Windows command interpreter to a labeled line in a
         batch program.
GRAFTABL Enables Windows to display an extended character set in graphics
         mode.
HELP     Provides Help information for Windows commands.
IF       Performs conditional processing in batch programs.
LABEL    Creates, changes, or deletes the volume label of a disk.
MD       Creates a directory.
MKDIR    Creates a directory.
MODE     Configures a system device.
MORE     Displays output one screen at a time.
MOVE     Moves one or more files from one directory to another directory.
PATH     Displays or sets a search path for executable files.
PAUSE    Suspends processing of a batch file and displays a message.
POPD     Restores the previous value of the current directory saved by PUSHD.
PRINT    Prints a text file.
PROMPT   Changes the Windows command prompt.
PUSHD    Saves the current directory then changes it.
RD       Removes a directory.
RECOVER  Recovers readable information from a bad or defective disk.
REM      Records comments (remarks) in batch files or CONFIG.SYS.
REN      Renames a file or files.
RENAME   Renames a file or files.
REPLACE  Replaces files.
RMDIR    Removes a directory.
SET      Displays, sets, or removes Windows environment variables.
SETLOCAL Begins localization of environment changes in a batch file.
SHIFT    Shifts the position of replaceable parameters in batch files.
SORT     Sorts input.
START    Starts a separate window to run a specified program or command.
SUBST    Associates a path with a drive letter.
TIME     Displays or sets the system time.
TITLE    Sets the window title for a CMD.EXE session.
TREE     Graphically displays the directory structure of a drive or path.
TYPE     Displays the contents of a text file.
VER      Displays the Windows version.
VERIFY   Tells Windows whether to verify that your files are written
         correctly to a disk.
VOL      Displays a disk volume label and serial number.
XCOPY    Copies files and directory trees.

#endif
// ----------------------------------------------------------------------------

void enjAddString(SCD_wxEXEC *_p,char* buffer,int len)
{
	wxString input(buffer,len);
	_p->input_buf[_p->input_len++]='\x1b';
	_p->input_buf[_p->input_len++]='[';
	wxString lenstr= wxString::Format("%d",len);
	memmove(_p->input_buf+_p->input_len,lenstr.c_str(),lenstr.Len());
	_p->input_len+=lenstr.Len();
	_p->input_buf[_p->input_len++]='@';
	_p->send_buf = wxString::Format("%s%s%s",
					_p->send_buf.Mid(0,_p->caret_pos),
					input,
					_p->send_buf.Mid(_p->caret_pos));
	//send_buf +=key;
	_p->caret_pos+=len;
	memmove(_p->input_buf+_p->input_len,buffer,len);
	_p->input_len+=len;
	for ( int i=0;i<len;i++){
		::SendMessage(_p->m_hwnd,WM_CHAR,*((char *)buffer+i),0);
	}
	
}
void enjGoLeft(SCD_wxEXEC *_p) {
	_p->input_buf[_p->input_len++]='\b';
}

void enjGoRight(SCD_wxEXEC *_p)
{
	_p->input_buf[_p->input_len++]='\x1b';
	_p->input_buf[_p->input_len++]='[';
	_p->input_buf[_p->input_len++]='C';

}

void enjCleanLineTail(SCD_wxEXEC *_p)
{
	_p->input_buf[_p->input_len++]='\x1b';
	_p->input_buf[_p->input_len++]='[';
	_p->input_buf[_p->input_len++]='K';
}

void enjDeleteOnChar(SCD_wxEXEC *_p)
{
	_p->input_buf[_p->input_len++]='\b';
	_p->input_buf[_p->input_len++]='\x1b';
	_p->input_buf[_p->input_len++]='[';
	_p->input_buf[_p->input_len++]='P';
}

extern void ForwardMsgConsole(void *handle,UINT uMsg, WPARAM wParam, LPARAM lParam);
void SCD_wxEXEC::Write(const void * buffer, wxUint32 nbytes)
{
	if ( !IsConnected()) return;
	wxString input((char *)buffer,nbytes);
	char key = *((char *)buffer);
	if ( key == 3 ) {
	ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_CONTROL,'\0');
	ForwardMsgConsole(m_handler,WM_KEYDOWN,'C','\0');
	ForwardMsgConsole(m_handler,WM_CHAR,3,'\0');
	ForwardMsgConsole(m_handler,WM_KEYUP,VK_CONTROL,'\0');
	ForwardMsgConsole(m_handler,WM_KEYUP,'C','\0');
	ForwardMsgConsole(m_handler,WM_KEYUP,VK_CONTROL,'\0');

//		PressControlC(m_hwnd);
	}else/* if ( key == 13 ){//keyenter
		int i;
		for(i=0;i<caret_pos;i++) input_buf[input_len++]='\b';
		if ( send_buf.Len() > 0 ) {
			if ( his_com.Count() == 0 || send_buf != his_com.Last() ){
				if ( his_com.Count() > 100 ){
					his_com.RemoveAt(0,1);
				}
				his_com.Add(send_buf);
			}
		}
		his_pos=his_com.Count();
		if ( 1 ) {
		send_buf +="\n";
		m_out->Write(send_buf.c_str(),send_buf.Len());
		}else{
			input_buf[input_len++]='\n';
			PressKey1(m_hwnd,WM_CHAR,VK_RETURN,0);
		}
		send_buf.Clear();
		caret_pos = 0;

	}else */if ( key == 127 ) {//backspace
		ForwardMsgConsole(m_handler,WM_CHAR,VK_BACK,0);
	} else if ( key == 9 ) {
		ForwardMsgConsole(m_handler,WM_CHAR,VK_TAB,0);
		//skip now,unexpect behaviour...
	}else if ( input == "\x1b\x5b\x44" ) {//goLeft
		ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_LEFT,0);
	} else if ( input == "\x1b\x5b\x43" ) {//goRight
			ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_RIGHT,0);
	}else if ( input == "\x1b\x5b\x41" //keyup
					||  input == "\x1b\x5b\x42" ) {//keydown
		if ( input == "\x1b\x5b\x41" ) {// keyup
			ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_UP,0);
		}else{//keydown
			ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_DOWN,0);
		}
	}else if ( input == "\x1b\x5b\x35\x7e" ||
			input == "\x1b\x5b\x36\x7e"  ){
			//skip
	}else if ( input == "\x1b\x5b\x31\x7e" ) {//keyhome
			ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_HOME,0);
	}else if ( input == "\x1b\x5b\x34\x7e"){//keyend
			ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_END,0);
	}else /*if ( input == "\x1bWXK_F8" ) {
		//skip
		ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_F8,0);
	}else if ( input == "\x1bWXK_F7" ) {
		ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_F7,0);
	}else if ( input.Find("\x1bWXK_F") != -1 ) {
		int fkey = 0;
		input.Mid(strlen("\x1bWXK_F")).ToLong(&fkey);
		ForwardMsgConsole(m_handler,WM_KEYDOWN,VK_F1+fkey,0);

	}else */if ( input == "\x1b" ) {//WXK_ESCAPE
		ForwardMsgConsole(m_handler,WM_KEYDOWN,'\x1b',0);
	} else {
		for ( int i=0;i<nbytes;i++){
			char key = *((char *)buffer +i);
			ForwardMsgConsole(m_handler,WM_CHAR,key,0);
		}
	//	enjAddString(this,(char *)buffer,nbytes);
	}
}

void SCD_wxEXEC::WriteSpecialKey(int key)
{
	int realkey =0;
	if ( key >= WXK_F1 && key <=WXK_F24){
		if ( key == WXK_F12 ) {
			extern void ShowConsoleWindow(void *handle,int show);
			showed=!showed;
			ShowConsoleWindow(m_handler,showed);
		}else{
			realkey = key - WXK_F1 + VK_F1;
			ForwardMsgConsole(m_handler,WM_KEYDOWN,realkey,0);
		}
	}
}

void SCD_wxEXEC::SendTextToConsole(char *text)
{
	extern void SendTextToConsole(void *handle,char *text);
	SendTextToConsole(m_handler,text);
}

#if 0
void SCD_wxEXEC::Write(const void * buffer, wxUint32 nbytes)
{
	if ( !IsConnected()) return;
	wxString input((char *)buffer,nbytes);
	char key = *((char *)buffer);

	if ( key == 3 ) {
		int i;
		for(i=0;i<caret_pos;i++)
			input_buf[input_len++]='\b';
		send_buf.Clear();
		caret_pos=0;
		PressControlC(m_hwnd);
		enjCleanLineTail(this);
		
	}else if ( key == 13 ){//keyenter
		int i;
		for(i=0;i<caret_pos;i++) input_buf[input_len++]='\b';
		if ( send_buf.Len() > 0 ) {
			if ( his_com.Count() == 0 || send_buf != his_com.Last() ){
				if ( his_com.Count() > 100 ){
					his_com.RemoveAt(0,1);
				}
				his_com.Add(send_buf);
			}
		}
		his_pos=his_com.Count();
		if ( 1 ) {
		send_buf +="\n";
		m_out->Write(send_buf.c_str(),send_buf.Len());
		}else{
			input_buf[input_len++]='\n';
			PressKey1(m_hwnd,WM_CHAR,VK_RETURN,0);
		}
		send_buf.Clear();
		caret_pos = 0;

	}else if ( key == 127 ) {//backspace
		if ( caret_pos > 0 ) {
			caret_pos--;
			send_buf.Remove(caret_pos,1);
			enjDeleteOnChar(this);
		}
		::SendMessage(m_hwnd,WM_CHAR,VK_BACK,0);
	} else if ( key == 9 ) {
		//skip now,unexpect behaviour...
	}else if ( input == "\x1b\x5b\x44" ) {//goLeft
		::SendMessage(m_hwnd,WM_KEYDOWN,VK_LEFT,0);
		if ( caret_pos > 0 ) {
			caret_pos--;
			enjGoLeft(this);
		}
	} else if ( input == "\x1b\x5b\x43" ) {//goRight
			::SendMessage(m_hwnd,WM_KEYDOWN,VK_RIGHT,0);
		if ( send_buf.Len() > caret_pos ){
			caret_pos++;
			enjGoRight(this);
		}
	}else if ( input == "\x1b\x5b\x41" //keyup
					||  input == "\x1b\x5b\x42" ) {//keydown
		if (!iswincmd) return;
		int pre_pos = his_pos;
		if ( input == "\x1b\x5b\x41" ) {// keyup
			::SendMessage(m_hwnd,WM_KEYDOWN,VK_UP,0);
			if ( his_pos > 0 ) his_pos--;
		}else{//keydown
			::SendMessage(m_hwnd,WM_KEYDOWN,VK_DOWN,0);
			if ( his_pos < (his_com.Count()-1)) his_pos++;
		}

		 if ( pre_pos != his_pos ){
			//clean line
			int i;
			for(i=0;i<caret_pos;i++)
				input_buf[input_len++]='\b';
			send_buf.Clear();
			caret_pos=0;
			enjCleanLineTail(this);
			
			send_buf = his_com[his_pos];

			memmove(input_buf+input_len,send_buf.c_str(),send_buf.Len());
			input_len+=send_buf.Len();
			caret_pos=send_buf.Len();
		}
		
	}else if ( input == "\x1b\x5b\x35\x7e" ||
			input == "\x1b\x5b\x36\x7e"  ){
			//skip
	}else if ( input == "\x1b\x5b\x31\x7e" ) {//keyhome
			for ( ;caret_pos!=0;caret_pos--)
				enjGoLeft(this);
			::SendMessage(m_hwnd,WM_KEYDOWN,VK_HOME,0);
	}else if ( input == "\x1b\x5b\x34\x7e"){//keyend
			::SendMessage(m_hwnd,WM_KEYDOWN,VK_END,0);
			for (;caret_pos<send_buf.Len();caret_pos++){
				enjGoRight(this);
			}
	}else if ( input == "\x1bWXK_F8" ) {
		//skip
		/*
		if (!iswincmd) return;
		if ( his_pos >0 ) {
			//clean line
			int i;
			for(i=0;i<caret_pos;i++)
				input_buf[input_len++]='\b';
			send_buf.Clear();
			caret_pos=0;
			input_buf[input_len++]='\x1b';
			input_buf[input_len++]='[';
			input_buf[input_len++]='K';
			
			his_pos--;
			send_buf = his_com[his_pos];

			memmove(input_buf+input_len,send_buf.c_str(),send_buf.Len());
			input_len+=send_buf.Len();
			caret_pos=send_buf.Len();
		}
		if ( his_pos == 0 ) his_pos = his_com.Count();
		::SendMessage(m_hwnd,WM_KEYDOWN,VK_F8,0);
		*/
		
	}else if ( input == "\x1bWXK_F7" ) {
		if ( !iswincmd ) return;
		int i;
		for(i=0;i<caret_pos;i++)
			input_buf[input_len++]='\b';
		send_buf.Clear();
		caret_pos=0;
		input_buf[input_len++]='\x1b';
		input_buf[input_len++]='[';
		input_buf[input_len++]='K';
		for (int j=0;j<his_com.Count();j++){
			input_buf[input_len++]='\n';
			input_buf[input_len++]='\t';
			wxString lenstr= wxString::Format("%3d ",j);
			memmove(input_buf+input_len,lenstr.c_str(),lenstr.Len());
			input_len+=lenstr.Len();
			memmove(input_buf+input_len,his_com[j].c_str(),his_com[j].Len());
			input_len+=his_com[j].Len();
		}
		m_out->Write("\n",1);
		::SendMessage(m_hwnd,WM_KEYDOWN,VK_F7,0);
		
	}else if ( input == "\x1b" ) {//WXK_ESCAPE
		int i;
		for(i=0;i<caret_pos;i++)
			input_buf[input_len++]='\b';
		send_buf.Clear();
		caret_pos=0;
		enjCleanLineTail(this);
		::SendMessage(m_hwnd,WM_KEYDOWN,'\x1b',0);
	} else {
		enjAddString(this,(char *)buffer,nbytes);
		//for ( int i=0;i<input.Len();i++)
	}
}
#endif

// ----------------------------------------------------------------------------
void SCD_wxEXEC::Unread(const void * buffer, wxUint32 nbytes)
{
	if( ! IsConnected() )	return;
	memmove(input_buf,buffer, nbytes);
	input_len = nbytes;
}
// ----------------------------------------------------------------------------
wxUint32 SCD_wxEXEC::LastCount()
{	//return intLastCount;	
	return intLastCount;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_wxEXEC::SetEventHandler(wxEvtHandler& handler, int id)
{
	evt_handler = & handler;
	evt_id = id;
}
// ----------------------------------------------------------------------------
void SCD_wxEXEC::SetClientData(void *data)
{	m_clientdata = data;	}
// ----------------------------------------------------------------------------
void* SCD_wxEXEC::GetClientData()
{	return m_clientdata;	}
// ----------------------------------------------------------------------------

void SCD_wxEXEC::close_session()
{
}

void SCD_wxEXEC::AdjustBufferSize(int lines)
{
	extern void AdjustBufferSize(void *handle,int _l);
	AdjustBufferSize(m_handler,lines);
	return;
}

void SCD_wxEXEC::SetScrollBarPos(int ydelta)
{
	extern void SetConsoleScrollBarPos(void *handle,int nDelta);
	SetConsoleScrollBarPos(m_handler,ydelta);
}

// ============================================================================

void enjProcess::OnTerminate(int pid, int status)
{
    //wxLogStatus(m_parent, _T("Process %u ('%s') terminated with exit code %d."), pid, m_cmd.c_str(), status);
	closed = true;	
    // we're not needed any more
    //delete this;
}

				/*
			int i=0;
			int pos=0;
			wxString right_c;
			char cmd_s[1024];
		    memmove(cmd_s,send_buf.c_str(),send_buf.Len());
			cmd_s[send_buf.Len()]='\0';
			if ( cmd_s[0] == '!' ) {
				i=1;
				if ( cmd_s[i] == '!' ){//!!
					pos = his_com.Count()-1;
					i++;
				}else{
					//!%d
					for(;cmd_s[i] != '\0' && isdigit(cmd_s[i]);i++){
						pos = pos * 10 + cmd_s[i] - '0';
						//i++;
					}
				}
			}
			if ( i > 1 &&
				(cmd_s[i] == '\0' || cmd_s[i] == ' '))
			{
				//find !! or !%d
				if ( pos >= his_com.Count() ){
					input_buf[input_len++]='\n';
					wxString err_m= wxString::Format("%d: Command not found.",pos) ;
					memmove(input_buf+input_len,err_m.c_str(),err_m.Len());
					input_len+=err_m.Len();
					//input_buf[input_len++]='\n';
					send_buf.Clear();
					caret_pos=0;
				}else{
					send_buf = his_com[pos];
					input_buf[input_len++]='\n';
					if ( cmd_s[i] == ' ' ){
						send_buf += cmd_s+i;
					}
					memmove(input_buf+input_len,send_buf.c_str(),send_buf.Len());
					input_len+=send_buf.Len();
					input_buf[input_len++]='\r';
				}
			}
			*/

#endif
