#include "X11/Xlib.h"
#include "cstdio"
#include "stdexcept"

#define keyESC      9
#define keyUP     111
#define keySPACE   65
#define keyW       25
#define keyX       53
#define keyZ       52

struct Rect{
    long x, y, width, height;
};

class XWindowDisplay{
    public:
    XWindowDisplay();
    ~XWindowDisplay();
    Display *classDisplay();
    void drawScreen();
    void drawOnScreen(int XX, int YY, int width, int height);
    void gameUpdate();
    Rect getGeo();
    

    private:
    Display *disPlay;
    int defScreen;
    Window window;

};

XWindowDisplay::XWindowDisplay(){

    disPlay = XOpenDisplay(NULL);
    if(disPlay == NULL){
        throw std::runtime_error("Display is NULL\n");
    }

    defScreen = DefaultScreen(disPlay);
    window = XCreateSimpleWindow(disPlay, RootWindow(disPlay, defScreen), 0, 0, 800, 800, 1, BlackPixel(disPlay, defScreen), 0x000000ff); //WhitePixel(disPlay, defScreen));
    XSelectInput(disPlay, window, KeyPressMask | ExposureMask);
    XMapWindow(disPlay, window);

}

XWindowDisplay::~XWindowDisplay(){
    XCloseDisplay(disPlay);
}

Display *XWindowDisplay::classDisplay(){
    return disPlay;
}

void XWindowDisplay::drawScreen(){
    unsigned long col = 0x6091ab;
    XSetForeground(disPlay, DefaultGC(disPlay, defScreen), col);
}

void XWindowDisplay::drawOnScreen(int XX, int YY, int width, int height){
    XFillRectangle(disPlay, window, DefaultGC(disPlay, defScreen), XX, YY, width, height);
    
}

void XWindowDisplay::gameUpdate(){
    XClearWindow(disPlay, window);

    Window primeWindow;
    int x, y;
    unsigned int width, height, win_Boundry, depth;
    XGetGeometry(disPlay, window, &primeWindow, &x, &y, &width, &height, &win_Boundry, &depth);

    XEvent event_Update;
    event_Update.xexpose.type = Expose;
    event_Update.xexpose.display = disPlay;
    event_Update.xexpose.window = window;
    event_Update.xexpose.x = x;
    event_Update.xexpose.y = y;
    event_Update.xexpose.width = width;
    event_Update.xexpose.height = height;
    event_Update.xexpose.count = 0;

    XSendEvent(disPlay, window, false, ExposureMask, &event_Update);
}

Rect XWindowDisplay::getGeo(){
    Window primeWindow;
    int x, y;
    unsigned int width, height, win_Boundry, depth;
    XGetGeometry(disPlay, window, &primeWindow, &x, &y, &width, &height, &win_Boundry, &depth);

    Rect rect;
    rect.x = x;
    rect.y = y;
    rect.width = width;
    rect.height = height;

    return rect;
}

class Game{
    public:
    Game();
    void Execute();

    private:
    XWindowDisplay gameWindow;
    XEvent eventX;
    bool Operateing = true;
    bool getEvent();
    void handleEvents();
    //bool pastWindow();
    float yPos = 350;
    int xPos;
    
};

Game::Game(){

}

void Game::Execute(){
    
    while (Operateing){

        if(getEvent()){
            gameWindow.gameUpdate();
            gameWindow.drawScreen();
            handleEvents();
        }
    }
    
}

bool Game::getEvent(){
    if(XPending(gameWindow.classDisplay())){
        XNextEvent(gameWindow.classDisplay(), &eventX);
        return true;
    }else{
        //printf("Pending is NULL\n");
        return false;
    }
}

void Game::handleEvents(){
    
    if(eventX.type == Expose){
        gameWindow.drawOnScreen(xPos, yPos, 20, 20);
        
        yPos = yPos + 0.005f;
       
    }

    if(eventX.type == KeyPress){
        switch (eventX.xkey.keycode){
            case keyUP      :printf("UP\n"); yPos = yPos - 1.5f;
                break;
            case keySPACE   :printf("SPACE\n"); 
                break;
            case keyW       :printf("W\n"); 
                break;
            case keyX       :printf("X\n"); 
                break;
            case keyZ       :printf("Z\n"); 
                break;
            case keyESC     :Operateing = false;
       }
       
    }
    
}


int main(){
    
    Game game;

    game.Execute();

    return 0;
}