#include <X11/extensions/XTest.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xresource.h>
#include <X11/Xatom.h>
#include <iostream>
#include <string>

Window *listWin (Display *disp, unsigned long *len);
char *nameWin (Display *disp, Window win);
void sendKey(Display* disp, char letter);

int main(int argc, char* argv[]){
	int i, counter, selectWindow, currentState;
	unsigned long len;
	char *name;
	XKeyEvent esend;
	Display *disp = XOpenDisplay(NULL);
	Window *list, currentWindow;
	std::string data;
	
	XGetInputFocus(disp, &currentWindow, &currentState); // Getting the current focused window
    	list = (Window*)listWin(disp,&len); // Getting all the windows
	
	for (i=0;i<(int)len;i++) {
		name = nameWin(disp,list[i]);
		std::cout << i << " : " << name << std::endl; // Display window's name
    	}
    	
    	std::cout << "Wich window do you want to use ?" << std::endl;
    	std::cin>>selectWindow;
	if(selectWindow<0 || selectWindow >i){
	    std::cout << "Invalid window" << std::endl;
	    return 1;
	}
	std::cout << "Data to send ?" << std::endl;
	std::cin.ignore(); 
	std::getline(std::cin,data);
	
	std::cout << "Number of sending : " << std::endl;
	std::cin >> counter;
	
	for(counter;counter > 0; counter--){
	  XSetInputFocus(disp, list[selectWindow],RevertToPointerRoot,0);
	  for(i=0;i<data.size();i++)
	    sendKey(disp, data[i]); // Send a char to the window
	  
	  sleep(1); // Sleep is needed, otherwise, the return caracter is not send.
	  sendKey(disp, '\n');
	}
	
	XSetInputFocus(disp, currentWindow, RevertToPointerRoot, 0); // Put the old window on focus
  	XFlush(disp);
	return 0;
}

void sendKey(Display* disp, char letter){ // Send a char to the selected window
    KeySym key;
    switch(letter){ // this switch is requiered to handle all the keyboards, you can add case: if you need
		case '1': key = XK_1; break;
		case '2': key = XK_2; break;
		case '3': key = XK_3; break;
		case '4': key = XK_4; break;
		case '5': key = XK_5; break;
		case '6': key = XK_6; break;
		case '7': key = XK_7; break;
		case '8': key = XK_8; break;
		case '9': key = XK_9; break;
		case '0': key = XK_0; break;

		case 'a': key = XK_a; break;
		case 'b': key = XK_b; break;
		case 'c': key = XK_c; break;
		case 'd': key = XK_d; break;
		case 'e': key = XK_e; break;
		case 'f': key = XK_f; break;
		case 'g': key = XK_g; break;
		case 'h': key = XK_h; break;
		case 'i': key = XK_i; break;
		case 'j': key = XK_j; break;
		case 'k': key = XK_k; break;
		case 'l': key = XK_l; break;
		case 'm': key = XK_m; break;
		case 'n': key = XK_n; break;
		case 'o': key = XK_o; break;
		case 'p': key = XK_p; break;
		case 'q': key = XK_q; break;
		case 'r': key = XK_r; break;
		case 's': key = XK_s; break;
		case 't': key = XK_t; break;
		case 'u': key = XK_u; break;
		case 'v': key = XK_v; break;
		case 'w': key = XK_w; break;
		case 'x': key = XK_x; break;
		case 'y': key = XK_y; break;
		case 'z': key = XK_z; break;


		case 'A': key = XK_a; break;
		case 'B': key = XK_b; break;
		case 'C': key = XK_c; break;
		case 'D': key = XK_d; break;
		case 'E': key = XK_e; break;
		case 'F': key = XK_f; break;
		case 'G': key = XK_g; break;
		case 'H': key = XK_h; break;
		case 'I': key = XK_i; break;
		case 'J': key = XK_j; break;
		case 'K': key = XK_k; break;
		case 'L': key = XK_l; break;
		case 'M': key = XK_m; break;
		case 'N': key = XK_n; break;
		case 'O': key = XK_o; break;
		case 'P': key = XK_p; break;
		case 'Q': key = XK_q; break;
		case 'R': key = XK_r; break;
		case 'S': key = XK_s; break;
		case 'T': key = XK_t; break;
		case 'U': key = XK_u; break;
		case 'V': key = XK_v; break;
		case 'W': key = XK_w; break;
		case 'X': key = XK_x; break;
		case 'Y': key = XK_y; break;
		case 'Z': key = XK_z; break;

		case 'é': key = XK_2; break;
		case 'è': key = XK_7; break;

		case '!': key = XK_exclam; break;
		case ' ': key = XK_space; break;
		case '\n': key = XK_Return; break;

		default : key = XK_space; break;
	}
     
    XTestGrabControl (disp, True);
    XTestFakeKeyEvent (disp, XKeysymToKeycode(disp, key), True, 0);  
    XTestFakeKeyEvent (disp, XKeysymToKeycode(disp, key), False, 0);
    XSync (disp, False);  
    XTestGrabControl (disp, False);  
}

Window *listWin (Display *disp, unsigned long *len) { // List all the application running on X
    Atom prop = XInternAtom(disp,"_NET_CLIENT_LIST",False), type;
    int form;
    unsigned long remain;
    unsigned char *list;
 
    if (XGetWindowProperty(disp,XDefaultRootWindow(disp),prop,0,1024,False,XA_WINDOW,
                &type,&form,len,&remain,&list) != Success) {
	return 0;
    }
     
    return (Window*)list;
}
char *nameWin (Display *disp, Window win) { // Return a window name
    Atom prop = XInternAtom(disp,"WM_NAME",False), type;
    int form;
    unsigned long remain, len;
    unsigned char *list;
 

    if (XGetWindowProperty(disp,win,prop,0,1024,False,AnyPropertyType,
                &type,&form,&len,&remain,&list) != Success) {
        
        return NULL;
    }
 
    return (char*)list;
}
