
#include "StdAfx.h"
#include "textbox.h"
#include "richedit.h"

//
//  FUNCTION: TextBoxProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the skinnable window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK TextBox::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	TextBox * wnd = (TextBox*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
	if(!wnd) return 0;

	switch (message) 
	{
	case WM_CHAR:
		if(wnd->OnChar(wParam,lParam))
			return 0;
		break;
	case WM_KEYDOWN:
		wnd->OnKeyDown(wParam,lParam);
		break;
	case WM_KILLFOCUS:
		wnd->OnLoseFocus();
		break;
	case WM_COMMAND:
		switch(wParam){
			case EN_UPDATE:
				if(wnd->hWndParent)
					SafeSendMessage(wnd->hWndParent,ID_CONTENTSCHANGED,(WPARAM)hWnd,0);
				break;
		}
	case WM_NOTIFY:
		switch(wParam){
			case EN_DROPFILES:

				break;
		}
		break;
	default:
		break;
	}
	return wnd->DefEditCtrlWndProc(hWnd, message, wParam, lParam);
}

TextBox::TextBox(void)
{
	type = TextBoxType;
	ncrhWnd = NULL;

	/// default text
	title = _T("");

	padding = 1; // num of pixels around the edit control
	multiline = 0;
	pOle = NULL;
}

TextBox::~TextBox(void)
{
	
}

void TextBox::OnDestroy()
{
	if(pOle) pOle->Release();
}

void TextBox::OnPaint(HWND hWnd, HDC hdc)
{
	if(hEditCtrl)
		SafeSendMessage(hEditCtrl,EM_SETBKGNDCOLOR,0,(LPARAM)RGB(current->bgcolor.GetR(),current->bgcolor.GetG(),current->bgcolor.GetB()));
	if(current->borderwidth>0){
		Graphics graphics(hdc);

		// Create a Rect object.
		Rect fillRect(Point(0,0),size);

		// Create a Pen object.
		Pen borderpen(current->bordercolor, current->borderwidth);

		// Draw the rectangle that bounds the image.
		fillRect.Width--;
		fillRect.Height--;
		graphics.DrawRectangle(&borderpen, fillRect);
	} else { // we need to draw the border in the background color of the text box
		Graphics graphics(hdc);

		// Create a Rect object.
		Rect fillRect(Point(0,0),size);

		// Create a Pen object.
		Pen borderpen(current->bgcolor, current->borderwidth);

		// Draw the rectangle that bounds the image.
		fillRect.Width--;
		fillRect.Height--;
		graphics.DrawRectangle(&borderpen, fillRect);

	}

}

int TextBox::Create(HWND parent, DWORD style )
{
	hWndParent = parent;
	
	ClipChildren = 0;
	SkinnableWindow::Create(parent);

	multiline = style&ES_MULTILINE;

	if(!multiline)
		style = style | ES_AUTOHSCROLL;

	hEditCtrl = CreateWindowEx(0,RICHEDIT_CLASS, title.data(), WS_CHILD|style,
		padding, padding, size.Width-2*padding, size.Height-2*padding, hWnd, NULL, env.hInstance, NULL);
	if (!hEditCtrl)
	{
		PrintLastError("TextBox.CreateWindowEx");
		return 0;
	}

	SendMessage(hEditCtrl,EM_GETOLEINTERFACE,0,(LPARAM)&pOle);
	DragAcceptFiles(hEditCtrl,TRUE);

	FitText();

	// save the ptr to this class in the window structure
	SetWindowLongPtr(hEditCtrl,GWLP_USERDATA,(LONG_PTR)this);

	// subclass this text box
	DefEditCtrlWndProc = (WNDPROC)SetWindowLongPtr(hEditCtrl,GWLP_WNDPROC,(LONG_PTR)TextBox::WndProc);
	// set the characterformat
	CHARFORMAT2 x;
	x.cbSize = sizeof(CHARFORMAT2);
	x.dwMask = CFM_FACE|CFM_SIZE|CFM_BACKCOLOR|CFM_COLOR|CFM_BOLD;
	x.dwEffects = 0;
	x.crTextColor = RGB(current->textcolor.GetRed(),current->textcolor.GetGreen(),current->textcolor.GetBlue());
	x.crBackColor = RGB(current->bgcolor.GetRed(),current->bgcolor.GetGreen(),current->bgcolor.GetBlue());
	_tcscpy(x.szFaceName,fontname.data());
	x.yHeight = 20*fontsize;
	if(SafeSendMessage(hEditCtrl,EM_SETCHARFORMAT,SCF_ALL,(LPARAM)&x)==0){
		print(L"TextBox: failed to set charactar formatting.\n");
		PrintLastError("TextBox");
	}

	// attach a scrollbar to the history text box
	if(multiline){
		scrollbar.Hide();
		scrollbar.SetLocation(size.Width-scrollbar.size.Width-1,0);
		scrollbar.SetSize(scrollbar.size.Width,size.Height);
		scrollbar.Create(hWnd);
		if(textheight>size.Height-2*padding)
			ShowScrollBar();
		else
			HideScrollBar();
	}

	ShowWindow(hEditCtrl, SW_SHOWNOACTIVATE);
	UpdateWindow(hEditCtrl);
	return 1;
}


void TextBox::Load(const char * b)
{
	_bstr_t base = b;
	SkinnableWindow::Load(b);
	scrollbar.Load(base+"::scrollbar");
}


void TextBox::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
	//print(L"WM_KEYDOWN: %x\n",wParam);
	switch (wParam) 
	{ 
	case VK_LEFT: 

		// Process the LEFT ARROW key. 

		break; 

	case VK_RIGHT: 

		// Process the RIGHT ARROW key. 

		break; 

	case VK_UP: 

		// Process the UP ARROW key. 

	case VK_DOWN: 

		// Process the DOWN ARROW key.  

	case VK_HOME: 

		// Process the HOME key. 

		break; 

	case VK_END: 

		// Process the END key. 

		break; 

	case VK_INSERT: 

		// Process the INS key. 

		break; 

	case VK_DELETE: 

		// Process the DEL key. 

		break; 

	case VK_F2: 
		// Process the F2 key. 
		if(env.console.hidden){
			env.console.Show();
		} else {
			env.console.Hide();
		}
		break; 


		// Process other non-character keystrokes. 

	default: 
		break; 
	}
}

int TextBox::OnChar(WPARAM wParam, LPARAM lParam)
{
	//print(L"WM_CHAR: %x\n",wParam);
		FitText(0);

	switch (wParam) 
	{ 
	case 17: // ctrl+q
		print(L"Quiting...");
		PostQuitMessage(0);
		break;
	case 0x08: 

		// Process a backspace. 

		break; 

	case 0x0A: 

		// Process a linefeed. 

		break; 

	case 0x1B: 

		// Process an escape. 

		break; 

	case 0x09: 

		// Process a tab. 
		if(hTabTo) SetFocus(hTabTo);
		return 1; 

	case 0x0D: 

		// Process a carriage return. 
		if(ncrhWnd)
			SafeSendMessage(ncrhWnd,ncrMsg,ncrwParam,ncrlParam);
		break; 

	default: 

		// Process displayable characters. 

		break; 
	} 
	return 0;
}

void TextBox::OnLButtonDown()
{

}

void TextBox::OnLButtonUp()
{

}

void TextBox::OnMouseMove()
{

}

void TextBox::SetMsgOnCarriageReturn(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	ncrhWnd = hWnd;
	ncrMsg = message;
	ncrwParam = wParam;
	ncrlParam = lParam;
}

VOID CALLBACK DeleteStringBufferCallback(
  HWND hwnd,        // handle to destination window
  UINT uMsg,        // message
  ULONG_PTR dwData, // application-defined value
  LRESULT lResult   // result of message processing
  )
{
	TCHAR * buffer = (TCHAR *)dwData;
	if(buffer)
		delete [] buffer;
}


void TextBox::AppendText(const TCHAR * buffer)
{
	// set selection to the end of the buffer
	
//	TCHAR * tmp = new TCHAR[_tcslen(buffer)+1];
//	_tcscpy(tmp,buffer);
	//SafeSendMessage(hEditCtrl,EM_SETSEL,-1,-1);
	//PostMessage(hEditCtrl,EM_REPLACESEL,FALSE,(LPARAM)buffer);
//	SendMessageCallback(hEditCtrl,EM_REPLACESEL,FALSE,(LPARAM)tmp,DeleteStringBufferCallback,(ULONG_PTR)tmp);
 SafeSendMessage( hEditCtrl, EM_SETSEL,-1,  -1);
 SafeSendMessage( hEditCtrl, EM_REPLACESEL,  FALSE, (LPARAM)buffer); 

	// resize the edit ctrl to exactly contain the text
	if(multiline){
		FitText();
	}
}

void TextBox::SetText(const TCHAR * buffer)
{ 
	SafeSendMessage(hEditCtrl,WM_SETTEXT,0,(LPARAM)buffer);
	// resize the edit ctrl to exactly contain the text
	if(multiline){
		FitText();
	}
}

const TCHAR * TextBox::Data()
{
	TCHAR * tmp;
	int len = SafeSendMessage(hEditCtrl,WM_GETTEXTLENGTH,0,0);
	tmp = new TCHAR[len+1];
	SafeSendMessage(hEditCtrl,WM_GETTEXT,(WPARAM)(len+1),(LPARAM)tmp);
	tmp[len] = '\0';
	buffer = tmp;
	delete [] tmp;	
	return (const TCHAR *)buffer;
}

void TextBox::GetText(std::wstring& x)
{
	TCHAR * tmp;
	int len = SafeSendMessage(hEditCtrl,WM_GETTEXTLENGTH,0,0);
	tmp = new TCHAR[len+1];
	SafeSendMessage(hEditCtrl,WM_GETTEXT,(WPARAM)(len+1),(LPARAM)tmp);
	tmp[len] = '\0';
	x = tmp;
	delete [] tmp;
}

void TextBox::OnSize(int cx, int cy)
{
	SkinnableWindow::OnSize(cx,cy);

	// resize the edit box contained in this window
	FitText();
}	

void TextBox::GrabFocus()
{
	SetFocus(hEditCtrl);
}

void TextBox::ShowScrollBar()
{
	if(multiline){
		// resize the edit control to allow room for the scroll bar
		SetWindowPos(hEditCtrl,NULL,0,0,size.Width-padding-scrollbar.size.Width-1, textheight,SWP_NOZORDER|SWP_NOMOVE|SWP_ASYNCWINDOWPOS|SWP_NOACTIVATE);
		scrollbar.Show();
	}
}

void TextBox::HideScrollBar()
{
	if(multiline){
		scrollbar.Hide();
		SetWindowPos(hEditCtrl,NULL,0,0,size.Width-2*padding, textheight,SWP_NOZORDER|SWP_NOMOVE|SWP_ASYNCWINDOWPOS|SWP_NOACTIVATE);
	}
}

void TextBox::OnScroll(HWND h)
{
	if(scrollbar.hWnd==h && multiline){
		if(textheight>size.Height-2*padding) {
			SetWindowPos(hEditCtrl,NULL,padding,padding-scrollbar.GetPos()*(textheight-size.Height+2*padding),0,0,SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
		}
	}
}

void TextBox::FitText(int scrolltoend)
{
	if(multiline){
		int oldtextheight = textheight;
		//DoEvents();
		
		// resize the edit ctrl so it exactly fits the text at the current width
		GETTEXTLENGTHEX g;
		g.codepage = 1200; // unicode
		g.flags = GTL_DEFAULT;
		int nchars = SafeSendMessage(hEditCtrl,EM_GETTEXTLENGTHEX,(WPARAM)&g,0);
		if(nchars==0) 
			textheight = 0;
		else {
			DWORD pt = SafeSendMessage(hEditCtrl,EM_POSFROMCHAR,(WPARAM)nchars-1,0);
			textheight = HIWORD(pt);
		}

		float lh = MeasureTextHeight(fontname.data(),fontsize,L"1\n1",size.Width-2*padding)/2;
		textheight += lh;
		
		if(textheight<size.Height-2*padding) textheight = size.Height-2*padding;
		scrollbar.SetPage((size.Height-2*padding)/(float)textheight);
		scrollbar.SetLine(lh/(float)(textheight));

		if(1){
			if(textheight>size.Height-2*padding) {
				scrollbar.SetLocation(size.Width-scrollbar.size.Width-1,0);
				scrollbar.SetSize(scrollbar.size.Width,size.Height);
				scrollbar.Show();
				if(scrolltoend)
					scrollbar.SetPos(1.0);
				else
					scrollbar.SetPos(scrollbar.GetPos());
			} else {
				SetWindowPos(hEditCtrl,NULL,padding,padding,0,0,SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
				scrollbar.Hide();
			}
		}
	
		if(scrollbar.hidden) 
			SetWindowPos(hEditCtrl,NULL,0,0,size.Width-2*padding,textheight,SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
		else 
			SetWindowPos(hEditCtrl,NULL,0,0,size.Width-padding-scrollbar.size.Width-1, textheight,SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);

	} else
		textheight = size.Height-2*padding;
}

void TextBox::InsertBitmap(Bitmap * bmp)
{
	HBITMAP h;
	bmp->GetHBITMAP(current->bgcolor,&h);
	EmbeddableImage::InsertBitmap(pOle,h);
}

void TextBox::SetForegroundColor(Color c)
{
	// set the characterformat
	CHARFORMAT2 x;
	x.cbSize = sizeof(CHARFORMAT2);
	x.dwMask = CFM_COLOR;
	x.crTextColor = RGB(c.GetRed(),c.GetGreen(),c.GetBlue());
	if(SafeSendMessage(hEditCtrl,EM_SETCHARFORMAT,SCF_SELECTION,(LPARAM)&x)==0){
		print(L"TextBox: failed to set foreground color.\n");
		PrintLastError("TextBox");
	}
}

void TextBox::SetBackgroundColor(Color c)
{
	// set the characterformat
	CHARFORMAT2 x;
	x.cbSize = sizeof(CHARFORMAT2);
	x.dwMask = CFM_BACKCOLOR;
	x.crBackColor = RGB(c.GetRed(),c.GetGreen(),c.GetBlue());
	if(SafeSendMessage(hEditCtrl,EM_SETCHARFORMAT,SCF_SELECTION,(LPARAM)&x)==0){
		print(L"TextBox: failed to set background color.\n");
		PrintLastError("TextBox");
	}
}

void TextBox::Select(int start, int end)
{
	CHARRANGE x;
	x.cpMin = start;
	x.cpMax = end;
	if(SafeSendMessage(hEditCtrl, EM_EXSETSEL ,0,(LPARAM)&x)==0){
		print(L"TextBox: failed to set selection.\n");
		PrintLastError("TextBox");
	}
}

void TextBox::SelectAll()
{
	CHARRANGE x;
	x.cpMin = 0;
	x.cpMax = -1;
	if(SafeSendMessage(hEditCtrl, EM_EXSETSEL ,0,(LPARAM)&x)==0){
		print(L"TextBox: failed to select all.\n");
		PrintLastError("TextBox");
	}
}