/* Copyright (c) 2020-2022 The Creators of Simphone

   See the file COPYING.LESSER.txt for copying permission.
*/

#ifndef _LIMIT_H_
#define _LIMIT_H_

#include "simtypes.h"

/* return the number of proxy sockets of the given type */
int limit_count_customer (int type);

/* add to cpu cycles (LIMIT_CPU_TIME) or nanoseconds (LIMIT_CPU_MAX) */
simunsigned limit_set_cpu (simunsigned oldcpu, simunsigned newcpu);

#define LIMIT_CPU_LOAD 0 /* number of cpu cycles of bogohash init */
#define LIMIT_CPU_RATE 1 /* number of cpu cycles per millisecond */
#define LIMIT_CPU_BOGO 2 /* number of bogohash init iterations */
#define LIMIT_CPU_HASH 3 /* number of bogohashes per nanosecond */
#define LIMIT_CPU_TIME 4 /* number of accumulated cpu cycles */
#define LIMIT_CPU_MAX 5  /* number of additionally accumulated cpu nanoseconds */

extern simnumber limit_cpu[]; /* indexed by LIMIT_CPU_xxx */

/* return number of accumulated milliseconds (LIMIT_CPU_TIME + LIMIT_CPU_MAX) */
simnumber limit_get_cpu (void);

#define LIMIT_PARAM_MAX 0     /* maximal internet speed as measured */
#define LIMIT_PARAM_REVERSE 1 /* proxy.reverse */
#define LIMIT_PARAM_MEASURE 2 /* limit.measure */
#define LIMIT_PARAM_RECV 3    /* allocated speed or zero if deallocated */
#define LIMIT_PARAM_SEND 4    /* maximal send speed or non-positive if unknown */

/* return limit parameter. param is LIMIT_PARAM_xxx */
unsigned limit_get_param (int param);

/* calculate maximal proxy speed */
int limit_get_speed_limit (int minlimit);

/* return speed limit for a given proxy customer (0 = infinite) */
int limit_get_speed_server (simcustomer server);

/* reset cpu load data */
void limit_reset (simunsigned cpu, simunsigned tick);

/* check if ip address has been temporarily blocked from making incoming connections */
int limit_check_blacklisted (simcustomer customer, unsigned ip);

/* check if there's an available (new) socket that can be accepted */
int limit_test_socket_ (simcustomer server);

/* make sure there's an available socket for a new client */
int limit_test_client_ (simsocket sock, simcontact contact);

/* check cpu load on a new incoming connection from ip address and blacklist if necessary */
void limit_test_customer_ip (simcustomer customer, unsigned ip);

/* check if there's an available socket for a proxy client, and that a new connection from this ip address is allowed */
int limit_test_client_ip (simcustomer client, simcustomer server);

/* check if there's an available socket and speed for a proxy server (customer) */
int limit_test_server (simcustomer server);

/* check if a new proxy server (customer) from this ip address is allowed */
int limit_test_server_ip (simcustomer server);

/* check if there's available speed for a proxy client */
int limit_test_handshake (simcustomer client, simcustomer server, simcustomer stats);

/* return SIM_OK if receiving a UDP packet from this customer (server, client, proxy) is currently allowed */
int limit_test_udp (simcustomer customer, int length);

/* enforce speed limit for a given proxy customer (by sleeping if necessary). client must be NULL */
int limit_wait_server_ (simcustomer server, simcustomer client);

/* enforce speed limit for a given proxy client (by sleeping if necessary) */
int limit_wait_client_ (simcustomer client, simcustomer server);

/* enforce handshake speed limit (by sleeping if necessary) */
void limit_wait_handshake_ (simcustomer customer, simcustomer stats);

/* initialize internet speed measurement (ping with the specified size) to given client or proxy customer */
simbool limit_start_ping (simsocket sock, simcontact contact, simunsigned tick,
                          simunsigned received, simunsigned sent, int measure);

/* measure internet speed to given client or proxy customer */
int limit_ping_ (simsocket sock, simcontact contact, simunsigned tick, unsigned size);

/* finalize internet speed measurement */
void limit_stop_ping (simsocket sock);

#define LIMIT_CMD_REPLY 0   /* return speed reply array */
#define LIMIT_CMD_REQUEST 1 /* request speed allocation or deallocation */
#define LIMIT_CMD_START 2   /* request speed limit reports */
#define LIMIT_CMD_STOP 3    /* no longer request speed limit reports */

/* create and return a speed cmd (LIMIT_CMD_xxx) packet or nil if nothing to send. caller must call table_free */
simtype limit_new_cmd (int cmd);

/* process a speed request packet and create and return a reply *table or nil if no reply is to be sent */
simcustomer limit_exec_request (simcustomer server, simtype *table);

/* process a speed reply packet */
int limit_exec_reply (simcustomer proxy, const simtype table);

/* send LIMIT_CMD_STOP (if speed is LIMIT_SPEED_STOP) or LIMIT_CMD_START (otherwise) */
int limit_send_request (simclient client, int speed);

#define LIMIT_SPEED_STOP 0  /* send LIMIT_CMD_STOP */
#define LIMIT_SPEED_START 1 /* send LIMIT_CMD_START */
#define LIMIT_SPEED_SEND 2  /* send LIMIT_CMD_START now */

/* ask proxy to allocate speed with a client (zero speed to deallocate) */
int limit_send_speed (simclient client, unsigned speed);

/* called when server port is opened or closed (init = true) and on ip change (init = false) */
void limit_reinit (simbool init);

/* initialize and deinitialize limit tables */
void limit_init (void);
void limit_uninit (void);

/* debug logging */
simbool limit_log_sockets (const char *module, int level);

#endif
