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

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

#ifndef _XFER_H_
#define _XFER_H_

#include "simtypes.h"

#define FILE_EXTENSION_MD5 ".md5" /* extension of hash files */

struct _xfer {
  struct _xfer *next;     /* next transfer with the same contact */
  struct _xfer **last;    /* pointer to next pointer of previous transfer with the same contact */
  simtype name;           /* file name (pathname for outgoing transfers) */
  simtype hash;           /* MD5 checksum of file contents up to initial (XFER_TYPE_SEND) or final position */
  simnumber filesize;     /* size of file as reported by the system message */
  simnumber handle;       /* temporary name of file */
  simnumber cid;          /* 61-bit identifier of computer the file was sent to or received from or zero if none */
  simnumber sndtime;      /* transfer start time */
  simnumber rcvtime;      /* last file change time for resumption (for outgoing transfers) or zero if not known */
  simnumber rcvdpos;      /* last received file offset or negative if unknown */
  simnumber rcvdsize;     /* last initial file position (minimal resumption size) */
  simnumber mdsize;       /* number of bytes hashed in context or negative if file not yet sent or received */
  void *md;               /* hash context for resumption or NULL if none */
  signed char type;       /* XFER_TYPE_xxx */
  unsigned char bits;     /* XFER_BIT_xxx */
  short err;              /* SIM_OK or error code (sent to peer) to report */
  short status;           /* SIM_OK or transfer match status at peer (SIM_SOCKET_NO_ERROR if matched) */
  unsigned short retries; /* number of transfer attempts in the past */
};

/* find transfer by handle; return pointer to transfer or NULL if not found */
struct _xfer *xfer_find (simcontact contact, simnumber handle);

/* return transfer type name. also return transfer status as *error */
const char *xfer_get_type_name (simcontact contact, const struct _xfer *xfer, int *error);

/* release file transfer file name and free it if no longer in use */
void xfer_free_name (simtype filename);

/* fd must be negative to return 61-bit computer identifier to send to this contact */
simnumber xfer_new_handle (simcontact contact, int fd);

/* return name of upload or download directory or nil if none. caller must call string_free */
simtype xfer_new_dir_name (simcontact contact, simbool sent);

/* check if there is something to send to contact. zero cid matches anything */
simbool xfer_check_pending (simcontact contact, simnumber cid);

/* check if transfer over proxy connection can be unlocked and do so if possible */
void xfer_check_unlock (simclient client);

/* send end of file to contact. error should be SIM_XFER_CANCELLED */
int xfer_send_close (simcontact contact, simnumber handle, int error);

/* send pause or unpause to contact. handle can also be SIM_CMD_XFER_INIT_PAUSE_xxx */
int xfer_send_pause (simcontact contact, simnumber handle);

/* send file to contact */
int xfer_send_file (simcontact contact, const simtype pathname, simnumber *handle);

/* contact is accepting or refusing to accept a sent file */
int xfer_accept_file (simclient client, simnumber handle, const simtype hash, simnumber hashsize);

/* contact is sending a file */
int xfer_recv_file (simclient client, simnumber handle, const simtype filename,
                    simnumber filesize, const simtype filetype, simnumber sendtime);

/* contact is sending a file fragment */
int xfer_recv_data_ (simclient client, const simtype data);

/* contact has received a file fragment */
void xfer_recv_speed (simclient client, simnumber filesize, simnumber speed);

/* contact is sending end of file */
int xfer_recv_close (simclient client, simnumber handle, const simtype hash, simnumber filetime, int error);

/* contact is sending end of file acknowledgment or an unpause acknowledgment */
int xfer_recv_end (simclient client, simnumber handle, int error);

/* contact is sending pause or unpause of file. handle can also be SIM_CMD_XFER_INIT_PAUSE_xxx */
int xfer_recv_pause (simclient client, simnumber handle, simnumber name, simnumber names);

/* contact is checking whether a known file is going to be sent (if error = SIM_SOCKET_NO_ERROR) or replying to such a check */
int xfer_recv_handle (simclient client, simnumber handle, simnumber error);

/* start file transfer thread */
int xfer_start_thread (simclient client);

/* stop file transfer thread */
void xfer_stop_thread_ (simclient client);

/* save current file transfer state if necessary */
void xfer_periodic (simbool save);

/* save current file transfer state */
int xfer_save (void);

/* load and unload file transfer state */
int xfer_init (void);
void xfer_uninit (void);

/* debug logging */
int xfer_log_queue (const char *module, int level, simnumber id, simnumber handle);

#endif
