#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

void drawBoard(Display * disp, Window w, GC gc, GC gc1, GC gc2);
void updateBoard(int wndNumber, int x, int y, int &result);
void printMessage(Display * disp, Window w, GC gc1, GC gc2, int result);

const int M = 5;
const int N = 5;

int board[M][N];

int main(int argc, char ** argv) {
	for (int m=0; m<M; m++) for (int n=0; n<N; n++) board[m][n]=0;
	Display * disp1 = XOpenDisplay(NULL);
	Display * disp2 = NULL;
	if (argc==1) disp2 = XOpenDisplay(NULL); else disp2 = XOpenDisplay(argv[1]);
	if (disp2==NULL) exit(1);
	int blackColor1 = BlackPixel(disp1, DefaultScreen(disp1));
	int whiteColor1 = WhitePixel(disp1, DefaultScreen(disp1));
	int blackColor2 = BlackPixel(disp2, DefaultScreen(disp2));
        int whiteColor2 = WhitePixel(disp2, DefaultScreen(disp2));
	Window wnd1 = XCreateSimpleWindow(disp1, DefaultRootWindow(disp1), 0, 0, 300, 300, 0, whiteColor1, whiteColor1);
	Window wnd2 = XCreateSimpleWindow(disp2, DefaultRootWindow(disp2), 0, 0, 300, 300, 0, whiteColor2, whiteColor2);
	XSetStandardProperties(disp1, wnd1, "Xgame: Player 1", NULL, None, argv, argc, NULL);
        XSetStandardProperties(disp2, wnd2, "Xgame: Player 2", NULL, None, argv, argc, NULL);
	GC gc1 = XCreateGC(disp1, wnd1, 0, NULL);
	GC gc2 = XCreateGC(disp2, wnd2, 0, NULL);
	GC greenGC1 = XCreateGC(disp1, wnd1, 0, NULL);
	GC greenGC2 = XCreateGC(disp2, wnd2, 0, NULL);
	GC redGC1 = XCreateGC(disp1, wnd1, 0, NULL);
        GC redGC2 = XCreateGC(disp2, wnd2, 0, NULL);
	Colormap colormap1 = DefaultColormap(disp1, 0);
	Colormap colormap2 = DefaultColormap(disp2, 0);
	XColor green_col1;
	XColor green_col2;
	XColor red_col1;
	XColor red_col2;
	char green[] = "#00FF00";
	char red[] = "#FF0000";
	XParseColor(disp1, colormap1, green, &green_col1);
	XParseColor(disp2, colormap2, green, &green_col2);
	XParseColor(disp1, colormap1, red, &red_col1);
        XParseColor(disp2, colormap2, red, &red_col2);
	XAllocColor(disp1, colormap1, &green_col1);
	XAllocColor(disp2, colormap2, &green_col2);
	XAllocColor(disp1, colormap1, &red_col1);
        XAllocColor(disp2, colormap2, &red_col2);
	XSetForeground(disp1, gc1, blackColor1);
	XSetForeground(disp2, gc2, blackColor2);
	XSetForeground(disp1, greenGC1, green_col1.pixel);
	XSetForeground(disp2, greenGC2, green_col2.pixel);
	XSetForeground(disp1, redGC1, red_col1.pixel);
        XSetForeground(disp2, redGC2, red_col2.pixel);
        XMapWindow(disp1, wnd1);
	XMapWindow(disp2, wnd2);
	long evtMask = ExposureMask | PointerMotionMask | ButtonPressMask | StructureNotifyMask;
	XSelectInput(disp1, wnd1, evtMask);
	XSelectInput(disp2, wnd2, evtMask);
	Atom WM_DELETE_WINDOW1 = XInternAtom(disp1, "WM_DELETE_WINDOW", False);
	XSetWMProtocols(disp1, wnd1, &WM_DELETE_WINDOW1, 1);
	Atom WM_DELETE_WINDOW2 = XInternAtom(disp2, "WM_DELETE_WINDOW", False);
        XSetWMProtocols(disp2, wnd2, &WM_DELETE_WINDOW2, 1);
	bool done = false;
	int result = 0;
	int wndNumber = 0;
	while(!done) {
		XEvent e;
		if (XCheckWindowEvent(disp1, wnd1, evtMask, &e)) {
			wndNumber = 1;
		} else {
			if (XCheckWindowEvent(disp2, wnd2, evtMask, &e)) wndNumber = 2;
		}
		switch(e.type) {
		case ButtonPress:
			switch (e.xbutton.button) {
			int x, y;
			case Button1:
				if (result==0) {
					XWindowAttributes wndAttributes;
		                        if (wndNumber==1) XGetWindowAttributes(disp1, wnd1, &wndAttributes); else XGetWindowAttributes(disp2, wnd2, &wndAttributes);
					x = e.xbutton.x/(wndAttributes.width/N);
					y = e.xbutton.y/(wndAttributes.height/M);
					updateBoard(wndNumber, x, y, result);
					drawBoard(disp1, wnd1, gc1, greenGC1, redGC1);
					drawBoard(disp2, wnd2, gc2, greenGC2, redGC2);
					if (result!=0) {
						printMessage(disp1, wnd1, greenGC1, redGC1, result);
						printMessage(disp2, wnd2, greenGC2, redGC2, result);
					}
				}
				break;
			case Button2:
				done = true;
				break;
			}
			break;
		case ConfigureNotify:
			drawBoard(disp1, wnd1, gc1, greenGC1, redGC1);
			drawBoard(disp2, wnd2, gc2, greenGC2, redGC2);
			break;
		case Expose:
			if (e.xexpose.count==0) {
				if (wndNumber==1) {
					drawBoard(disp1, wnd1, gc1, greenGC1, redGC1);
					printMessage(disp1, wnd1, greenGC1, redGC1, result);
				}
				if (wndNumber==2) {
					drawBoard(disp2, wnd2, gc2, greenGC2, redGC2);
					printMessage(disp2, wnd2, greenGC2, redGC2, result);
				}
			}
		default:
			XCheckTypedWindowEvent(disp1, wnd1, ClientMessage, &e);
			if (e.xclient.data.l[0] == WM_DELETE_WINDOW1) done = true;
			XCheckTypedWindowEvent(disp2, wnd2, ClientMessage, &e);
                        if (e.xclient.data.l[0] == WM_DELETE_WINDOW2) done = true;
		}
	}
	XFreeGC(disp1, gc1);
	XFreeGC(disp2, gc2);
	XFreeGC(disp1, greenGC1);
        XFreeGC(disp2, greenGC2);
	XFreeGC(disp1, redGC1);
        XFreeGC(disp2, redGC2);
	XDestroyWindow(disp1, wnd1);
	XDestroyWindow(disp2, wnd2);
	XCloseDisplay(disp1);
	XCloseDisplay(disp2);
	return 0;
}

void drawBoard(Display * disp, Window wnd, GC gc, GC gc1, GC gc2) {
	XWindowAttributes wndAttributes;
	XGetWindowAttributes(disp, wnd, &wndAttributes);
	double blockWidth = (double)wndAttributes.width/N;
	double blockHeight = (double)wndAttributes.height/M;
	for (int m=0; m<M; m++) {
		for (int n=0; n<N; n++) {
			if (board[m][n]==1) {
				XFillRectangle(disp, wnd, gc1, (int)(blockWidth*n), (int)(blockHeight*m), (int)blockWidth+1, (int)blockHeight+1);
			}
			if (board[m][n]==2) {
				XFillRectangle(disp, wnd, gc2, (int)(blockWidth*n), (int)(blockHeight*m), (int)blockWidth+1, (int)blockHeight+1);
			}
		}
	}
	for (int i = 1; i<M; i++) {
		XDrawLine(disp, wnd, gc, 0, (int)(blockHeight*i), wndAttributes.width, (int)(blockHeight*i));
	}
	for (int i = 1; i<N; i++) {
        	XDrawLine(disp, wnd, gc, (int)(blockWidth*i), 0, (int)(blockWidth*i), wndAttributes.height);
	}
	XFlush(disp);
}

void updateBoard(int n, int x, int y, int &result) {
	if (board[y][x]==0 || board[y][x]==n) {
		board[y][x] = n;
		int a = 0;
		int b = 0;
		for (int m=0; m<M; m++) {
                	for (int n=0; n<N; n++) {
                        	if (board[m][n]==1) a++;
				if (board[m][n]==2) b++;
			}
		}
		if ((a+b)==(M*N)) {
			if (a>b) result = 1;
			if (a<b) result = 2;
			if (a==b) result = 3;
		}		
	} else {
		result = board[y][x];
	}
}

void printMessage(Display * disp, Window wnd, GC gc1, GC gc2, int result) {
	char msg1[] = "Player 1 won the game.";
	char msg2[] = "Player 2 won the game.";
	char msg3[] = "Game is draw.";
	if (result==1) XDrawImageString(disp, wnd, gc1, 100, 100, msg1, strlen(msg1));
	if (result==2) XDrawImageString(disp, wnd, gc2, 100, 100, msg2, strlen(msg2));
	if (result==3) XDrawImageString(disp, wnd, gc1, 100, 100, msg3, strlen(msg3));
}
