/******************************************************************************
 * 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_COMMON_SOCK_CPP
#define SCD_COMMON_SOCK_CPP
#include "scd_common_sock.h"


extern "C" {
#include "terminal.h"
#include "storage.h"
#include "network.h"
#include "win_res.h"
	    extern SOCKET first_socket(int *), next_socket(int *);
	    extern int select_result(WPARAM, LPARAM);
		extern SOCKET get_win_sock(Socket sock);
	    extern int select_result_sock(Socket,WPARAM, LPARAM);
}

// ============================================================================
int putty_inited =0;

int SCD_wxCOMSOCK::from_backend(int is_stderr, const char *data, int len)
{
	return 0;
	/*
	if (!term) return 0;
	return term_data(term, is_stderr, data, len);
	*/
}

SCD_wxCOMSOCK::SCD_wxCOMSOCK(wxSocketFlags /*flags*/)
{
	back = NULL;
	backhandle = NULL;
//	ldisc = NULL;
//	term = NULL;
//	session = NULL;
//	channel = NULL;
//	readbuf = buffer_new();
//	input_buf = NULL;
	input_len = 0;
	blLogined = false;
	connect_state = 0;
	intLastCount = 0;
	unread_len = 0;
	not_read_after_notify = false;

	evt_handler = NULL;
	m_clientdata = NULL;

//	working_thread = NULL;
	sftp_count = 0;
//	m_destroyed = false;

	unread_len =0;
	inited=false;
}
// ----------------------------------------------------------------------------
SCD_wxCOMSOCK::~SCD_wxCOMSOCK()
{
	
	//因為要刪除此物件必須從外部呼叫 Destroy() 不能直接刪除 delete sock
	//因此如果這邊還呼叫 Destroy() 會造成無限回圈
//	Destroy();
//wxMessageBox( _T("~SCD_wxCOMSOCK") );
}
// ----------------------------------------------------------------------------
bool SCD_wxCOMSOCK::Destroy()
{
	return true;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
bool SCD_wxCOMSOCK::Connect(SiteInfo &si)
{
	if( ! IsDisconnected() )	return false;
	if ( !putty_inited ) {
	    sk_init();
		default_protocol = be_default_protocol;
		/* Find the appropriate default port. */
		{
		    int i;
		    default_port = 0; /* illegal */
		    for (i = 0; backends[i].backend != NULL; i++)
			if (backends[i].protocol == default_protocol) {
			    default_port = backends[i].backend->default_port;
			    break;
			}
		}	
	}
	putty_inited ++;

	ip = si.ip;//address.Hostname();
	port = si.port;//address.Service();
	username = si.username;
	{
	
		do_defaults(NULL, &cfg);
		cfg.logtype = LGTYP_NONE;
		cfg.close_on_exit = FORCE_OFF;
		strncpy(cfg.host, ip.c_str(), strlen(ip.c_str()));
		cfg.host[strlen(ip.c_str()) ] = '\0';
		if ( si.keyfile != wxEmptyString ) {
			strcpy(cfg.keyfile.path ,si.keyfile.c_str());
		}
	
	    /*
	     * Initialise the terminal. (We have to do this _after_
	     * creating the window, since the terminal is the first thing
	     * which will call schedule_timer(), which will in turn call
	     * timer_change_notify() which will expect hwnd to exist.)
	     */
		
	    netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
	
	    HANDLE handles[4];
	    handles[0] = netevent;
	
		
		{
		    const char *error;
		    char msg[1024], *title;
		    char *realhost;
		    int i;
		
		    /*
		     * Select protocol. This is farmed out into a table in a
		     * separate file to enable an ssh-free variant.
		     */
			if ( si.protocol == SOCK_TELNET ) {
				cfg.protocol = PROT_TELNET;
			}else if ( si.protocol == SOCK_SSH ){
				cfg.protocol = PROT_SSH;
			}
			//cfg.port = 23;
			//cfg.protocol = PROT_SSH;
			cfg.port = port;
		    back = NULL;
		    for (i = 0; backends[i].backend != NULL; i++)
			if (backends[i].protocol == cfg.protocol) {
			    back = backends[i].backend;
			    break;
			}
		    if (back == NULL) {
			char *str = dupprintf("%s Internal Error", appname);
			MessageBox(NULL, "Unsupported protocol number found",
				   str, MB_OK | MB_ICONEXCLAMATION);
			sfree(str);
			return false;
		    }
		
		    error = back->init(this, &backhandle, &cfg,
				       cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,
				       cfg.tcp_keepalives);
		    if (error) {
			char *str = dupprintf("%s Error", appname);
			sprintf(msg, "Unable to open connection to\n"
				"%.800s\n" "%s", cfg.host, error);
			MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
			sfree(str);
			return false;
		    }
		    if (*cfg.wintitle) {
			title = cfg.wintitle;
		    } else {
			sprintf(msg, "%s - %s", realhost, appname);
			title = msg;
		    }
		    sfree(realhost);
		    session_closed = FALSE;
		}
		
		sc = back->socket(backhandle);
		socket = get_win_sock(sc);
		inited=true;
		connect_state = 2;
	}
	SendEvent(wxSOCKET_CONNECTION);
	
	return true;
}

bool SCD_wxCOMSOCK::Connect(wxIPV4address& address, wxString _username)
{
	if( ! IsDisconnected() )	return false;
	if ( !putty_inited ) {
	    sk_init();
		default_protocol = be_default_protocol;
		/* Find the appropriate default port. */
		{
		    int i;
		    default_port = 0; /* illegal */
		    for (i = 0; backends[i].backend != NULL; i++)
			if (backends[i].protocol == default_protocol) {
			    default_port = backends[i].backend->default_port;
			    break;
			}
		}	
	}
	putty_inited ++;

	ip = address.Hostname();
	port = address.Service();
	username = _username;
	//因為 ssh_connect(), pubkey_get_hash(), ssh_userauth_autopubkey(), Login()
	//都是 blocking function, 因此放到 Thread 中執行
	{
	
		//cfg.logtype = LGTYP_DEBUG;
		do_defaults(NULL, &cfg);
		cfg.logtype = LGTYP_NONE;
		cfg.close_on_exit = FORCE_OFF;
		strncpy(cfg.host, ip.c_str(), strlen(ip.c_str()));
		cfg.host[strlen(ip.c_str()) ] = '\0';
//		wxSize ws = GetSize();
//		wxSize cs = telnet->getCharSize();
	
	    /*
	     * Initialise the terminal. (We have to do this _after_
	     * creating the window, since the terminal is the first thing
	     * which will call schedule_timer(), which will in turn call
	     * timer_change_notify() which will expect hwnd to exist.)
	     */
	    //term = term_init(&cfg, &ucsdata, this);
//	    logctx = log_init(NULL, &cfg);
	    //term_provide_logctx(term, logctx);
	    //term_size(term, cfg.height, cfg.width, cfg.savelines);
		
	    netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
	
	    HANDLE handles[4];
	    handles[0] = netevent;
	
		
		
	    //start_backend();
		{
		    const char *error;
		    char msg[1024], *title;
		    char *realhost;
		    int i;
		
		    /*
		     * Select protocol. This is farmed out into a table in a
		     * separate file to enable an ssh-free variant.
		     */
			//cfg.protocol = PROT_TELNET;
			//cfg.port = 23;
			cfg.protocol = PROT_SSH;
			cfg.port = 22;
		    back = NULL;
		    for (i = 0; backends[i].backend != NULL; i++)
			if (backends[i].protocol == cfg.protocol) {
			    back = backends[i].backend;
			    break;
			}
		    if (back == NULL) {
			char *str = dupprintf("%s Internal Error", appname);
			MessageBox(NULL, "Unsupported protocol number found",
				   str, MB_OK | MB_ICONEXCLAMATION);
			sfree(str);
			//cleanup_exit(1);
			return false;
		    }
		
		    error = back->init(this, &backhandle, &cfg,
				       cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,
				       cfg.tcp_keepalives);
//		    back->provide_logctx(backhandle, logctx);
		    if (error) {
			char *str = dupprintf("%s Error", appname);
			sprintf(msg, "Unable to open connection to\n"
				"%.800s\n" "%s", cfg.host, error);
			MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
			sfree(str);
			return false;
			//exit(0);
		    }
//		    window_name = icon_name = NULL;
		    if (*cfg.wintitle) {
			title = cfg.wintitle;
		    } else {
			sprintf(msg, "%s - %s", realhost, appname);
			title = msg;
		    }
		    sfree(realhost);
//		    set_title(NULL, title);
//		    set_icon(NULL, title);
		
		    /*
		     * Connect the terminal to the backend for resize purposes.
		     */
		    //term_provide_resize_fn(term, back->size, backhandle);
		
		    /*
		     * Set up a line discipline.
		     */
		    //ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
		
		    /*
		     * Destroy the Restart Session menu item. (This will return
		     * failure if it's already absent, as it will be the very first
		     * time we call this function. We ignore that, because as long
		     * as the menu item ends up not being there, we don't care
		     * whether it was us who removed it or not!)
		     */
			/*
		    for (i = 0; i < lenof(popup_menus); i++) {
			DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
		    }
			*/
		
		    session_closed = FALSE;
		}
		
	    //term_set_focus(term, 1);
		sc = back->socket(backhandle);
		socket = get_win_sock(sc);
		inited=true;
		connect_state = 2;
	}
	SendEvent(wxSOCKET_CONNECTION);
	
	return true;
}
// ----------------------------------------------------------------------------
bool SCD_wxCOMSOCK::isLogined()
{	return blLogined;	}
// ----------------------------------------------------------------------------
bool SCD_wxCOMSOCK::Login(wxString /*_username*/, wxString _password)
{
//wxMessageBox("login() start");
	if(blLogined)	return true;

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

	return true;
}
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::Close()
{
	if( IsDisconnected() ) return;
	close_session();
	connect_state = 0;
	SendEvent(wxSOCKET_LOST);
	//connect_state = 0;
	//這裡不可以產生 wxSOCKET_LOST 事件, 因為如果此一方法是在此物件被 delete 時產生時,
	//會造成 crash

}
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::SendEvent(wxSocketNotify v)
{
	if( evt_handler )
	{
		wxSocketEvent e( evt_id );
		e.m_event = v;
		evt_handler->AddPendingEvent(e);
	}
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
bool SCD_wxCOMSOCK::IsDisconnected()
{	return connect_state == 0;	}
// ----------------------------------------------------------------------------
bool SCD_wxCOMSOCK::IsConnected()
{	return connect_state == 2;	}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::Read(void * buffer, wxUint32 nbytes)
{
	int    connopen = 1;
	
	{
		intLastCount = 0;

	    WSANETWORKEVENTS things;
		WPARAM wp = (WPARAM) socket;
		if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {
                    static const struct { int bit, mask; } eventtypes[] = {
                        {FD_CONNECT_BIT, FD_CONNECT},
                        {FD_READ_BIT, FD_READ},
                        {FD_CLOSE_BIT, FD_CLOSE},
                        {FD_OOB_BIT, FD_OOB},
                        {FD_WRITE_BIT, FD_WRITE},
                        {FD_ACCEPT_BIT, FD_ACCEPT},
                    };
                    int e;

		    noise_ultralight(socket);
		    noise_ultralight(things.lNetworkEvents);
			int isget=0;
            for (e = 0; e < lenof(eventtypes); e++)
           		 if (things.lNetworkEvents & eventtypes[e].mask) {
           		     LPARAM lp;
           		     int err = things.iErrorCode[eventtypes[e].bit];
           		     lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
           		     connopen &= select_result(wp, lp);
					 isget=1;
   					 switch (WSAGETSELECTEVENT(lp)) {
   					   case FD_CONNECT:
							   wxLogMessage("FD_CONNECT");
								connect_state = 2;
							   break;
   					   case FD_READ:
							   memmove(buffer,input_buf,input_len);
							   intLastCount = input_len;
							   input_len =0;
							   break;
   					   case FD_OOB:
							   wxLogMessage("FD_OOB");
							   break;
   					   case FD_WRITE:
							   wxLogMessage("FD_WRITE");
							   break;
   					   case FD_CLOSE:
							   connect_state = 0;
							SendEvent(wxSOCKET_LOST);
						   wxLogMessage("FD_CLOSE");
						   break;
   					   case FD_ACCEPT:
						   wxLogMessage("FD_ACCEPT");
						   break;
   					 }
					 
           		 }

			if (!isget & input_len) {
			   memmove(buffer,input_buf,input_len);
			   intLastCount = input_len;
			   input_len =0;
			}
		}
	}
}
void SCD_wxCOMSOCK::ReSize(int width,int height)
{
	back->size(backhandle,width,height);
}

void SCD_wxCOMSOCK::SetInputBuffer(char *buf,int len)
{
	memmove(input_buf+input_len,buf,len);
	input_len +=len;
		/*
	input_len = 0;
	memmove(input_buf,unread,unread_len);
	input_len = unread_len ;
	unread_len = 0;
	memmove(input_buf+input_len,buf,len);
	input_len +=len;
	*/
}
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::Write(const void * buffer, wxUint32 nbytes)
{
	if ( !IsConnected()) return;
	back->send(backhandle,(char*)buffer,nbytes);
	//ldisc_send(ldisc,(char *)buffer,nbytes,0);
//	if( ! IsConnected() || !channel )	return;
}
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::Unread(const void * buffer, wxUint32 nbytes)
{
	if( ! IsConnected() )	return;
	memmove(input_buf,buffer, nbytes);
	input_len = nbytes;
}
// ----------------------------------------------------------------------------
wxUint32 SCD_wxCOMSOCK::LastCount()
{	//return intLastCount;	
	return intLastCount;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::SetEventHandler(wxEvtHandler& handler, int id)
{
	evt_handler = & handler;
	evt_id = id;
}
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::SetClientData(void *data)
{	m_clientdata = data;	}
// ----------------------------------------------------------------------------
void* SCD_wxCOMSOCK::GetClientData()
{	return m_clientdata;	}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
/*
SCD_wxSFTP* SCD_wxCOMSOCK::CreateSFTP()
{
	if( ! IsConnected() || ! isLogined() )	return NULL;

	SFTP_SESSION *_sftp_session = sftp_new(session);
	if( ! _sftp_session )	return NULL;

	if( sftp_init(_sftp_session) != 0 )	//init failed
	{
		sftp_free(_sftp_session);
		return NULL;
	}

	SCD_wxSFTP *_ftp = new SCD_wxSFTP( / *this, * / _sftp_session);
	sftp_count ++;	//add sftp count
	return _ftp;
}
*/
// ----------------------------------------------------------------------------
void SCD_wxCOMSOCK::DeleteSFTP(void *_ftp)
{
}

void SCD_wxCOMSOCK::close_session()
{
    //session_closed = TRUE;
	connect_state = 0;
/*
    if (ldisc) {
	ldisc_free(ldisc);
	ldisc = NULL;
    }
	*/
    if (back) {
	back->free(backhandle);
	//free(backhandle);
	//free(back);
	backhandle = NULL;
	back = NULL;
    }
	SendEvent(wxSOCKET_LOST);
	putty_inited--;
	if ( putty_inited == 0 ) sk_cleanup();
}
// ============================================================================
#endif
