#include <windows.h>
#include "../../../include/com_server.h"

DEFINE_GUID(CLSID_LED, 0x681299e2, 0x8bb4, 0x11d3, 0xad, 0xaa, 0x00,0x60,0x67,0x49,0x62,0x45);

class Window {
public:
	Window(LPCTSTR lpClassName, int x, int y, int nWidth, int nHeight, HWND hWndParent, HINSTANCE _hInstance,
		ImpComponent* pcomp, BoolConnection& pin);
	LRESULT WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam);
	HWND GetHandle();
private:
	void Paint();
	void pinChanged();
	void CmToggle();
	void CmConnect();
	HWND hWnd;
	HINSTANCE hInstance;
	HMENU hMenu;
	CallOnChange<BoolConnection::BaseType, Window> coc;
	ImpComponent* pComp;
};

Window::Window(LPCTSTR lpClassName, int x, int y, int nWidth, int nHeight, HWND hWndParent, HINSTANCE _hInstance,
	ImpComponent* pcomp, BoolConnection& pin):
		hWnd(CreateWindow(lpClassName, "", WS_CHILD|WS_VISIBLE, x, y, nWidth, nHeight, hWndParent, NULL, _hInstance,
		/* Pass 'this' pointer in lpParam of CreateWindow() */ reinterpret_cast<LPSTR>(this))),
		hInstance(_hInstance),
		coc(pin, this, &Window::pinChanged),
		pComp(pcomp),
		hMenu(CreatePopupMenu()) {
	AppendMenu(hMenu, MF_STRING, 10, "&Connect...");
	AppendMenu(hMenu, MF_STRING, 20, "&Toggle Led");
	pinChanged();
}

HWND Window::GetHandle() {
	return hWnd;
}

void Window::Paint() {
	PAINTSTRUCT ps;
	BeginPaint(hWnd, &ps);

	HDC hdc(CreateCompatibleDC(ps.hdc));
	HBITMAP hBitmap(LoadBitmap(hInstance, coc->get() ? "ledOn" : "ledOff"));
	SelectObject(hdc, hBitmap);

	RECT rect;
	GetClientRect(hWnd, &rect);

	StretchBlt(ps.hdc, 0, 0, rect.right, rect.bottom, hdc, 0 , 0, 104, 104, SRCCOPY);

	DeleteObject(hBitmap);
	DeleteDC(hdc);
	EndPaint(hWnd, &ps);
}

LRESULT Window::WndProc(UINT iMessage, WPARAM wParam, LPARAM lParam) {
	switch (iMessage) {
		case WM_CREATE:
			break;
		case WM_PAINT:
			Paint();
			break;
		case WM_SIZE:
			InvalidateRect(hWnd, NULL, TRUE);
			break;
		case WM_CONTEXTMENU:
			TrackPopupMenu(hMenu, TPM_LEFTALIGN|TPM_RIGHTBUTTON, LOWORD(lParam),	HIWORD(lParam), 0, hWnd, NULL);
			break;
		case WM_COMMAND:
			switch (LOWORD(wParam)) {
				case 10:
					CmConnect();
					break;
				case 20:
					CmToggle();
					break;
			}
			break;
		default:
			return DefWindowProc(hWnd, iMessage, wParam, lParam);
	}
	return 0;
}

LRESULT CALLBACK _export WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
	// Pointer to the (C++ object that is the) window.
	Window *pWindow=reinterpret_cast<Window*>(GetWindowLong(hWnd, 0));
	// The pointer pWindow will have an invalid value if the WM_CREATE
	// message has not yet been processed (we respond to the WM_CREATE
	// message by setting the extra bytes to be a pointer to the
	// (C++) object corresponding to the Window identified by hWnd). The
	// messages that precede WM_CREATE must be processed without using
	// pWindow so we pass them to DefWindowProc.
	// How do we know in general if the pointer pWindow is invalid?
	// Simple: Windows allocates the window extra bytes using LocalAlloc
	// which zero initializes memory; thus, pWindow will have a value of
	// zero before we set the window extra bytes to the 'this' pointer.
	// Caveat emptor: the fact that LocalAlloc will zero initialize the
	// window extra bytes is not documented; therefore, it could change
	// in the future.

	if (pWindow==0) {
		if (iMessage==WM_CREATE) {
			LPCREATESTRUCT lpcs(reinterpret_cast<LPCREATESTRUCT>(lParam));
			pWindow=reinterpret_cast<Window*>(lpcs->lpCreateParams);
			// Store a pointer to this object in the window's extra bytes;
			// this will enable us to access this object (and its member
			// functions) in WndProc where we are given only a handle to
			// identify the window.
			SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(pWindow));
			// Now let the object perform whatever initialization it needs
			// for WM_CREATE in its own WndProc.
			return pWindow->WndProc(iMessage, wParam, lParam);
		}
		else
			return DefWindowProc(hWnd, iMessage, wParam, lParam);
	}
	else
		return pWindow->WndProc(iMessage, wParam, lParam);
}

void Window::pinChanged() {
	pComp->SetCompWinIcon(LoadIcon(hInstance, coc->get() ? "iconLedOn" : "iconLedOff"));
	pComp->SetCompWinTitle(coc->get() ? "Led is ON" : "Led is OFF");
	InvalidateRect(hWnd, NULL, FALSE);
}

void Window::CmToggle() {
	coc->set(!coc->get());
}

void Window::CmConnect() {
	pComp->ConnectDialog();
}

class LED: public ImpComponent {
public:
	LED();
	~LED();
private:
	HWND CreateComponentWindow(HWND parent);
	Window* LEDWindow;
	BoolConnection	pin;
};

LED::LED(): LEDWindow(0) {
	SetComponentName("Led API");
	SetGroupName("CDK Borland C++ 5.02 Examples");
	Expose(pin, "Led input");
}

LED::~LED() {
	delete LEDWindow;
}

HWND LED::CreateComponentWindow(HWND parent) {
	if (LEDWindow==0) {
		HINSTANCE hInstance(GetModuleHandle("led.dll"));
		WNDCLASS wndclass;	// Structure used to register Windows class.
		if (GetClassInfo(hInstance, "THRSim11_LED", &wndclass)==0) {
			wndclass.style=CS_HREDRAW|CS_VREDRAW;
			wndclass.lpfnWndProc=::WndProc;
			wndclass.cbClsExtra=0;
			// Reserve extra bytes for each instance of the window;
			// we will use these bytes to store a pointer to the C++
			// (Window) object corresponding to the window.
			wndclass.cbWndExtra=4;
			wndclass.hInstance=hInstance;
			wndclass.hIcon=LoadIcon(hInstance, "iconLedOff");
			wndclass.hCursor=LoadCursor(NULL, IDC_ARROW);
			wndclass.hbrBackground=(HBRUSH)GetStockObject(LTGRAY_BRUSH);
			wndclass.lpszMenuName=NULL;
			wndclass.lpszClassName="THRSim11_LED";
			RegisterClass(&wndclass);
		}
		LEDWindow = new Window("THRSim11_LED", 0, 0, 104, 104, parent, hInstance, this, pin);
	}
	return LEDWindow->GetHandle();
}

void RegisterClassFactories() {
	ClassFactory* new_classfactory=new ComponentFactory<LED>(CLSID_LED);
	class_factories.push_back(new_classfactory);
}
