#include <winsock2.h>
#include <windows.h>
#include <tchar.h>

#pragma comment(lib,"ws2_32.lib")

#define IDC_EDIT_IN		    101
#define IDC_EDIT_OUT		102
#define IDC_BUTTON_SEND		103
#define IDC_BUTTON_RUN      104
#define IDC_EDIT_PORT		105
#define IDC_STATIC		    106

HWND hEditIn = NULL;
HWND hEditOut = NULL;

#define MAX_CONNECTIONS 10
SOCKET ConnectionSockets[MAX_CONNECTIONS];
SOCKET ListenSocket = INVALID_SOCKET;

int nConnections = 0;

#define ROZMIAR_BUFORA  1024

LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

void DodajKomunikat(HWND hEdit, const TCHAR *tekst)
{
	TCHAR tmp[ROZMIAR_BUFORA];
	int index = GetWindowTextLength(hEdit);
	_tcsncpy_s(tmp, tekst, ROZMIAR_BUFORA);
	_tcscat_s(tmp, ROZMIAR_BUFORA, TEXT("\r\n"));
	SetFocus(hEdit);
	SendMessage(hEdit, EM_SETSEL, (WPARAM)index, (LPARAM)index);
	SendMessage(hEdit, EM_REPLACESEL, 0, (LPARAM)tmp);
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
	WNDCLASSEX wClass;
	ZeroMemory(&wClass, sizeof(WNDCLASSEX));
	wClass.cbSize = sizeof(WNDCLASSEX);
	wClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
	wClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wClass.hInstance = hInst;
	wClass.lpfnWndProc = (WNDPROC)WinProc;
	wClass.lpszClassName = L"Window Class";
	wClass.style = CS_HREDRAW | CS_VREDRAW;

	if (!RegisterClassEx(&wClass))
	{
		MessageBox(NULL, TEXT("Window class creation failed"), TEXT("Window Class Failed"), MB_ICONERROR);
		return 1;
	}

	HWND hWnd = CreateWindowEx(NULL,
		TEXT("Window Class"),
		TEXT("Serwer"),
		WS_OVERLAPPEDWINDOW,
		200,
		200,
		335,
		480,
		NULL,
		NULL,
		hInst,
		NULL);

	if (!hWnd)
		MessageBox(NULL, TEXT("Window creation failed"), TEXT("Window Creation Failed"), MB_ICONERROR);

	ShowWindow(hWnd, nShowCmd);

	MSG msg;
	ZeroMemory(&msg, sizeof(MSG));

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}

DWORD ConnesctionThread(LPVOID dane)
{
	SOCKET *s = (SOCKET*)dane;
	TCHAR komunikat[ROZMIAR_BUFORA];

	DodajKomunikat(hEditIn, TEXT("Watek polaczenia dziala"));

	while (1)
	{
		ZeroMemory(komunikat, sizeof(komunikat));

		int dlugosc = recv(*s, (char*)komunikat, sizeof(komunikat) / sizeof(komunikat[0]), 0);
		if (dlugosc > 0)
		{
			DodajKomunikat(hEditIn, komunikat);
			for (int i = 0; i < MAX_CONNECTIONS; i++)
			{
				if (ConnectionSockets[i] != INVALID_SOCKET && ConnectionSockets[i] != *s)
					send(ConnectionSockets[i], (char*)komunikat, sizeof(TCHAR)*_tcslen(komunikat) + 1, 0);
			}
		}
		else break;
	}

	DodajKomunikat(hEditIn, TEXT("Połaczenie zerwane"));
	closesocket(*s);

	nConnections--;

	*s = INVALID_SOCKET;
	return 0;
}


DWORD MainServerThread(LPVOID dane)
{
	SOCKET *s = (SOCKET*)dane;

	while (1)
	{
		DodajKomunikat(hEditIn, TEXT("Czekam na nowe połaczenie"));
		if (nConnections < MAX_CONNECTIONS)
		{
			int i;
			for (i = 0; i < MAX_CONNECTIONS && ConnectionSockets[i] != INVALID_SOCKET; i++)
				;

			ConnectionSockets[i] = accept(*s, NULL, NULL);
			if (ConnectionSockets[i] != INVALID_SOCKET)
			{
				DWORD dwThreadId;
				CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ConnesctionThread, (LPVOID)(ConnectionSockets + i), 0, &dwThreadId);
				nConnections++;
			}
		}
	}

	return 0;

}

LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	static bool serwerDziala = false;

	switch (msg)
	{
	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_BUTTON_RUN:
		{
								
							   if (serwerDziala == false)
							   {

								   // Proba uruchomienia watka serwera
								   int nPort = 0;
								   TCHAR portstr[100];
								   if (!GetDlgItemText(hWnd, IDC_EDIT_PORT, portstr, 100)) portstr[0] = TCHAR('\0');
								   else nPort = _ttoi(portstr);

								   TCHAR komunikat[ROZMIAR_BUFORA];
								   _stprintf_s(komunikat, ROZMIAR_BUFORA, TEXT("Próba połącznia na porcie %d"), nPort);
								   DodajKomunikat(hEditIn, komunikat);

								   ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
								   if (ListenSocket == INVALID_SOCKET) {
									   MessageBox(hWnd, TEXT("socket error"), TEXT("Error"), MB_OK);
									   break;
								   }

								   sockaddr_in service;
								   service.sin_family = AF_INET;
								   service.sin_addr.s_addr = htonl(INADDR_ANY);
								   service.sin_port = htons(nPort);

								   int iResult = bind(ListenSocket, (SOCKADDR *)& service, sizeof (service));
								   if (iResult == SOCKET_ERROR) {
									   MessageBox(hWnd, TEXT("Bind error"), TEXT("Error"), MB_OK);
									   closesocket(ListenSocket);
									   break;
								   }

								   if (listen(ListenSocket, MAX_CONNECTIONS) == SOCKET_ERROR)
								   {
									   MessageBox(hWnd, TEXT("Listen error"), TEXT("Error"), MB_OK);
									   closesocket(ListenSocket);
									   break;
								   }

								   DWORD dwThreadID;

								   CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainServerThread, &ListenSocket, 0, &dwThreadID);

								   EnableWindow(hEditOut, true);
								   EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_SEND), true);
								   SetWindowText(GetDlgItem(hWnd, IDC_BUTTON_RUN), TEXT("Stop"));
								   serwerDziala = true;
							   }
							   else
							   {
								  
								   closesocket(ListenSocket);
								   ListenSocket = INVALID_SOCKET;
								   serwerDziala = false;

								   SetWindowText(GetDlgItem(hWnd, IDC_BUTTON_RUN), TEXT("Start"));
								   EnableWindow(hEditOut, false);

								   EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_SEND), false);
								   DodajKomunikat(hEditIn, TEXT("Serwer wylaczony"));
							   }

							   break;
		}
		case IDC_BUTTON_SEND:
		{
								TCHAR komunikat[ROZMIAR_BUFORA];
								GetWindowText(hEditOut, komunikat, ROZMIAR_BUFORA);

								// Wyslanie komunikatu do wszystkich klientow
								for (int i = 0; i < MAX_CONNECTIONS; i++)
								{
									if (ConnectionSockets[i] != INVALID_SOCKET)
										send(ConnectionSockets[i], (char*)komunikat, sizeof(TCHAR)*_tcslen(komunikat) + 1, 0);
								}
								SetWindowText(hEditOut, TEXT(""));
								break;

		}

			break;
		}
		break;
	case WM_CREATE:
	{
					  CreateWindow(TEXT("STATIC"), TEXT("Port: "),
						  WS_CHILD | WS_VISIBLE,
						  10, 10, 30, 23, hWnd, (HMENU)IDC_STATIC, GetModuleHandle(NULL), NULL);

					  CreateWindow(TEXT("EDIT"), TEXT("1234"),
						  WS_CHILD | WS_VISIBLE | WS_BORDER,
						  40, 10, 50, 23, hWnd, (HMENU)IDC_EDIT_PORT, GetModuleHandle(NULL), NULL);

					  HWND hWndButton2 = CreateWindow(TEXT("BUTTON"), TEXT("Run"),
						  WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
						  110, 10, 175, 23,
						  hWnd,
						  (HMENU)IDC_BUTTON_RUN,
						  GetModuleHandle(NULL),
						  NULL);

					 
					  hEditIn = CreateWindowEx(WS_EX_CLIENTEDGE,
						  TEXT("EDIT"), TEXT("Serwer nie jest uruchomiony.\r\n"),
						  WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
						  10, 50, 300, 300,
						  hWnd,
						  (HMENU)IDC_EDIT_IN,
						  GetModuleHandle(NULL),
						  NULL);

					  if (!hEditIn)  MessageBox(hWnd, TEXT("Could not create incoming edit box."), TEXT("Error"), MB_OK | MB_ICONERROR);

					  hEditOut = CreateWindowEx(WS_EX_CLIENTEDGE,
						  TEXT("EDIT"), TEXT("Tutaj wpisz komunikat..."), WS_CHILD | WS_VISIBLE | ES_MULTILINE |
						  ES_AUTOVSCROLL | ES_AUTOHSCROLL,
						  10, 360, 220, 60, hWnd,
						  (HMENU)IDC_EDIT_OUT,
						  GetModuleHandle(NULL),
						  NULL);

					  if (!hEditOut) MessageBox(hWnd, TEXT("Could not create outgoing edit box."), TEXT("Error"), MB_OK | MB_ICONERROR);

					  HWND hWndButton = CreateWindow(
						  TEXT("BUTTON"),
						  TEXT("Send"),
						  WS_TABSTOP | WS_VISIBLE |
						  WS_CHILD | BS_DEFPUSHBUTTON,
						  240, 360, 60, 60,
						  hWnd,
						  (HMENU)IDC_BUTTON_SEND,
						  GetModuleHandle(NULL),
						  NULL);

					  EnableWindow(hEditOut, false);
					  EnableWindow(GetDlgItem(hWnd, IDC_BUTTON_SEND), false);

					  WSADATA WsaDat;
					  int nResult = WSAStartup(MAKEWORD(2, 2), &WsaDat);
					  if (nResult != 0)
					  {
						  MessageBox(hWnd, TEXT("Winsock initialization failed"), TEXT("Critical Error"), MB_ICONERROR);
						  SendMessage(hWnd, WM_DESTROY, NULL, NULL);
						  break;
					  }

					  HGDIOBJ hfDefault = GetStockObject(DEFAULT_GUI_FONT);
					  SendMessage(GetDlgItem(hWnd, IDC_EDIT_PORT), WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
					  SendMessage(GetDlgItem(hWnd, IDC_STATIC), WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
					  SendMessage(hEditOut, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
					  SendMessage(hEditIn, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
					  SendMessage(hWndButton, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
					  SendMessage(hWndButton2, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));

					  for (int i = 0; i < MAX_CONNECTIONS; i++) ConnectionSockets[i] = INVALID_SOCKET;
	
					  serwerDziala = false;
	}
		break;

	case WM_DESTROY:
	{
					   closesocket(ListenSocket);
					   ListenSocket = INVALID_SOCKET;
	
					   PostQuitMessage(0);
					   WSACleanup();
					   return 0;
	}
		break;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}