#pragma once

#include "buildsettings.h"

#define MAX_BOX_TYPES 64
#define ZOOM_MIN 0.04   // smaller means you can zoom out more
#define ZOOM_MAX 1500.0 // bigger means you can zoom in more
#define MAX_PLAYERS 16
#define MAX_SUNS 8
#define MAX_ENTITIES 1024 * 25
#define BOX_SIZE 0.25f
#define MERGE_MAX_DIST (BOX_SIZE / 2.0f + 0.01f)

#define MISSILE_RANGE 4.0f
#define MISSILE_BURN_TIME 1.5f
#define MISSILE_ARM_TIME 0.5f
#define MISSILE_BURN_FORCE 4.0f
#define MISSILE_MASS 1.0f
// how many missiles grown per second
#define MISSILE_DAMAGE_THRESHOLD 0.2f
#define MISSILE_CHARGE_RATE 0.5f
// centered on the sprite
#define MISSILE_SPRITE_SIZE ((cpVect){.x = BOX_SIZE, .y = BOX_SIZE})
#define MISSILE_COLLIDER_SIZE ((cpVect){.x = BOX_SIZE * 0.5f, .y = BOX_SIZE * 0.5f})
#define MISSILE_SPAWN_DIST (sqrt((BOX_SIZE / 2.0) * (BOX_SIZE / 2.0) * 2.0) + MISSILE_COLLIDER_SIZE.x / 2.0 + 0.1)
#define MISSILE_EXPLOSION_PUSH 2.5f
#define MISSILE_EXPLOSION_RADIUS 0.4f

#define PLAYER_SIZE ((cpVect){.x = BOX_SIZE, .y = BOX_SIZE})
#define PLAYER_MASS 0.5f
#ifdef FAT_THRUSTERS
#define PLAYER_JETPACK_FORCE 200.0f
#else
#define PLAYER_JETPACK_FORCE 3.5f
#endif
#define PLAYER_JETPACK_TORQUE 0.05f
#define PLAYER_JETPACK_ROTATION_ENERGY_PER_SECOND 0.2f
#define PLAYER_JETPACK_SPICE_PER_SECOND 0.08f
#define PLAYER_BIG_SCALING 300.0

#define ORB_MASS 4.0
#define ORB_RADIUS 1.0
#define ORB_HEAT_FORCE_MULTIPLIER 5.0
#define ORB_DRAG_CONSTANT 1.0
#define ORB_FROZEN_DRAG_CONSTANT 10.0
#define ORB_HEAT_MAX_DETECTION_DIST 100.0
#define ORB_HEAL_RATE 0.2
#define ORB_MAX_FORCE 200.0

#define VISION_RADIUS 20.0f
#define MAX_HAND_REACH 1.0f
#define GOLD_COLLECT_RADIUS 0.3f
#define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2
#define BOX_MASS 1.0
#define COLLISION_DAMAGE_SCALING 0.15

#define THRUSTER_FORCE 24.0
#define THRUSTER_ENERGY_USED_PER_SECOND 0.005
#define THRUSTER_DAMAGE_PER_SEC 2.0

#define LANDING_GEAR_MAX_DIST (BOX_SIZE * 0.25)

#define GYROSCOPE_ENERGY_USED_PER_SECOND 0.005f
#define GYROSCOPE_TORQUE 1.5f
#define GYROSCOPE_PROPORTIONAL_INERTIAL_RESPONSE 0.7 // between 0-1. How strongly responds to rotation, to stop the rotation

#define CLOAKING_ENERGY_USE 0.1f
#define CLOAKING_PANEL_SIZE BOX_SIZE * 3.0f

#define SCANNER_ENERGY_USE 0.05f
#define SCANNER_SCAN_RATE 0.5f
#define SCANNER_RADIUS 1.0f
#define SCANNER_MAX_RANGE 2000.0
#define SCANNER_MAX_VIEWPORT_RANGE 400.0
#define SCANNER_MIN_RANGE 1.0
#define SCANNER_MAX_POINTS 10
#define SCANNER_MAX_PLATONICS 3

#define MAX_SERVER_TO_CLIENT 1024 * 512 // maximum size of serialized gamestate buffer
#define MAX_CLIENT_TO_SERVER 1024 * 10  // maximum size of serialized inputs and mic data
#define GRAVITY_CONSTANT 0.01f
#define GRAVITY_SMALLEST 0.05f // used to determine when gravity is clamped to 0.0f
#define INSTANT_DEATH_DISTANCE_FROM_CENTER 10000.0f
#define SOLAR_ENERGY_PER_SECOND 0.09f
#define DAMAGE_TO_PLAYER_PER_BLOCK 0.1f
#define BATTERY_CAPACITY 4.5f
#define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.2
#define EXPLOSION_TIME 0.5f
#define EXPLOSION_DAMAGE_PER_SEC 10.0f
#define EXPLOSION_DAMAGE_THRESHOLD 0.2f // how much damage until it explodes
#define GOLD_UNLOCK_RADIUS 1.0f

#ifndef TIME_BETWEEN_WORLD_SAVE
#define TIME_BETWEEN_WORLD_SAVE 30.0f
#endif

#define BOMB_EXPLOSION_PUSH 5.0f
#define BOMB_EXPLOSION_RADIUS 1.0f

// VOIP
#define VOIP_PACKET_BUFFER_SIZE 15 // audio. Must be bigger than 2
#define VOIP_EXPECTED_FRAME_COUNT 240
#define VOIP_SAMPLE_RATE (48000 / 2)

// in seconds
#define VOIP_TIME_PER_PACKET (1.0f / ((float)((float)VOIP_SAMPLE_RATE / VOIP_EXPECTED_FRAME_COUNT)))
#define VOIP_PACKET_MAX_SIZE 4000
#define VOIP_DISTANCE_WHEN_CANT_HEAR (VISION_RADIUS * 0.8f)

// multiplayer
#define CAUTIOUS_MULTIPLIER 0.8 // how overboard to go with the time ahead predicting, makes it less likely that inputs are lost
#define TICKS_BEHIND_DO_SNAP 6  // when this many ticks behind, instead of dilating time SNAP to the healthy ticks ahead
#define MAX_MS_SPENT_REPREDICTING 30.0f
#define TIME_BETWEEN_SEND_GAMESTATE (1.0f / 20.0f)
#define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
#define TIMESTEP (1.0f / 60.0f)  // server required to simulate at this, defines what tick the game is on
#define LOCAL_INPUT_QUEUE_MAX 90 // please god let you not have more than 90 frames of game latency
#define INPUT_QUEUE_MAX 15

// fucks up serialization if you change this, fix it if you do that!
#define BOX_UNLOCKS_TYPE uint64_t

// cross platform threadlocal variables
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#define THREADLOCAL __declspec(thread)
#else
#define THREADLOCAL __thread
#endif

#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))

#include "cpVect.h"    // offers vector functions and types for the structs
#include "miniaudio.h" // @Robust BAD. using miniaudio mutex construct for server thread synchronization. AWFUL!

#define _USE_MATH_DEFINES
#include <math.h>   // sqrt and cos vector functions and PI
#include <stdint.h> // tick is unsigned integer
#include <stdio.h>  // logging on errors for functions

// defined in gamestate.c. Janky
#define flight_assert(condition) __flight_assert(condition, __FILE__, __LINE__, #condition)

// including headers from headers bad
#include <chipmunk.h> // unfortunate but needs cpSpace, cpBody, cpShape etc

#include "queue.h"
#include <stdbool.h>

#ifndef OPUS_TYPES_H
typedef int opus_int32;
#endif

#ifndef _STDBOOL

#define bool _Bool
#define false 0
#define true 1

#endif

extern FILE *log_file;
#include <time.h> // the time in logging!

void fill_time_string(char *to_fill, size_t max_length);

#define Log(...)                                                           \
  {                                                                        \
    char time_string[2048] = {0};                                          \
    fill_time_string(time_string, 2048);                                   \
    fprintf(stdout, "%s | %s:%d | ", time_string, __FILE__, __LINE__);     \
    fprintf(stdout, __VA_ARGS__);                                          \
    if (log_file != NULL)                                                  \
    {                                                                      \
      fprintf(log_file, "%s | %s:%d | ", time_string, __FILE__, __LINE__); \
      fprintf(log_file, __VA_ARGS__);                                      \
    }                                                                      \
  }

enum BoxType
{
  BoxInvalid, // zero initialized box is invalid!
  BoxHullpiece,
  BoxThruster,
  BoxBattery,
  BoxCockpit,
  BoxMedbay,
  BoxSolarPanel,
  BoxExplosive,
  BoxScanner,
  BoxGyroscope,
  BoxCloaking,
  BoxMissileLauncher,
  BoxMerge,
  BoxLandingGear,
  BoxLast,
};

enum CompassRotation
{
  Right,
  Down,
  Left,
  Up,
  RotationLast,
};

enum Squad
{
  SquadNone,
  SquadRed,
  SquadGreen,
  SquadBlue,
  SquadPurple,
  SquadLast,
};

// when generation is 0, invalid ID
typedef struct EntityID
{
  unsigned int generation; // VERY IMPORTANT if 0 then EntityID points to nothing, generation >= 1
  unsigned int index;      // index into the entity arena
} EntityID;

static inline bool entityids_same(EntityID a, EntityID b)
{

  return (a.generation == b.generation) && (a.index == b.index);
}

enum ScannerPointKind
{
  Platonic,
  Neutral,
  Enemy,
};

// when updated, must update serialization, comparison in main.c, and the server
// on input received processing function
typedef struct InputFrame
{
  uint64_t tick;

  bool been_processed; // not serialized, used by server just to keep track of what inputs have been processed

  cpVect movement;
  double rotation;

  int take_over_squad; // -1 means not taking over any squad
  bool accept_cur_squad_invite;
  bool reject_cur_squad_invite;
  EntityID invite_this_player; // null means inviting nobody! @Robust make it so just sends interact pos input, and server processes who to invite. This depends on client side prediction + proper input processing at the right tick.

  bool seat_action;
  cpVect hand_pos; // local to player transationally but not rotationally

  bool dobuild;
  enum BoxType build_type;
  enum CompassRotation build_rotation;
} InputFrame;

typedef struct PlatonicDetection
{
  // only these two are serialized
  cpVect direction;
  double intensity;
  
  enum BoxType of_type;
  bool used_in_scanner_closest_lightning_bolts;
} PlatonicDetection;

typedef struct Entity
{
  bool exists;
  bool flag_for_destruction;
  EntityID next_free_entity;
  unsigned int generation;
  bool always_visible; // always serialized to the player.

  bool no_save_to_disk; // stuff generated later on, like player's bodies or space stations that respawn.

  double damage;  // used by box, player, and orb
  cpBody *body;   // used by grid, player, and box
  cpShape *shape; // see notes on serializing the shape

  // players and boxes can be cloaked
  // If this is within 2 timesteps of the current game time, the entity is invisible.
  double time_was_last_cloaked;
  enum Squad last_cloaked_by_squad;

  // for serializing the shape
  // @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
  // what the shape's parent entity is
  bool is_circle_shape;
  EntityID shape_parent_entity; // can't be zero if shape is nonzero
  double shape_radius;          // only when circle shape
  cpVect shape_size;            // only when rect shape

  // player
  bool is_player;
  enum Squad owning_squad; // also controls what the player can see, because of cloaking!
  EntityID currently_inside_of_box;
  enum Squad squad_invited_to; // if squad none, then no squad invite

  // explosion
  bool is_explosion;
  cpVect explosion_pos;
  cpVect explosion_vel;
  double explosion_progress; // in seconds
  double explosion_push_strength;
  double explosion_radius;

  // sun
  bool is_sun;
  cpVect sun_vel;
  cpVect sun_pos;
  double sun_mass;
  double sun_radius;
  bool sun_is_safe;

  // missile
  bool is_missile;
  double time_burned_for; // until MISSILE_BURN_TIME. Before MISSILE_ARM_TIME cannot explode

  // orb
  bool is_orb;

  // grids
  bool is_grid;
  double total_energy_capacity;
  EntityID boxes;

  // boxes
  bool is_box;
  enum BoxType box_type;
  bool is_platonic;  // can't be destroyed, unaffected by physical forces
  EntityID next_box; // for the grid!
  EntityID prev_box; // doubly linked so can remove in middle of chain
  enum CompassRotation compass_rotation;
  bool indestructible;

  // used by multiple boxes that use power to see if it should show low power warning.
  // not serialized, populated during client side prediction. For the medbay
  // is only valid if the medbay has somebody inside of it
  double energy_effectiveness; // from 0 to 1, how effectively the box is operating given available power

  // merger
  bool wants_disconnect; // don't serialized, termporary value not used across frames

  // missile launcher
  double missile_construction_charge;

  // used by medbay and cockpit
  EntityID player_who_is_inside_of_me;

  // only serialized when box_type is thruster or gyroscope, used for both. Thrust
  // can mean rotation thrust!
  double wanted_thrust; // the thrust command applied to the thruster
  double thrust;        // the actual thrust it can provide based on energy sources in the grid

  // only gyroscope, velocity not serialized. Cosmetic
  double gyrospin_angle;
  double gyrospin_velocity;

  // only serialized when box_type is battery
  double energy_used; // battery, between 0 battery capacity. You have to look through code to figure out what that is! haha sucker!

  // only serialized when box_type is solar panel
  double sun_amount; // solar panel, between 0 and 1

  // cloaking only
  double cloaking_power; // 0.0 if unable to be used because no power, 1.0 if fully cloaking!

  // scanner only stuff!
  EntityID currently_scanning;
  double currently_scanning_progress; // when 1.0, scans it!
  BOX_UNLOCKS_TYPE blueprints_learned;
  double scanner_head_rotate_speed; // not serialized, cosmetic
  double scanner_head_rotate;

  // landing gear only
  cpConstraint *landed_constraint; // when null is landed

  PlatonicDetection detected_platonics[SCANNER_MAX_PLATONICS]; // intensity of 0.0 means undetected

  struct ScannerPoint
  {
    char kind; // is of ScannerPointKind
    char x;
    char y;
  } scanner_points[SCANNER_MAX_POINTS];
} Entity;

typedef struct Player
{
  bool connected;
  BOX_UNLOCKS_TYPE box_unlocks; // each bit is that box's unlock
  enum Squad squad;
  EntityID entity;
  EntityID last_used_medbay;
  InputFrame input;
} Player;

// use i.sun to access the current sun's pointer
typedef struct SunIter
{
  int i;
  Entity *sun;
} SunIter;
#define SUNS_ITER(gs_ptr)                      \
  for (SunIter i = {0}; i.i < MAX_SUNS; i.i++) \
    if ((i.sun = get_entity(gs_ptr, (gs_ptr)->suns[i.i])) != NULL)

#define ENTITIES_ITER(gs, cur)                                                      \
  for (Entity *cur = (gs)->entities; cur < (gs)->entities + (gs)->cur_next_entity; cur++) \
    if (cur->exists)

// gotta update the serialization functions when this changes
typedef struct GameState
{
  cpSpace *space;

  uint64_t tick;
  double subframe_time; // @Robust remove this, I don't think it's used anymore

  Player players[MAX_PLAYERS];
  EntityID suns[MAX_SUNS]; // can't have holes in it for serialization

  cpVect platonic_positions[MAX_BOX_TYPES]; // don't want to search over every entity to get the nearest platonic box!

  bool server_side_computing; // some things only the server should know and calculate, like platonic locations

  // Entity arena
  // entity pointers can't move around because of how the physics engine handles user data.
  // if you really need this, potentially refactor to store entity IDs instead of pointers
  // in the shapes and bodies of chipmunk. Would require editing the library I think
  Entity *entities;
  unsigned int max_entities;    // maximum number of entities possible in the entities list
  unsigned int cur_next_entity; // next entity to pass on request of a new entity if the free list is empty
  EntityID free_list;
} GameState;

#define PLAYERS_ITER(players, cur)                                \
  for (Player *cur = players; cur < players + MAX_PLAYERS; cur++) \
    if (cur->connected)

#define PI M_PI
#define TAU (M_PI * 2.0)

// returns in radians
static inline double rotangle(enum CompassRotation rot)
{
  switch (rot)
  {
  case Right:
    return 0.0f;
    break;
  case Down:
    return -PI / 2.0f;
    break;
  case Left:
    return -PI;
    break;
  case Up:
    return -3.0f * PI / 2.0f;
    break;
  default:
    Log("Unknown rotation %d\n", rot);
    return -0.0f;
    break;
  }
}

typedef struct OpusPacket
{
  opus_int32 length;
  unsigned char data[VOIP_PACKET_MAX_SIZE];
} OpusPacket;

typedef struct ServerToClient
{
  struct GameState *cur_gs;
  Queue *audio_playback_buffer;
  int your_player;
} ServerToClient;

typedef struct ClientToServer
{
  Queue *mic_data;   // on serialize, flushes this of packets. On deserialize, fills it
  Queue *input_data; // does not flush on serialize! must be in order of tick
} ClientToServer;

#define DeferLoop(start, end) \
  for (int _i_ = ((start), 0); _i_ == 0; _i_ += 1, (end))

// server
void server(void *info); // data parameter required from thread api...
void create_player(Player *player);
bool box_unlocked(Player *player, enum BoxType box);

// gamestate
void create_initial_world(GameState *gs);
void initialize(struct GameState *gs, void *entity_arena, size_t entity_arena_size);
void destroy(struct GameState *gs);
void process_fixed_timestep(GameState *gs);
// if is subframe, doesn't always increment the tick. When enough
// subframe time has been processed, increments the tick
void process(struct GameState *gs, double dt); // does in place
Entity *closest_box_to_point_in_radius(struct GameState *gs, cpVect point, double radius, bool (*filter_func)(Entity *));
uint64_t tick(struct GameState *gs);
double elapsed_time(GameState *gs);
double sun_dist_no_gravity(Entity *sun);

void quit_with_popup(const char *message_utf8, const char *title_utf8);

// serialization stuff
typedef struct SerState
{
  unsigned char *bytes;
  bool serializing;
  size_t cursor; // points to next available byte, is the size of current message after serializing something
  size_t max_size;
  Entity *for_player;
  size_t max_entity_index; // for error checking
  bool write_varnames;
  bool save_or_load_from_disk;

  // output
  uint32_t version;
  uint32_t git_release_tag; // release tag, unlike version, is about the game version not the serialization verson
} SerState;

typedef struct SerMaybeFailure
{
  bool failed;
  int line;
  const char *expression;
} SerMaybeFailure;

// all of these return if successful or not
size_t ser_size(SerState *ser);
SerState init_serializing(GameState *gs, unsigned char *bytes, size_t max_size, Entity *for_player, bool to_disk);
SerState init_deserializing(GameState *gs, unsigned char *bytes, size_t max_size, bool from_disk);
SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s);
SerMaybeFailure ser_client_to_server(SerState *ser, ClientToServer *msg);
SerMaybeFailure ser_inputframe(SerState *ser, InputFrame *i);

// entities
bool is_burning(Entity *missile);
Entity *get_entity(struct GameState *gs, EntityID id);
Entity *new_entity(struct GameState *gs);
EntityID get_id(struct GameState *gs, Entity *e);
cpVect entity_pos(Entity *e);
bool box_interactible(GameState *gs, Player *for_player, Entity *box);
void entity_set_rotation(Entity *e, double rot);
bool could_learn_from_scanner(Player *for_player, Entity *box);
void entity_set_pos(Entity *e, cpVect pos);
double entity_rotation(Entity *e);
void entity_ensure_in_orbit(GameState *gs, Entity *e);
void entity_memory_free(GameState *gs, Entity *e);
#define BOX_CHAIN_ITER(gs, cur, starting_box) for (Entity *cur = get_entity(gs, starting_box); cur != NULL; cur = get_entity(gs, cur->next_box))
#define BOXES_ITER(gs, cur, grid_entity_ptr) BOX_CHAIN_ITER(gs, cur, (grid_entity_ptr)->boxes)
typedef struct LauncherTarget
{
  bool target_found;
  double facing_angle; // in global coords
} LauncherTarget;
LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher);

// grid
void grid_create(struct GameState *gs, Entity *e);
void create_box(struct GameState *gs, Entity *new_box, Entity *grid, cpVect pos, enum BoxType type);
Entity *box_grid(Entity *box);
cpVect grid_com(Entity *grid);
cpVect grid_vel(Entity *grid);
cpVect box_vel(Entity *box);
cpVect grid_local_to_world(Entity *grid, cpVect local);
cpVect grid_world_to_local(Entity *grid, cpVect world);
cpVect grid_snapped_box_pos(Entity *grid, cpVect world); // returns the snapped pos in world coords
double entity_angular_velocity(Entity *grid);
cpVect entity_shape_pos(Entity *box);
double box_rotation(Entity *box);

// thruster
cpVect box_facing_vector(Entity *box);
cpVect thruster_force(Entity *box);

// debug draw
void dbg_drawall();
void dbg_line(cpVect from, cpVect to);
void dbg_rect(cpVect center);

typedef struct ServerThreadInfo
{
  ma_mutex info_mutex;
  const char *world_save;
  bool should_quit;
} ServerThreadInfo;
// all the math is static so that it can be defined in each compilation unit its included in

typedef struct AABB
{
  double x, y, width, height;
} AABB;

static inline AABB centered_at(cpVect point, cpVect size)
{
  return (AABB){
      .x = point.x - size.x / 2.0f,
      .y = point.y - size.y / 2.0f,
      .width = size.x,
      .height = size.y,
  };
}

static inline bool has_point(AABB aabb, cpVect point)
{
  return point.x > aabb.x && point.x < aabb.x + aabb.width && point.y > aabb.y && point.y < aabb.y + aabb.height;
}

static inline double cpvprojectval(cpVect vec, cpVect onto)
{
  double length_onto = cpvlength(onto);
  return cpvdot(vec, onto) / (length_onto * length_onto);
}

// spins around by theta
static inline cpVect cpvspin(cpVect vec, double theta)
{
  return (cpVect){
      .x = vec.x * cos(theta) - vec.y * sin(theta),
      .y = vec.x * sin(theta) + vec.y * cos(theta),
  };
}

// also known as atan2
static inline double cpvangle(cpVect vec)
{
  return atan2(vec.y, vec.x);
}

typedef struct BoxCentered
{
  cpVect pos;
  double rotation;
  cpVect size; // half width and half height, centered on position
} BoxCentered;

static inline bool box_has_point(BoxCentered box, cpVect point)
{
  cpVect local_point = cpvspin(cpvsub(point, box.pos), -box.rotation);
  return has_point((AABB){.x = -box.size.x / 2.0, .y = -box.size.y / 2.0, .width = box.size.x, .height = box.size.y}, local_point);
}

static double sign(double f)
{
  if (f >= 0.0f)
    return 1.0f;
  else
    return -1.0f;
}

static inline double clamp01(double f)
{
  return fmax(0.0f, fmin(f, 1.0f));
}

static inline double clamp(double f, double minimum, double maximum)
{
  if (f < minimum)
    return minimum;
  if (f > maximum)
    return maximum;
  return f;
}

static inline double cpvanglediff(cpVect a, cpVect b)
{
  double acos_input = cpvdot(a, b) / (cpvlength(a) * cpvlength(b));
  acos_input = clamp(acos_input, -1.0f, 1.0f);
  flight_assert(acos_input >= -1.0f && acos_input <= 1.0f);
  return acos(acos_input) * sign(cpvdot(a, b));
}

static inline double fract(double f)
{
  return f - floor(f);
}

static inline double lerp(double a, double b, double f)
{
  return a * (1.0f - f) + (b * f);
}

static inline double lerp_angle(double p_from, double p_to, double p_weight)
{
  double difference = fmod(p_to - p_from, (float)TAU);
  double distance = fmod(2.0f * difference, (float)TAU) - difference;
  return p_from + distance * p_weight;
}

static inline cpVect cpvfloor(cpVect p)
{
  return (cpVect){floor(p.x), floor(p.y)};
}

static inline cpVect cpvfract(cpVect p)
{
  return (cpVect){fract(p.x), fract(p.y)};
}

// for random generation
static inline double hash11(double p)
{
  p = fract(p * .1031f);
  p *= p + 33.33f;
  p *= p + p;
  return 2.0 * fract(p) - 1.0;
}

static inline double deg2rad(double deg)
{
  return (deg / 360.0f) * 2.0f * PI;
}

#define min(X, Y) (((X) < (Y)) ? (X) : (Y))
#define max(X, Y) (((X) > (Y)) ? (X) : (Y))