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

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

#ifndef _API_H_
#define _API_H_

#include "simtypes.h"
#include "version.h"

#define SIM_VERSION_BAD "_OS"

void sim_protect_ (void);
void sim_unprotect_ (void);

#define PTH_PROTECT_UNPROTECT_(CODE) (sim_protect_ (), (CODE), sim_unprotect_ ()) /* wrap any pth function */

#define SIM_INIT_BIT_DEFAULT 0 /* reset all init bits */
#define SIM_INIT_BIT_NOAUDIO 1 /* do not use audio */
#define SIM_INIT_BIT_NODHT 2   /* do not connect to the DHT */
#define SIM_INIT_BIT_NOCRASH 4 /* do not install crash reporter */
#define SIM_INIT_BIT_INSTALL 8 /* try to bypass firewall */

/* these can be called before sim_init_ */

unsigned sim_init_bits (unsigned bits); /* set initialization flags */

#define SIM_PATH_EXE 1  /* exe file name */
#define SIM_PATH_USER 2 /* user home directory */

simtype sim_init_path (int path);                             /* return exe name (path is SIM_PATH_EXE) */
simtype sim_init_path_ (const char *user, simbool create);    /* return user directory and optionally create it */
int sim_init_move (const char *pathname, const char *prefix); /* rename last part of pathname to random name with given prefix */
simtype sim_init_param_ (const char *user);                   /* read and return the contents of the login file */

/* initialize and deinitialize the library */
int sim_init_ (const char *user, unsigned version, const char *ui);
int sim_init_user_ (const char *password);
int sim_exit_user_ (void);
int sim_exit_ (void);

typedef void (simevent) (simnumber id, const simtype event);
int sim_event_register_ (const char *name, simevent *handler, simevent **oldhandler);

#define SIM_KEY_NONE (unsigned) (-1)
#define SIM_KEY_BP512R1_RSA2K 0  /* 512-bit EC key + 2048-bit RSA key */
#define SIM_KEY_BP512R1_RSA4K 1  /* 512-bit EC key + 4096-bit RSA key */
#define SIM_KEY_BP512R1_RSA8K 2  /* 512-bit EC key + 8192-bit RSA key */
#define SIM_KEY_BP512R1_RSA16K 3 /* 512-bit EC key + 16834-bit RSA key */

#define SIM_MAX_SEED_BITS 3072

/* generate seed or key */
int sim_key_generate_ (simtype *seed, const simtype entropy, int size, unsigned type);

#define SIM_PASSWORD_BIT_DEFAULT 0   /* save key file only if it didn't exist */
#define SIM_PASSWORD_BIT_OVERWRITE 1 /* overwrite key file even if it exists */
#define SIM_PASSWORD_BIT_CHECK 2     /* only check seed for validity */

/* protect current key with a new password */
int sim_key_set_password_ (const char *password, unsigned bits);

#define SIM_STATUS_FLAG_DHT_OUT 1  /* internet connection is present */
#define SIM_STATUS_FLAG_DHT_IN 2   /* incoming UDP port is receiving DHT requests */
#define SIM_STATUS_FLAG_UDP_OUT 4  /* outgoing UDP port is receiving replies */
#define SIM_STATUS_FLAG_UDP_IN 8   /* incoming UDP port is receiving requests */
#define SIM_STATUS_FLAG_TCP_OUT 16 /* outgoing TCP to port other than 443 */
#define SIM_STATUS_FLAG_TCP_IN 32  /* incoming TCP port is receiving requests */
#define SIM_STATUS_FLAG_SSL_OUT 64 /* outgoing TCP to port 443 */
#define SIM_STATUS_FLAG_SSL_IN 128 /* incoming TCP port 443 is receiving requests */
#define SIM_STATUS_FLAG_DNS 256    /* DNS resolver has been used successfully */
#define SIM_STATUS_FLAG_BOOT 512   /* has attempted to boot (or reboot) the DHT */
#define SIM_STATUS_FLAG_UPNP 1024  /* incoming TCP port forwarded by uPNP service */

#define SIM_STATUS_FLAG_IP 8388608 /* internally used */

/* return my status */
int sim_status_get (unsigned *flags);
int sim_status_get_ (unsigned *flags);

/* set my status */
int sim_status_set_ (int status);

#define SIM_STATUS_EXEC_DHT_OFF "dhtoff"    /* disconnect from the DHT */
#define SIM_STATUS_EXEC_DHT_ON "dhton"      /* connect to the DHT */
#define SIM_STATUS_EXEC_LOG_OFF "logoff"    /* inform contacts that i am offline */
#define SIM_STATUS_EXEC_LOG_ON "logon"      /* inform contacts that i am online */
#define SIM_STATUS_EXEC_CHECK "check"       /* check if still online */
#define SIM_STATUS_EXEC_PROXY_BLOCK "block" /* disconnect and blacklist current proxy */
#define SIM_STATUS_EXEC_PROXY_DROP "drop"   /* disconnect current proxy */

/* perform a special action (SIM_STATUS_EXEC_xxx) */
int sim_status_exec_ (const char *type, const char *value);

/* add connection to contact */
int sim_contact_connect_ (simnumber id);

/* remove connection to contact */
int sim_contact_disconnect_ (simnumber id);

#define SIM_PING_BIT_CONNECT 1 /* connect if not currently connected */
#define SIM_PING_BIT_TCP 2     /* send ping over TCP socket (SIM_EVENT_NET_PONG will be received) */
#define SIM_PING_BIT_AUDIO 4   /* send ping with next audio packet (SIM_EVENT_NET_QOS will be received) */
#define SIM_PING_BIT_PROXY 8   /* ping proxy instead of contact (if connected through proxy) */
#define SIM_PING_BIT_NAT 16    /* initiate a NAT traversal attempt (if connected through proxy) */

/* check if contact is alive */
int sim_contact_ping_ (simnumber id, simnumber number, unsigned bits);

#define CONTACT_KEY_ADDRESS "addr"
#define CONTACT_KEY_SEEN "seen"
#define CONTACT_KEY_IP "ip"
#define CONTACT_KEY_CONNECT "pxy"
#define CONTACT_KEY_LATENCY "ms"
#define CONTACT_KEY_KEY_SIZE "ks"

#define CONTACT_KEY_AUDIO "call"
#define CONTACT_AUDIO_HANGUP ""
#define CONTACT_AUDIO_OUTGOING "out"
#define CONTACT_AUDIO_INCOMING "in"
#define CONTACT_AUDIO_TALKING "talk"
#define CONTACT_AUDIO_UDP "udp"
#define CONTACT_AUDIO_CALLING "conn"
#define CONTACT_AUDIO_RINGING "ring"

#define CONTACT_KEY_VIP "vip"
#define CONTACT_VIP_MYSELF "self"
#define CONTACT_VIP_TEST "test"
#define CONTACT_VIP_SYSTEM "simphone"

#define CONTACT_KEY_VERIFY "verify"

#define CONTACT_KEY_THIS_CLIENT_RECEIVED "TCR"
#define CONTACT_KEY_THIS_CLIENT_SENT "TCS"
#define CONTACT_KEY_THIS_CLIENT_TIME "TCT"
#define CONTACT_KEY_THIS_CLIENT_COUNT "TCC"

#define CONTACT_KEY_THIS_SOCKET_RECEIVED "TSR"
#define CONTACT_KEY_THIS_SOCKET_SENT "TSS"
#define CONTACT_KEY_THIS_SOCKET_TIME "TST"
#define CONTACT_KEY_THIS_SOCKET_COUNT "TSC"

#define CONTACT_KEY_THIS_OUTPUT_RECEIVED "TOR"
#define CONTACT_KEY_THIS_OUTPUT_SENT "TOS"
#define CONTACT_KEY_THIS_OUTPUT_TIME "TOT"
#define CONTACT_KEY_THIS_OUTPUT_COUNT "TOC"

#define CONTACT_KEY_THIS_INPUT_RECEIVED "TIR"
#define CONTACT_KEY_THIS_INPUT_SENT "TIS"
#define CONTACT_KEY_THIS_INPUT_TIME "TIT"
#define CONTACT_KEY_THIS_INPUT_COUNT "TIC"

#define CONTACT_KEY_PROXY_SPEED_LIMIT "PSL"

#define CONTACT_KEY_FRAMES_MINE_READ "FMR"
#define CONTACT_KEY_FRAMES_MINE_WRITE "FMW"
#define CONTACT_KEY_FRAMES_MINE_ALL "FMA"
#define CONTACT_KEY_FRAMES_MINE_LOST "FML"

#define CONTACT_KEY_FRAMES_PEER_READ "FPR"
#define CONTACT_KEY_FRAMES_PEER_WRITE "FPW"
#define CONTACT_KEY_FRAMES_PEER_ALL "FPA"
#define CONTACT_KEY_FRAMES_PEER_LOST "FPL"

#define CONTACT_KEY_MS_TO_CLIENT "LTC"
#define CONTACT_KEY_MS_TO_PROXY "LTP"
#define CONTACT_KEY_MS_FROM_CLIENT "LFC"
#define CONTACT_KEY_MS_FROM_PROXY "LFP"

#define CONTACT_BIT_DEFAULT 1  /* most of the keys */
#define CONTACT_BIT_LATENCY 2  /* ping time */
#define CONTACT_BIT_VERIFY 4   /* verification token */
#define CONTACT_BIT_LINE 8     /* info line */
#define CONTACT_BIT_STATS 16   /* statistics */
#define CONTACT_BIT_VERSION 32 /* version list */

#define CONTACT_ID_TEST 1          /* id of audio test (return client statistics) */
#define CONTACT_ID_KEYGEN 2        /* id for audio entropy collection */
#define CONTACT_ID_SELF 2          /* return self contact (nick) */
#define CONTACT_ID_XFER_RECEIVED 2 /* check if received file name is valid */
#define CONTACT_ID_XFER_SENT 3     /* return time of file transfer */
#define CONTACT_ID_PROXY 3         /* return server statistics */

/* return bits of information about contact or list of contacts if zero id */
simtype sim_contact_get_ (simnumber id, unsigned bits);

/* free returned information about contact or contacts */
void sim_contact_free_ (simtype contact);

/* add and authorize a (new) contact and return its *id */
int sim_contact_add_ (const char *address, simnumber *id);

/* set contact information (CONTACT_KEY_NICK, CONTACT_KEY_LINE, CONTACT_KEY_FLAGS or CONTACT_KEY_VERIFY */
int sim_contact_set_ (simnumber id, const char *key, const simtype value);

#define SIM_MSG_DELIVERED 0    /* received ACK from peer */
#define SIM_MSG_PENDING 1      /* pushed into the pth queue */
#define SIM_MSG_SENT 2         /* sent to socket (not in pth queue any more) */
#define SIM_MSG_NOTSENT 3      /* not sent to socket because peer is not connected */
#define SIM_MSG_ACKNOWLEDGED 4 /* received ACK from peer but still in pth queue (impossible) */
#define SIM_MSG_UNSENT 5       /* PENDING message was reported in SIM_EVENT_NOTSENT */
#define SIM_MSG_UNDELIVERED 6  /* did not receive ACK for SENT message (reported in SIM_EVENT_NOTSENT) */
#define SIM_MSG_NOTDELIVERED 7 /* NOTSENT message was reported in SIM_EVENT_NOTSENT */
#define SIM_MSG_INCOMING 8     /* received from peer */

#define SIM_MAX_MSG_SIZE 7000 /* hopefully leaves enough room for added overhead */

#define SIM_MSG_TYPE_UTF 0    /* UTF-8 text message */
#define SIM_MSG_TYPE_SYSTEM 1 /* status message, file transfer notification or audio call */
#define SIM_MSG_TYPE_HTML 2   /* not used */

/* send chat message in UTF-8. always consumes the message text and frees it when done */
int sim_msg_send_utf_ (simnumber id, simtype text, simunsigned *idx);

/* edit sent chat message. always consumes the new message text and frees it when done */
int sim_msg_edit_utf_ (simnumber id, simtype text, simunsigned idx);

/* delete chat message by replacing its text with spaces in history file */
int sim_msg_remove_ (simnumber id, simunsigned idx);

/* return stored message with the specified index or nil if failed. else caller must call sim_msg_free_ */
simtype sim_msg_get_ (simnumber id, simunsigned idx);

/* free returned message */
void sim_msg_free_ (simtype msg);

/* return number of stored messages with the specified contact or zero if failed (no such contact) */
simunsigned sim_msg_count_ (simnumber id);

/* load last number of messages from history file into buffer. clear buffer if number is zero */
int sim_msg_load_ (simnumber id, simunsigned number);

/* load last number of messages from console log into buffer. clear buffer if number is zero */
int sim_console_load_ (simnumber number);

/* start a file transfer */
int sim_xfer_send_file_ (simnumber id, const simtype pathname, simnumber *handle);

#define SIM_XFER_GET_INFO_PAUSE "pause"
#define SIM_XFER_GET_INFO_CURRENT "current"
#define SIM_XFER_GET_INFO_DURATION "ms"
#define SIM_XFER_GET_INFO_TRANSFERRED "size"
#define SIM_XFER_GET_INFO_RECEIVED "received"
#define SIM_XFER_GET_INFO_SENT "sent"

#define SIM_XFER_GET_RECEIVED 0 /* return array of tables containing incoming transfers */
#define SIM_XFER_GET_SENT 1     /* return array of tables containing incoming and outgoing transfers */
#define SIM_XFER_GET_INFO (-1)  /* return a table keyed by SIM_XFER_GET_INFO_xxx */

#define SIM_XFER_BIT_DEFAULT 0
#define SIM_XFER_BIT_EXTENDED 1

/* return information about file transfer handle unless handle is SIM_XFER_GET_xxx. caller must call sim_xfer_free */
simtype sim_xfer_get_ (simnumber id, simnumber handle, unsigned bits);

#define SIM_XFER_TYPE_DONE "DONE"
#define SIM_XFER_TYPE_INIT "INIT"
#define SIM_XFER_TYPE_WAIT "WAIT"
#define SIM_XFER_TYPE_HOLD "HOLD"
#define SIM_XFER_TYPE_SEND "SEND"
#define SIM_XFER_TYPE_WIPE "WIPE"
#define SIM_XFER_TYPE_RECV "RECV"
#define SIM_XFER_TYPE_HASH "HASH"
#define SIM_XFER_TYPE_DATA "DATA"
#define SIM_XFER_TYPE_EXEC "EXEC"

/* free returned file transfer list or information */
void sim_xfer_free_ (simtype xfer);

#define SIM_XFER_TYPE_FREE "free"
#define SIM_XFER_TYPE_KILL "kill"
#define SIM_XFER_TYPE_CANCEL "cancel"
#define SIM_XFER_TYPE_PAUSE "pause"

/* control a file transfer */
int sim_xfer_set_ (simnumber id, simnumber handle, const char *type);

/* call a contact */
int sim_audio_call_ (simnumber id);

/* terminate call or decline to answer. use zero to terminate current call */
int sim_audio_hangup_ (simnumber id);

/* return id of contact which is currently talking or zero */
simnumber sim_audio_check_talking_ (void);

/* start playing sound file the specified number of times (or until stop call if count = 0) */
int sim_sound_start_ (const char *sound, int count);

/* stop playing sound file */
int sim_sound_stop_ (const char *sound);

/* return default, minimal and maximal allowed value of configuration parameter */
int sim_param_get_limits_ (const char *param, simtype *def, int *min, int *max, const char **description);

#define SIM_PARAM_GET_WARNINGS "warn" /* return only warning parameters */

/* return configuration parameter or the whole configuration table if NULL param */
simtype sim_param_get_ (const char *param);

/* free returned configuration table or parameter */
void sim_param_free_ (simtype value);

#define SIM_PARAM_UNAPPLIED (-1) /* for internal use only */
#define SIM_PARAM_PERMANENT 0    /* save value to configuration file */
#define SIM_PARAM_TEMPORARY 1    /* set value only until logout */
#define SIM_PARAM_USER 2         /* save value of a new (not predefined) parameter to configuration file */

/* set configuration parameter(s) */
int sim_param_set_ (const char *param, const simtype value, int permanent);

#define SIM_INFO_BIT_NET 1   /* network status */
#define SIM_INFO_BIT_AUDIO 2 /* lists of audio devices */
#define SIM_INFO_BIT_CRYPT 4 /* list of supported/preferred ciphers */

#define SIM_INFO_NET_IP "net.ip"
#define SIM_INFO_NET_DHT_GOOD "dht.good"
#define SIM_INFO_NET_DHT_DUBIOUS "dht.dubious"
#define SIM_INFO_NET_DHT_BLOCKED "dht.blocked"
#define SIM_INFO_NET_DHT_BAD "dht.blacklisted"
#define SIM_INFO_NET_DHT_INCOMING "dht.incoming"
#define SIM_INFO_NET_DHT_STATUS "dht.status"

#define SIM_INFO_AUDIO_INPUT "audio.input"
#define SIM_INFO_AUDIO_OUTPUT "audio.output"

#define SIM_INFO_CRYPT_CIPHERS "ciphers"

/* return system information */
simtype sim_list_info_ (unsigned bits);

/* return version numbers */
simtype sim_list_versions (void);

/* free returned list */
void sim_list_free (simtype list);

/* internal use only */

simbool sim_check_version (unsigned version);
char *sim_get_version (char *version, const char **ui);

int sim_random_get (simbyte *output, int length); /* return random bytes from the system */

#define CLIENT_STATE_STOPPING (-1) /* starting to log off */
#define CLIENT_STATE_STOPPED 0     /* logged off (status is SIM_STATUS_OFF) */
#define CLIENT_STATE_RUNNING 1     /* logged on (client queue/logon threads are running) */

struct _status_self {
  int status;           /* my current status (SIM_STATUS_xxx) */
  unsigned flags;       /* my current status flags (SIM_STATUS_FLAG_xxx) */
  unsigned oldflags;    /* my saved status flags (SIM_STATUS_FLAG_SSL_IN or zero) */
  int state;            /* my current state (CLIENT_STATE_xxx) */
  unsigned ipout;       /* my current ip address (reported by last outgoing client) or zero */
  unsigned ipin;        /* my current ip address (reported by last incoming client) or zero */
  simunsigned nonce[2]; /* for detecting connection to self */
  simnumber tickin;     /* tick of last incoming connection (SIM_STATUS_FLAG_TCP_IN) */
  simtype ipclient;     /* keyed by ip address, value is a counter */
  simtype ipdht;        /* keyed by ip address, value is a counter */
};

extern struct _status_self simself;

simunsigned sim_get_tick (void);
simunsigned status_get_tick (void); /* return millisecond age of my logon */

#endif
