//#include "stdafx.h"

//#include "Console.h"
#include <windows.h>
#include <wchar.h>
#include <psapi.h>
#include <dbghelp.h>

#include <string>

using namespace std;

#pragma warning(push)
#pragma warning(disable: 4244 4267 4511 4512 4701 4702)
#include <boost/smart_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/format.hpp>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string/trim.hpp>
using namespace boost;
#pragma warning(pop)


#include "shared/SharedMemNames.h"
#include "ConsoleHandler.h"

typedef unsigned short _ushort16;
//#define wchar_t char

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
//#define new DEBUG_NEW
#endif
//#include "atlbase.h"
//#include "atlstr.h"
/*
size_t wchar2char(const wchar_t *orig,char * out)
{
    //size_t origsize = wcslen(orig) + 1;
    //const size_t newsize = 100;
    //size_t convertedChars = 0;
    //char nstring[MAX_PATH];
    //wcstombs_s(&convertedChars, nstring, origsize, orig, _TRUNCATE);
	return wcstombs(nstring, orig, MAX_PATH);
	//return strdup(nstring);
}
size_t char2wchar(const char *orig,wchar_t *out,int len)
{
    //size_t origsize = strlen(orig) + 1;
    //const size_t newsize = 100;
    //size_t convertedChars = 0;
    //wchar_t wcstring[newsize];
    return mbstowcs(out, orig,MAX_PATH,len);
}
*/
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

//shared_ptr<SettingsHandler>	g_settingsHandler;
//////////////////////////////////////////////////////////////////////////////

ConsoleHandler::ConsoleHandler()
: m_hConsoleProcess()
, m_consoleParams()
, m_consoleInfo()
, m_consoleBuffer()
, m_consolePaste()
, m_newConsoleSize()
, m_newScrollPos()
, m_hMonitorThread()
, m_hMonitorThreadExit(shared_ptr<void>(::CreateEvent(NULL, FALSE, FALSE, NULL), ::CloseHandle))
{
}

ConsoleHandler::ConsoleHandler(void *evt)
: m_hConsoleProcess()
, m_consoleParams()
, m_consoleInfo()
, m_consoleBuffer()
, m_consolePaste()
, m_newConsoleSize()
, m_newScrollPos()
, m_hMonitorThread()
, m_hMonitorThreadExit(shared_ptr<void>(::CreateEvent(NULL, FALSE, FALSE, NULL), ::CloseHandle))
{
		
	//evt_owner = evt;
	front = evt;
	//InitCMDTerm(front,200);
	/*
	Term_Data_t *head=new Term_Data_t();
	head->full = 0;
	head->_n = NULL;
	head->_p = NULL;
	Term_Data_t *p=head;

	for (int i=0; i<MAX_TERM_BUFFERS;i++) {
		p->_n=new Term_Data_t();
		p->_n->_p = p;
		p=p->_n;
		p->full = 0;
		p->_n=NULL;
   	}
	p->_n = head;
	head->_p = p;
	wor_term = cur_term = head;
	pre_term = NULL;
	*/
	//wor_term = head->_n;
}

void ConsoleHandler::CloseHandle()
{
	StopMonitorThread();
	
	if ((m_consoleParams.Get() != NULL) && 
		(m_consoleParams->hwndConsoleWindow))
	{
		::SendMessage(m_consoleParams->hwndConsoleWindow, WM_CLOSE, 0, 0);
	}
}

ConsoleHandler::~ConsoleHandler()
{
	CloseHandle();
		/*
	StopMonitorThread();
	
	if ((m_consoleParams.Get() != NULL) && 
		(m_consoleParams->hwndConsoleWindow))
	{
		::SendMessage(m_consoleParams->hwndConsoleWindow, WM_CLOSE, 0, 0);
	}
	*/
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

void ConsoleHandler::SetupDelegates(ConsoleChangeDelegate consoleChangeDelegate, ConsoleCloseDelegate consoleCloseDelegate)
{
	m_consoleChangeDelegate	= consoleChangeDelegate;
	m_consoleCloseDelegate	= consoleCloseDelegate;
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

bool ConsoleHandler::StartShellProcess(const wstring& strCustomShell, const wstring& strInitialDir, const wstring& strConsoleTitle, DWORD dwStartupRows, DWORD dwStartupColumns)
{
	wstring	strShell(strCustomShell);
	
	if (strShell.length() == 0)
	{
		wchar_t	szComspec[MAX_PATH];

		::ZeroMemory(szComspec, MAX_PATH*sizeof(wchar_t));
		//mbstowcs();
		char tmp[MAX_PATH];
		
		//wcstombs(tmp,szComspec,MAX_PATH);
		if (::GetEnvironmentVariable("COMSPEC", tmp, MAX_PATH) > 0)
		{
			mbstowcs(szComspec,tmp,MAX_PATH);
			strShell = szComspec;		
		}
		else
		{
			strShell = L"cmd.exe";
		}
	}

	// TODO: build command line
	wstring	strShellCmdLine(strShell);
	wstring	strStartupTitle(strConsoleTitle);

	if (strStartupTitle.length() == 0)
	{
		strStartupTitle = str(wformat(L"Console command window 0x%08X") % this);
	}

	// setup the startup info struct
	STARTUPINFO si;
	::ZeroMemory(&si, sizeof(STARTUPINFO));
	si.dwFlags		= STARTF_USESHOWWINDOW;
	si.cb			= sizeof(STARTUPINFO);
	si.wShowWindow	= SW_SHOW;
#ifndef _DEBUG
	si.wShowWindow	= SW_HIDE;
#endif
	//si.lpTitle		= (wchar_t*)(strStartupTitle.c_str());
	char title[MAX_PATH];
	wcstombs(title,strStartupTitle.c_str(),MAX_PATH);
	si.lpTitle =title;//wcstombs(strStartupTitle.c_str());
	//si.lpTitle		= (char *)(strStartupTitle.c_str());

	PROCESS_INFORMATION pi;
	char initDir[MAX_PATH];
	wcstombs(initDir,strInitialDir.c_str(),MAX_PATH);
	char shellCmd[MAX_PATH];
	wcstombs(shellCmd,strShellCmdLine.c_str(),MAX_PATH);
	//string shellCmd(strShellCmdLine);
	if (!::CreateProcess(
			NULL,
			shellCmd,
			NULL,
			NULL,
			FALSE,
			CREATE_NEW_CONSOLE|CREATE_SUSPENDED,
			NULL,
			(strInitialDir.length() > 0) ? initDir : NULL,
			&si,
			&pi))
	{
		return false;
	}

	// create shared memory objects
	CreateSharedMemory(pi.dwProcessId);

	// write startup params
	m_consoleParams->dwConsoleMainThreadId	= pi.dwThreadId;
	m_consoleParams->dwParentProcessId		= ::GetCurrentProcessId();
	m_consoleParams->dwNotificationTimeout	= 10;//g_settingsHandler->GetConsoleSettings().dwChangeRefreshInterval;
	m_consoleParams->dwRefreshInterval		= 100;//g_settingsHandler->GetConsoleSettings().dwRefreshInterval;
	m_consoleParams->dwRows					= dwStartupRows;
	m_consoleParams->dwColumns				= dwStartupColumns;
	m_consoleParams->dwBufferRows			= 500;//g_settingsHandler->GetConsoleSettings().dwBufferRows;
	m_consoleParams->dwBufferColumns		= dwStartupColumns;//dwStartupRows;//g_settingsHandler->GetConsoleSettings().dwBufferColumns;

	m_hConsoleProcess = shared_ptr<void>(pi.hProcess, ::CloseHandle);

	// inject our hook DLL into console process
	if (!InjectHookDLL()) return false;

	// resume the console process
	::ResumeThread(pi.hThread);
	::CloseHandle(pi.hThread);

	// wait for hook DLL to set console handle
	if (::WaitForSingleObject(m_consoleParams.GetEvent(), 10000) == WAIT_TIMEOUT) return false;

	return true;
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

DWORD ConsoleHandler::StartMonitorThread()
{
	DWORD dwThreadId = 0;
	m_hMonitorThread = shared_ptr<void>(
		::CreateThread(
		NULL,
		0, 
		MonitorThreadStatic, 
		reinterpret_cast<void*>(this), 
		0, 
		&dwThreadId),
		::CloseHandle);

	return dwThreadId;
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

void ConsoleHandler::StopMonitorThread()
{
	::SetEvent(m_hMonitorThreadExit.get());
	::WaitForSingleObject(m_hMonitorThread.get(), 10000);
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

bool ConsoleHandler::CreateSharedMemory(DWORD dwConsoleProcessId)
{
	// create startup params shared memory
	m_consoleParams.Create((SharedMemNames::formatConsoleParams % dwConsoleProcessId).str());

	// create console info shared memory
	m_consoleInfo.Create((SharedMemNames::formatInfo % dwConsoleProcessId).str());

	// create console info shared memory
	m_cursorInfo.Create((SharedMemNames::formatCursorInfo % dwConsoleProcessId).str());

	// TODO: max console size
	m_consoleBuffer.Create((SharedMemNames::formatBuffer % dwConsoleProcessId).str(), 300*300);

	// paste info 
	m_consolePaste.Create((SharedMemNames::formatPasteInfo % dwConsoleProcessId).str());

	// new console size
	m_newConsoleSize.Create((SharedMemNames::formatNewConsoleSize % dwConsoleProcessId).str());

	// new scroll position
	m_newScrollPos.Create((SharedMemNames::formatNewScrollPos % dwConsoleProcessId).str());

	// TODO: separate function for default settings
	m_newConsoleSize->dwBufferSize = 500;
	m_consoleParams->dwRows		= 24;
	m_consoleParams->dwColumns	= 79;

	return true;
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//extern size_t WCharToChar(char* buf, const wchar_t* psz, size_t n);
bool ConsoleHandler::InjectHookDLL()
{
	// allocate memory for parameter in the remote process
	wstring				strHookDllPath(GetModulePath(NULL) + wstring(L"\\ConsoleHook.dll"));
	//char *tmp = (char *)(strHookDllPath.c_str());

	//WCharToChar(tmp,strHookDllPath.c_str(),1024);
	char hookDll[MAX_PATH];
	wcstombs(hookDll,strHookDllPath.c_str(),MAX_PATH);
	if (::GetFileAttributes(hookDll) == INVALID_FILE_ATTRIBUTES) return false;

	shared_ptr<void>	hRemoteThread;
	/*
	shared_ptr<wchar_t>	pszHookDllPathRemote(
							static_cast<wchar_t*>(::VirtualAllocEx(
														m_hConsoleProcess.get(), 
														NULL, 
														strHookDllPath.length()*sizeof(wchar_t), 
														MEM_COMMIT, 
														PAGE_READWRITE)),
							bind<BOOL>(::VirtualFreeEx, m_hConsoleProcess.get(), _1, NULL, MEM_RELEASE));
							*/

	LPVOID remoteaddr = ::VirtualAllocEx( m_hConsoleProcess.get(), 
						NULL, 
						strlen(hookDll),
						//strHookDllPath.length()*sizeof(wchar_t), 
						MEM_COMMIT, 
						PAGE_READWRITE);
#pragma warning(push)
#pragma warning(disable: 4127)
	if ( remoteaddr == NULL ) return false;
	//if (pszHookDllPathRemote.get() == NULL) return false;
	//wchar_t *real = pszHookDllPathRemote.get();
	//char hookDllRemote[MAX_PATH];
	//wcstombs(hookDllRemote,real,MAX_PATH);
	DWORD numbers=0;
	// write the memory
	if (!::WriteProcessMemory(
				m_hConsoleProcess.get(), 
				remoteaddr,
				//(PVOID)pszHookDllPathRemote.get(),
				//hookDllRemote,
				hookDll,
				strlen(hookDll),
				//(PVOID)pszHookDllPathRemote.get(), 
				//(PVOID)strHookDllPath.c_str(), 
				//strHookDllPath.length()*sizeof(wchar_t), 
				&numbers))
	{
		return false;
	}

	// get address to LoadLibraryW function
	//PTHREAD_START_ROUTINE pfnThreadRoutine = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryW");
	PTHREAD_START_ROUTINE pfnThreadRoutine = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
	if (pfnThreadRoutine == NULL) return false;

	HANDLE crthread = ::CreateRemoteThread(
							m_hConsoleProcess.get(), 
							NULL, 
							0, 
							pfnThreadRoutine, 
							remoteaddr,
							//hookDllRemote,
							//(PVOID)pszHookDllPathRemote.get(), 
							0, 
							NULL);
	if ( crthread == NULL ) return false;
	::WaitForSingleObject(crthread, INFINITE);
		
	// start the remote thread
	/*
	hRemoteThread = shared_ptr<void>(
						::CreateRemoteThread(
							m_hConsoleProcess.get(), 
							NULL, 
							0, 
							pfnThreadRoutine, 
							remoteaddr,
							//hookDllRemote,
							//(PVOID)pszHookDllPathRemote.get(), 
							0, 
							NULL),
						::CloseHandle);

	if (hRemoteThread.get() == NULL) return false;
	*/

	// wait for the thread to finish
	//::WaitForSingleObject(hRemoteThread.get(), INFINITE);

#pragma warning(pop)

	return true;
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

DWORD WINAPI ConsoleHandler::MonitorThreadStatic(LPVOID lpParameter)
{
	ConsoleHandler* pConsoleHandler = reinterpret_cast<ConsoleHandler*>(lpParameter);
	return pConsoleHandler->MonitorThread();
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

DWORD ConsoleHandler::MonitorThread()
{
	HANDLE arrWaitHandles[] = { 
			m_hConsoleProcess.get(), 
			m_hMonitorThreadExit.get(),
		   	m_consoleBuffer.GetEvent() };

	while (::WaitForMultipleObjects(sizeof(arrWaitHandles)/sizeof(arrWaitHandles[0]), arrWaitHandles, FALSE, INFINITE) > WAIT_OBJECT_0 + 1)
	{
		updated = 1;
		//SharedMemoryLock	memLock(m_consoleBuffer);
		//while (updated){
		//	::Sleep(1000);
		//}
		//updated = 1;
			/*
		while(wor_term->full
			|| (pre_term && wor_term->_n == pre_term) ){
			::Sleep(1000);
		}
		*/
		SharedMemoryLock	memLock(m_consoleBuffer);
		DWORD				dwColumns	= m_consoleInfo->srWindow.Right - m_consoleInfo->srWindow.Left + 1;
		DWORD				dwRows		= m_consoleInfo->srWindow.Bottom - m_consoleInfo->srWindow.Top + 1;
		bool				bResize		= false;
		DWORD top = m_consoleInfo->srWindow.Top;
		if ((m_consoleParams->dwColumns != dwColumns) ||
			(m_consoleParams->dwRows != dwRows))
		{
			m_consoleParams->dwColumns	= dwColumns;
			m_consoleParams->dwRows		= dwRows;
			bResize = true;
		}

		//extern void enjCMDUpdateTerm(void *frontend,PCHAR_INFO chars,PCONSOLE_SCREEN_BUFFER_INFO conInfo);
//		enjCMDUpdateTerm(front,m_consoleBuffer.Get(),m_consoleInfo.Get());
		
//		m_consoleChangeDelegate(bResize);

		/*
		::CopyMemory(&(wor_term->screenInfo), 
						m_consoleInfo.Get(), sizeof(CONSOLE_SCREEN_BUFFER_INFO));
		::CopyMemory(&(wor_term->conParams),
						m_consoleParams.Get(),sizeof(ConsoleParams));
		::CopyMemory(&(wor_term->conBuffer),
						m_consoleBuffer.Get(),sizeof(CHAR_INFO)*dwColumns*dwRows);
		//COORD cPos = m_consoleInfo->dwCursorPosition;
		wor_term = wor_term->_n;
		wor_term->_p->full = 1;
*/
		//pre_term = wor_term->_p;
		//pre_term->full = 1;
		//wcstombs(output,tempbuf,dwRows*dwColumns);
	}

	ConsoleClose();
//	TRACE(L"exiting thread\n");
	// exiting thread
//	m_consoleCloseDelegate();
	return 0;
}

//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////

wstring ConsoleHandler::GetModulePath(HMODULE hModule)
{
	wchar_t szModulePath[MAX_PATH+1];
	::ZeroMemory(szModulePath, (MAX_PATH+1)*sizeof(wchar_t));
	char modulepath[MAX_PATH];
	::GetModuleFileName(hModule, modulepath, MAX_PATH);
	mbstowcs(szModulePath,modulepath,MAX_PATH);
	wstring strPath(szModulePath);
	//wstring strPath(modulepath);
	return strPath.substr(0, strPath.rfind(L'\\'));
}

void ConsoleHandler::ConsoleChange(bool reSize)
{
		/*
	if ( reSize ) {
	extern void SendResizeEvent(void *_w,int _c,int _r);
	SendResizeEvent(evt_owner,m_consoleParams->dwColumns,m_consoleParams->dwRows);
	}
	*/
	return;
}

void ConsoleHandler::ConsoleClose()
{
	extern void DisconnectFrontend(void *frontend);	
	DisconnectFrontend(front);
	return;
}

void ConsoleHandler::SendTextToConsole(const char* ptext)
{
	wchar_t pszText[1024];
	mbstowcs(pszText,ptext,1024);
	if (!pszText || (wcslen(pszText) == 0)) return;

	void* pRemoteMemory = ::VirtualAllocEx(
								GetConsoleHandle().get(),
								NULL, 
								(wcslen(pszText)+1)*sizeof(wchar_t), 
								MEM_COMMIT, 
								PAGE_READWRITE);

	if (pRemoteMemory == NULL) return;

	if (!::WriteProcessMemory(
				GetConsoleHandle().get(),
				pRemoteMemory, 
				(PVOID)pszText, 
				(wcslen(pszText)+1)*sizeof(wchar_t), 
				NULL))
	{
		::VirtualFreeEx(GetConsoleHandle().get(), pRemoteMemory, NULL, MEM_RELEASE);
		return;
	}

	GetConsolePasteInfo() = reinterpret_cast<UINT_PTR>(pRemoteMemory);
	GetConsolePasteInfo().SetEvent();
}

void ConsoleHandler::ConsoleFwdMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//	TRACE(L"Bonk\n");
	::PostMessage(GetConsoleParams()->hwndConsoleWindow, uMsg, wParam, lParam);
	return;
}
void ConsoleHandler::AdjustScrollBarPos(int nDelta)
{
	//SharedMemory<SIZE>& newScrollPos = m_consoleHandler.GetNewScrollPos();
	SharedMemoryLock memLock(m_newScrollPos);

	m_newScrollPos->cx = 0;
	m_newScrollPos->cy = nDelta;

	m_newScrollPos.SetEvent();
	
}

int ConsoleHandler::GetConsoleOutput(char *buf,int len)
{
	//buf[0]='\0';
	//return 0;
	//if ( !updated ) return 0;
	HANDLE arrWaitHandles[] = { 
			m_hConsoleProcess.get(), 
			m_hMonitorThreadExit.get(),
		   	m_consoleBuffer.GetEvent() };
	DWORD	dwWaitRes		= 0;
	dwWaitRes = ::WaitForMultipleObjects(sizeof(arrWaitHandles)/sizeof(arrWaitHandles[0]), arrWaitHandles, FALSE, 0);// > WAIT_OBJECT_0 + 1)
   	if ( dwWaitRes > WAIT_OBJECT_0 + 2 ) {
		return 0;
	}
	if ( dwWaitRes == WAIT_OBJECT_0  ) {
		ConsoleClose();
		return 0;
	}else if ( dwWaitRes == WAIT_OBJECT_0 + 1 ) {
		return 0;
	}else if ( dwWaitRes == WAIT_OBJECT_0 + 2 ) {

	}
		   	/*
		else if ( dwWaitRes == WAIT_OBJECT_0 +3 ) {
	}
	*/
	
	SharedMemoryLock	memLock(m_consoleBuffer);
		//wor_term->screen_info
	/*
	DWORD				dwColumns	= m_consoleInfo->srWindow.Right - m_consoleInfo->srWindow.Left + 1;
	DWORD				dwRows		= m_consoleInfo->srWindow.Bottom - m_consoleInfo->srWindow.Top + 1;
	bool				bResize		= false;
	DWORD top = m_consoleInfo->srWindow.Top;
	if ((m_consoleParams->dwColumns != dwColumns) ||
		(m_consoleParams->dwRows != dwRows))
	{
		m_consoleParams->dwColumns	= dwColumns;
		m_consoleParams->dwRows		= dwRows;
		bResize = true;
	}
	*/
	extern void enjCMDUpdateTerm(void *frontend,PCHAR_INFO chars,PCONSOLE_SCREEN_BUFFER_INFO conInfo);
	enjCMDUpdateTerm(front,m_consoleBuffer.Get(),m_consoleInfo.Get());
	updated = 0;
	return 0;
//	enjCMDUpdateTerm();
#if 0
	if ( !cur_term->full ) return 0;
	//string buffer;
	DWORD				dwColumns	= m_consoleParams->dwColumns;
	DWORD				dwRows		= m_consoleParams->dwRows;
	
	buffer = '\0';
	//buffer ="\x1b[1;1H";
	if ( pre_term ) {
		int pre_cursor_y = pre_term->screenInfo.dwCursorPosition.X;
		int pre_cursor_x = pre_term->screenInfo.dwCursorPosition.Y;
		int pre_top = pre_term->screenInfo.srWindow.Top;

		int cur_cursor_y = cur_term->screenInfo.dwCursorPosition.X;
		int cur_cursor_x = cur_term->screenInfo.dwCursorPosition.Y;
		int cur_top = cur_term->screenInfo.srWindow.Top;
		int scroll_num = cur_top - pre_top;
		if ( cur_top != 0 //&& scroll_num < dwRows && scroll_num > -1 * dwRows
					) {
			char tempbuf[1024];
			if ( scroll_num > 0 ) {
				for ( int i=0;i<scroll_num;i++){
					buffer +='\n';
				}
				//sprintf(tempbuf,"\x1b[%dM",scroll_num);
				//buffer +=tempbuf;
			}else{
					/*
				for ( int i=0;i<-1*scroll_num;i++){
					buffer +='\n';
				}
				*/
				//sprintf(tempbuf,"\x1b[%dL",scroll_num);
				//buffer +=tempbuf;
			}
		}
		buffer +="\x1b[1;1H";
		/*
		COORD pre_pos;
		COORD cur_pos;
		pre_pos.X = pre_pos.Y = 0;
		*/
					/*
				if ((::memcmp(m_consoleInfo.Get(), &csbiConsole, sizeof(CONSOLE_SCREEN_BUFFER_INFO)) != 0) ||
					(m_dwScreenBufferSize != dwScreenBufferSize) ||
		(::memcmp(m_consoleBuffer.Get(), pScreenBuffer.get(), m_dwScreenBufferSize*sizeof(CHAR_INFO)) != 0))

		*/
		int isjump = 0;
    	for (DWORD i = 0; i < dwRows; ++i) {
			for (DWORD j = 0; j < dwColumns; ++j) {
				if ( memcmp(&(pre_term->conBuffer[i*dwColumns+j]),
							&(cur_term->conBuffer[i*dwColumns+j]),sizeof(CHAR_INFO)) != 0 )
				{
					if (!isjump) {
						char tempbuf[1024];
						sprintf(tempbuf,"\x1b[%d;%dH",i+1,j+1);
						buffer+=tempbuf;
						isjump = 1;
						//pre_pos = cur_pos;
					}
					buffer += cur_term->conBuffer[i*dwColumns+j].Char.AsciiChar;
				}else{
					isjump = 0;
					//pre_pos = cur_pos ;
				}
		   	}
	   	}
	}else
	{
		//buffer = '\0';
    	for (DWORD i = 0; i < dwRows; ++i) {
			for (DWORD j = 0; j < dwColumns; ++j) {
				buffer += m_consoleBuffer[i*dwColumns+j].Char.AsciiChar;
		   	}
	   	}
	}
	//calce curpos;
	int cursor_y = cur_term->screenInfo.dwCursorPosition.X - 
			cur_term->screenInfo.srWindow.Left;
	int cursor_x = cur_term->screenInfo.dwCursorPosition.Y - 
			cur_term->screenInfo.srWindow.Top;
	char tempbuf[1024];
	sprintf(tempbuf,"\x1b[%d;%dH",cursor_x+1,cursor_y+1);
	buffer+=tempbuf;
	memcpy(buf,buffer.c_str(),buffer.length());
	pre_term = cur_term;
	cur_term = cur_term->_n;
	cur_term->_p->full = 0;
	return buffer.length();
#endif 
	//memcpy(buf,buffer.c_str(),buffer.length());
	//return buffer.length();
#if 0	
	buffer = '\0';
	HANDLE arrWaitHandles[] = { m_hConsoleProcess.get(), m_hMonitorThreadExit.get(), m_consoleBuffer.GetEvent() };
	if (::WaitForMultipleObjects(sizeof(arrWaitHandles)/sizeof(arrWaitHandles[0]), arrWaitHandles, FALSE, 0) > WAIT_OBJECT_0 + 1)
	{
		SharedMemoryLock	memLock(m_consoleBuffer);

		DWORD				dwColumns	= m_consoleInfo->srWindow.Right - m_consoleInfo->srWindow.Left + 1;
		DWORD				dwRows		= m_consoleInfo->srWindow.Bottom - m_consoleInfo->srWindow.Top + 1;
		bool				bResize		= false;
		DWORD top = m_consoleInfo->srWindow.Top;
		if ((m_consoleParams->dwColumns != dwColumns) ||
			(m_consoleParams->dwRows != dwRows))
		{
			m_consoleParams->dwColumns	= dwColumns;
			m_consoleParams->dwRows		= dwRows;
			bResize = true;
		}

		m_consoleChangeDelegate(bResize);
		COORD cPos = m_consoleInfo->dwCursorPosition;
		//wchar_t *tempbuf= (wchar_t*)malloc(dwRows*dwColumns*sizeof(wchar_t));
		//char *output=(char *)malloc(dwRows*dwColumns*sizeof(char));
		if ( pre_pos.X != m_consoleInfo->dwCursorPosition.X || pre_pos.Y != m_consoleInfo->dwCursorPosition.Y ){
			buffer +="\x1b[1;1H";
		    for (DWORD i = 0; i < dwRows; ++i)
			{
				for (DWORD j = 0; j < dwColumns; ++j)
				{
					//tempbuf[i*dwColumns+j] = m_consoleBuffer[i*dwColumns+j].Char.UnicodeChar;
					 //output[i*dwColumns+j] = m_consoleBuffer[i*dwColumns+j].Char.AsciiChar;
					 buffer += m_consoleBuffer[i*dwColumns+j].Char.AsciiChar;
					//wchar_t aw = m_consoleBuffer[i*dwColumns+j].Char.UnicodeChar;
					//char ac = m_consoleBuffer[i*dwColumns+j].Char.AsciiChar;
				}
				//buffer +='\n';
				if ( i== m_consoleInfo->dwCursorPosition.Y 
								&& j == m_consoleInfo->dwCursorPosition.X )
						break;
			}
			pre_pos = m_consoleInfo->dwCursorPosition;
			//calce curpos;
			int cursor_y = m_consoleInfo->dwCursorPosition.X - m_consoleInfo->srWindow.Left;
			int cursor_x = m_consoleInfo->dwCursorPosition.Y - m_consoleInfo->srWindow.Top;
			char tempbuf[1024];
			sprintf(tempbuf,"\x1b[%d;%dH",cursor_x+1,cursor_y+1);
			buffer+=tempbuf;
			memcpy(buf,buffer.c_str(),buffer.length());
			return buffer.length();
		}else{
			buf[0]='\0';
			return 0;
		}
		//wcstombs(output,tempbuf,dwRows*dwColumns);
	}
#endif
}

void ConsoleHandler::AdjustSize(int _c,int _r)
{
	//DWORD dwColumns	= _c;
	//DWORD dwRows	= _r;
	{
	SharedMemoryLock memLock(GetNewConsoleSize());

	GetNewConsoleSize()->dwColumns	= _c;
	GetNewConsoleSize()->dwRows	= _r;
	}
	//if ( GetNewConsoleSize()->dwBufferSize < 500)	
	//	GetNewConsoleSize()->dwBufferSize = 500;
	//}
	/*
	{
	SharedMemoryLock memLock(m_consoleParams);
	m_consoleParams->dwRows = dwRows;
	m_consoleParams->dwColumns = dwColumns;
	m_consoleParams->dwBufferRows			= 200;//g_settingsHandler->GetConsoleSettings().dwBufferRows;
	m_consoleParams->dwBufferColumns		= dwColumns;//g_settingsHandler->GetConsoleSettings().dwBufferColumns;
	}
	*/


	GetNewConsoleSize().SetEvent();
	
}

void ConsoleHandler::AdjustBufferSize(int lines)
{
	{
		SharedMemoryLock memLock(GetNewConsoleSize());

	//GetNewConsoleSize()->dwColumns	= _c;
	//GetNewConsoleSize()->dwRows	= _r;
		GetNewConsoleSize()->dwBufferSize	= lines;
	}
	GetNewConsoleSize().SetEvent();
}

void ConsoleHandler::ShowConsole(int show)
{
	::ShowWindow(m_consoleParams->hwndConsoleWindow, show ? SW_SHOW : SW_HIDE);
}
//////////////////////////////////////////////////////////////////////////////
 
//bool ConsoleHandler::StartShellProcess(const wstring& strCustomShell, const wstring& strInitialDir, const wstring& strConsoleTitle, DWORD dwStartupRows, DWORD dwStartupColumns)
bool StartShellProcess(void *handle,wchar_t *scshell,wchar_t *initdir,wchar_t *title,int rows,int cols)
{
	//wstring shell(scshell);
	// set current language in the console window
	ConsoleHandler * con = (ConsoleHandler *)handle;

	//con->SetupDelegates( fastdelegate::MakeDelegate(con, &ConsoleHandler::ConsoleChange), fastdelegate::MakeDelegate(con, &ConsoleHandler::ConsoleClose));

	con->StartShellProcess(scshell, initdir,title,rows,cols);

	::PostMessage(
		con->GetConsoleParams()->hwndConsoleWindow,
		WM_INPUTLANGCHANGEREQUEST, 
		0, 
		reinterpret_cast<LPARAM>(::GetKeyboardLayout(0)));

	// scrollbar stuff
//	InitializeScrollbars();

	// create offscreen buffers
//	CreateOffscreenBuffers();

	// TODO: put this in console size change handler
//	m_screenBuffer.reset(new CHAR_INFO[m_consoleHandler.GetConsoleParams()->dwRows*m_consoleHandler.GetConsoleParams()->dwColumns]);
//	::ZeroMemory(m_screenBuffer.get(), sizeof(CHAR_INFO)*m_consoleHandler.GetConsoleParams()->dwRows*m_consoleHandler.GetConsoleParams()->dwColumns);

	//con->StartMonitorThread();
	
	return true;
}



void* NewConsoleHandle(void *evt)
{
	return new ConsoleHandler(evt);
}

int ReadCMDConsoleOutput(void *handle,char *buf,int len)
{
	return ((ConsoleHandler *)handle)->GetConsoleOutput(buf,len);
}

void AdjustConsoleSize(void *handle,int _c,int _r)
{
	((ConsoleHandler *)handle)->AdjustSize(_c,_r);
}

void AdjustBufferSize(void *handle,int _l)
{
	((ConsoleHandler *)handle)->AdjustBufferSize(_l);
}

void ForwardMsgConsole(void *handle,UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	((ConsoleHandler *)handle)->ConsoleFwdMsg(uMsg,wParam,lParam);
}

void CloseConsoleHandle(void *handle)
{
	((ConsoleHandler *)handle)->CloseHandle();
}

void SetConsoleScrollBarPos(void *handle,int nDelta)
{
	((ConsoleHandler *)handle)->AdjustScrollBarPos(nDelta);
}

void ShowConsoleWindow(void *handle,int show)
{
	((ConsoleHandler *)handle)->ShowConsole(show);
}

void SendTextToConsole(void *handle,char *text)
{
	((ConsoleHandler *)handle)->SendTextToConsole(text);
}
