#pragma once
#include <chrono>
#include <Eigen/Dense>
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>


using std::min;
using std::max;

constexpr auto NET_SECRET = "880b3713b9ff3e7a94b2712d54679e1f";
#define ENABLE_NETWORK_CHECKSUM

constexpr float ANGULAR_ACCEL = 0.005f * 1000.0f;
constexpr float SHIP_ACCEL = 1.0f * 200.0f;
constexpr float ROTATION_SENSITIVITY = 0.002f;

constexpr float PLANET_RADIUS = 20000.f;
constexpr float PLANET_ALIGN_ZONE = 1.05f;
constexpr float PLANET_ANGULAR_ACCEL = 0.01f;
constexpr float PLANET_MAX_ANGULAR_VELOCITY = 10.f;
constexpr float PITCH_LIMIT = static_cast<float>(M_PI) / 9.f;//18.0f;

constexpr long long SERVER_DELAY = 0; //ms
constexpr long long CLIENT_DELAY = 500; //ms
constexpr long long CUTOFF_TIME = 5000; //ms
constexpr long long PLAYER_TIMEOUT_MS = 10000; //ms — disconnect if no UPD received

constexpr float PROJECTILE_VELOCITY = 600.f;
constexpr float PROJECTILE_LIFE = 15000.f; //ms

const float projectileHitRadius = 1.5f * 4;
const float boxCollisionRadius = 2.0f * 4;
const float shipCollisionRadius = 15.0f * 3;
const float BOX_PICKUP_RADIUS = shipCollisionRadius * 3;
const float npcCollisionRadius = 5.0f * 3;

uint32_t fnv1a_hash(const std::string& data);

struct ClientState {
    int id = 0;
    Eigen::Vector3f position = { 0, 0, 45000.0f };
    Eigen::Matrix3f rotation = Eigen::Matrix3f::Identity();
    Eigen::Vector3f currentAngularVelocity = Eigen::Vector3f::Zero();
    float velocity = 0.0f;
    int selectedVelocity = 0;
    float discreteMag = 0;
    int discreteAngle = -1;

    std::string nickname = "Player";
    int shipType = 0;

    std::chrono::system_clock::time_point lastUpdateServerTime;

    void simulate_physics(size_t delta);

    void apply_lag_compensation(std::chrono::system_clock::time_point nowTime);

    void handle_full_sync(const std::vector<std::string>& parts, int startFrom);

    std::string formPingMessageContent() const;
};

struct ClientStateInterval
{
	std::vector<ClientState> timedStates;

    void add_state(const ClientState& state);

    bool canFetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const;

    ClientState fetchClientStateAtTime(std::chrono::system_clock::time_point targetTime) const;
};

