#define GL_GLEXT_PROTOTYPES
#define _USE_MATH_DEFINES

#include <string.h>
#include <unistd.h>
#if defined(_WIN32)
    #include <winsock2.h>
    #if defined(__GNUC__)
        #include <cpuid.h>
    #elif defined(_MSC_VER)
        #include <intrin.h>
    #endif
    //#define APIENTRY __stdcall
    #include <windows.h>
    #include <errno.h>
    #define GLEW_STATIC
    #include <GL/glew.h>
    //#include "glew/glew.h"
    //#include <GL/gl.h>
    //#include <GLES2/gl2.h>
    //#include <GLES2/gl2ext.h>
    //#define GLFW_INCLUDE_ES3
	#define GLFW_EXPOSE_NATIVE_WGL
    #define GLFW_EXPOSE_NATIVE_WIN32
#else
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <GL/gl.h>
    //#include <GL/glext.h>
	#define GLFW_EXPOSE_NATIVE_X11
	#define GLFW_EXPOSE_NATIVE_GLX
#endif
#include <sys/time.h>

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>

#include "linmathv2.h"

#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
#define degToRad(angleInDegrees) ((angleInDegrees) * M_PI / 180.0)
#define radToDeg(angleInRadians) ((angleInRadians) * 180.0 / M_PI)

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower)))


#include "custom.h"
#include "player.h"

#include "cjson/cJSON.h"
#include "input.h"

#define PORT 12345
#define MAX_PLAYERS 2
#define UPDATE_INTERVAL 0.050  // 50ms
#define SPEED 100.0            // Pixels per second
#define SMOOTHING_SPEED 10.0   // Smoothing factor for correction


typedef struct {
    uint32_t sequence;
    double server_time;
    uint8_t num_players;
    struct {
        uint8_t id;
        float pos_x, pos_y, pos_z;
        float vel_x, vel_y, vel_z;
    } players[MAX_PLAYERS];
} Snapshot;

// Get current time in seconds
double get_time() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + tv.tv_usec / 1e6;
}


// Server code
//--void init_server() {
//--    int sockfd;
//--    struct sockaddr_in server_addr, client_addr;
//--    socklen_t addr_len = sizeof(client_addr);
//--    NetworkPlayer players[MAX_PLAYERS] = {0};
//--    InputMessage input;
//--    Snapshot snapshot = {0};
//--    double last_update_time = get_time();
//--    uint32_t sequence = 0;
//--
//--    // Initialize players
//--    for (int i = 0; i < MAX_PLAYERS; i++) {
//--        players[i].id = i;
//--        players[i].position.x = 100.0 * (i + 1);
//--        players[i].position.y = 100.0;
//--    }
//--
//--    // Create UDP socket
//--    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//--    if (sockfd < 0) {
//--        perror("Socket creation failed");
//--        exit(1);
//--    }
//--
//--    // Bind socket
//--    memset(&server_addr, 0, sizeof(server_addr));
//--    server_addr.sin_family = AF_INET;
//--    server_addr.sin_addr.s_addr = INADDR_ANY;
//--    server_addr.sin_port = htons(PORT);
//--    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
//--        perror("Bind failed");
//--        close(sockfd);
//--        exit(1);
//--    }
//--
//--    printf("Server running on port %d\n", PORT);
//--}
//--void update_server() {
//--    //while (1) {
//--    double current_time = get_time();
//--
//--    // Receive inputs non-blocking
//--    while (1) {
//--        ssize_t n = recvfrom(sockfd, &input, sizeof(input), MSG_DONTWAIT,
//--                             (struct sockaddr*)&client_addr, &addr_len);
//--        if (n < 0) {
//--            if (errno == EWOULDBLOCK || errno == EAGAIN) break;
//--            perror("Recvfrom failed");
//--            continue;
//--        }
//--        if (player_id < MAX_PLAYERS) {
//--            // Update velocity based on input
//--            players[player_id].velocity.x = input_x * SPEED;
//--            players[player_id].velocity.y = input_y * SPEED;
//--        }
//--    }
//--
//--    // Fixed update
//--    if (current_time - last_update_time >= UPDATE_INTERVAL) {
//--        double dt = UPDATE_INTERVAL;
//--
//--        // Update game state
//--        for (int i = 0; i < MAX_PLAYERS; i++) {
//--            players[i].position.x += players[i].velocity.x * dt;
//--            players[i].position.y += players[i].velocity.y * dt;
//--        }
//--
//--        // Prepare snapshot
//--        snapshot.sequence = sequence++;
//--        snapshot.server_time = current_time;
//--        snapshot.num_players = MAX_PLAYERS;
//--        for (int i = 0; i < MAX_PLAYERS; i++) {
//--            snapshot.players[i].id = players[i].id;
//--            snapshot.players[i].pos_x = players[i].position.x;
//--            snapshot.players[i].pos_y = players[i].position.y;
//--            snapshot.players[i].vel_x = players[i].velocity.x;
//--            snapshot.players[i].vel_y = players[i].velocity.y;
//--        }
//--
//--        // Send snapshot to all clients (broadcast to known clients)
//--        // Here, we assume clients are at known addresses; in practice, maintain a client list
//--        for (int i = 0; i < MAX_PLAYERS; i++) {
//--            sendto(sockfd, &snapshot, sizeof(snapshot), 0,
//--                   (struct sockaddr*)&client_addr, addr_len);
//--        }
//--
//--        last_update_time += UPDATE_INTERVAL;
//--    }
//--
//--    //usleep(1000); // Prevent CPU hogging
//--    //}
//--
//--    //close(sockfd);
//--}
//--
//--
//--
//--
//--// client
//--
//--// Client code
//--void init_client(int local_player_id) {
//--    int sockfd;
//--    struct sockaddr_in server_addr;
//--    socklen_t addr_len = sizeof(server_addr);
//--    NetworkPlayer players[MAX_PLAYERS] = {0};
//--    Snapshot snapshot;
//--    InputMessage input = {0};
//--    uint32_t last_sequence = 0;
//--    double last_frame_time = get_time();
//--
//--    // Initialize players
//--    for (int i = 0; i < MAX_PLAYERS; i++) {
//--        players[i].id = i;
//--    }
//--    player_id = local_player_id;
//--
//--    // Create UDP socket
//--    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//--    if (sockfd < 0) {
//--        perror("Socket creation failed");
//--        exit(1);
//--    }
//--
//--    // Server address
//--    memset(&server_addr, 0, sizeof(server_addr));
//--    server_addr.sin_family = AF_INET;
//--    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Localhost
//--    server_addr.sin_port = htons(PORT);
//--
//--    printf("Client %d running, connecting to server\n", local_player_id);
//--}
//--
//--void update_client() {
//--    //while (1) {
//--    double current_time = get_time();
//--    double frame_dt = current_time - last_frame_time;
//--
//--    // Simulate input (e.g., arrow keys; in practice, use real input)
//--    input_x = (local_player_id == 0) ? 1.0 : -1.0; // Player 0 moves right, Player 1 left
//--    input_y = 0.0;
//--    sendto(sockfd, &input, sizeof(input), 0,
//--           (struct sockaddr*)&server_addr, sizeof(server_addr));
//--
//--    // Receive snapshots
//--    while (1) {
//--        ssize_t n = recvfrom(sockfd, &snapshot, sizeof(snapshot), MSG_DONTWAIT,
//--                             (struct sockaddr*)&server_addr, &addr_len);
//--        if (n < 0) {
//--            if (errno == EWOULDBLOCK || errno == EAGAIN) break;
//--            perror("Recvfrom failed");
//--            continue;
//--        }
//--        if (snapshot.sequence > last_sequence) {
//--            last_sequence = snapshot.sequence;
//--            for (int i = 0; i < snapshot.num_players; i++) {
//--                int id = snapshot.players[i].id;
//--                players[id].position.x = snapshot.players[i].pos_x;
//--                players[id].position.y = snapshot.players[i].pos_y;
//--                players[id].velocity.x = snapshot.players[i].vel_x;
//--                players[id].velocity.y = snapshot.players[i].vel_y;
//--                players[id].snapshot_time = current_time;
//--                // Initialize rendered position if first update
//--                if (players[id].rendered_position.x == 0.0 && players[id].rendered_position.y == 0.0) {
//--                    players[id].rendered_position = players[id].position;
//--                }
//--            }
//--        }
//--    }
//--
//--    // Render loop
//--    for (int i = 0; i < MAX_PLAYERS; i++) {
//--        double dt = current_time - players[i].snapshot_time;
//--        // Predict position using last known velocity
//--        Vec3 target_position = {
//--            players[i].position.x + players[i].velocity.x * dt,
//--            players[i].position.y + players[i].velocity.y * dt
//--            players[i].position.z + players[i].velocity.z * dt
//--        };
//--
//--        // Smoothly correct rendered position toward target (rollback)
//--        float correction_x = (target_position.x - players[i].rendered_position.x) * SMOOTHING_SPEED * frame_dt;
//--        float correction_y = (target_position.y - players[i].rendered_position.y) * SMOOTHING_SPEED * frame_dt;
//--        float correction_z = (target_position.z - players[i].rendered_position.z) * SMOOTHING_SPEED * frame_dt;
//--        players[i].rendered_position.x += correction_x;
//--        players[i].rendered_position.y += correction_y;
//--        players[i].rendered_position.z += correction_z;
//--
//--        // Simulate rendering (print position)
//--        printf("Client %d: Player %d at (%f, %f)\n", local_player_id, i,
//--               players[i].rendered_position.x, players[i].rendered_position.y, players[i].rendered_position.z);
//--    }
//--
//--    last_frame_time = current_time;
//--    //usleep(16666); // ~60 FPS
//--    //}
//--
//--    //close(sockfd);
//--}

