// sb-send: Sonobus audio sender

// Common include files used by all of the other .c files in the project.

#ifndef SB_SEND_H
#define SB_SEND_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>

#include <opus/opus.h>
#include <opus/opus_multistream.h>
#include <ogg/ogg.h>

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define INITGUID
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <objbase.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <functiondiscoverykeys_devpkey.h>
#include <io.h>
#include <fcntl.h>
#include <unistd.h>
typedef SOCKET sock_t;
#define SOCK_INVALID INVALID_SOCKET
#define CLOSE_SOCKET closesocket
#define SOCK_ERR() WSAGetLastError()
#define SOCK_WOULDBLOCK WSAEWOULDBLOCK
#define SOCK_EINTR WSAEINTR
#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#ifdef ALSA
#include <alsa/asoundlib.h>
#endif
typedef int sock_t;
#define SOCK_INVALID (-1)
#define CLOSE_SOCKET close
#define SOCK_ERR() errno
#define SOCK_WOULDBLOCK EWOULDBLOCK
#define SOCK_EINTR EINTR
#endif

#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

#define VERSION "1.2r1"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
#define AOO_VERSION_MAJOR 2
#define AOO_VERSION_MINOR 0
#define AOO_PROTOCOL_FLAG_COMPACT_DATA 0x01
#define SLIP_END 192
#define SLIP_ESC 219
#define SLIP_ESC_END 220
#define SLIP_ESC_ESC 221

#define SAMPLE_RATE 48000
#define MAX_PACKET_SIZE 1400
#define MAX_OPUS_FRAME 4000
#define MAX_OPUS_ENCODE (MAX_PACKET_SIZE - 100)
#define OPUS_MAX_SAMPLES 5760 // 120ms at 48kHz, max possible Opus frame
#define MAX_PEERS 64
#define MAX_OSC_SIZE 2048

#define DEFAULT_SERVER "sonobus.ground.local"
#define DEFAULT_PORT 10998
#define DEFAULT_GROUP "gfcaud"
#define DEFAULT_NICK "sbsend"
#define DEFAULT_CHANNELS 1
#define DEFAULT_BITRATE 16000
#define DEFAULT_COMPLEXITY 10
#define MAINTENANCE_INTERVAL 3
#define INVITE_RETRY_INTERVAL 2
#define INVITE_MAX_RETRIES 3
#define PEER_TIMEOUT_SECONDS 300
#define PEER_CLEANUP_INTERVAL 60
#define UNINVITE_ACTIVE_INTERVAL 300
#define AUDIO_PREFILL_FRAMES (SAMPLE_RATE / 10)
#define STDIN_RING_SECONDS 2
#define STDIN_RING_SIZE (SAMPLE_RATE * STDIN_RING_SECONDS * 2)
#define PREFILL_SAMPLES_STDIN (SAMPLE_RATE / 2)
#define PREFILL_SAMPLES_WASAPI (SAMPLE_RATE / 10)

#ifdef _WIN32
#define SLEEP_MS(ms) Sleep(ms)
#else
#define SLEEP_MS(ms) usleep((ms) * 1000)
#endif

#define dbg(state, fmt, ...) do { \
    if ((state)->debug) fprintf(stderr, fmt, ##__VA_ARGS__); \
} while (0)

#define SB_ASSERT(cond) do { \
    if (!(cond)) { \
        fprintf(stderr, "ASSERT FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
        abort(); \
    } \
} while (0)

static const char MD5_EMPTY[] = "D41D8CD98F00B204E9800998ECF8427E";

typedef struct {
    char name[64];
    int64_t token;
    int32_t remote_sink_id;
    int32_t remote_source_id;
    int32_t our_source_id;
    int32_t salt;
    _Atomic int32_t sequence;
    struct sockaddr_in addr;
    time_t last_invite_time;
    time_t last_packet_time;
    time_t last_ping_reply;
    bool active;
    bool connected;
    bool format_sent;
    bool salt_initialized;
    bool uninvited;
    bool is_sb_send;
    int invite_count;
} Peer;

typedef struct {
    Peer *items;
    int count;
    int capacity;
} PeerList;

typedef struct {
    unsigned char buf[MAX_OSC_SIZE];
    int len;
    int escape;
} SlipDecoder;

typedef struct {
    char server[256];
    int server_port;
    char group[64];
    char password_hash[33];
    char nickname[64];
    int channels;
    int bitrate;
    int recv_bitrate;
    int complexity;
    int frame_size;
    bool voice;
    bool joint_stereo;
    char audio_device[64];
    sock_t tcp_sock;
    sock_t udp_sock;
    char public_ip[64];
    int public_port;
    int local_port;
    int64_t client_token;
    PeerList peers;
    int32_t next_sink_id;
    pthread_mutex_t peer_mutex;
    _Atomic bool running;
    _Atomic bool logged_in;
    _Atomic bool group_joined;
    bool debug;
    time_t last_uninvite;
    OpusMSEncoder *encoder;
    OpusMSDecoder *decoder;
    int16_t *decode_s16;
    bool use_stdin;
    int output_fd;
    int opus_output_fd;
    bool use_encoded;
    SlipDecoder slip;
#ifdef _WIN32
    bool use_wasapi;
    int wasapi_channels;
    IAudioClient *wasapi_client;
    IAudioCaptureClient *wasapi_capture;
#endif
#ifdef ALSA
    snd_pcm_t *capture;
#endif
} AppState;

typedef struct {
    float buf[STDIN_RING_SIZE];
    int64_t head;
    int64_t tail;
    int channels;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    _Atomic int done;
    bool backpressure;
} AudioRing;

extern _Atomic int g_running;
extern AudioRing g_ring;

uint64_t get_ntp_time(void);

int osc_pad4(int len);
int osc_write_string(unsigned char *buf, const char *s);
int osc_write_int32(unsigned char *buf, int32_t val);
int osc_write_int64(unsigned char *buf, int64_t val);
int osc_write_double(unsigned char *buf, double val);
int osc_write_timetag(unsigned char *buf);
int osc_write_blob(unsigned char *buf, const unsigned char *data, int len);
int osc_read_string(const unsigned char *buf, char *out, int max);
int32_t osc_read_int32(const unsigned char *buf);
int64_t osc_read_int64(const unsigned char *buf);

int slip_encode(const unsigned char *in, int inlen, unsigned char *out, int maxout);
void slip_decoder_init(SlipDecoder *d);
int slip_decode_byte(SlipDecoder *d, unsigned char b, unsigned char **msg, int *msglen);
int send_tcp_slip(sock_t sock, const unsigned char *msg, int len);

int build_login_msg(AppState *state, unsigned char *buf);
int build_group_join_msg(AppState *state, unsigned char *buf);
int build_group_leave_msg(AppState *state, unsigned char *buf);
int build_udp_request(unsigned char *buf);
int build_invite_msg(int32_t our_sink_id, unsigned char *buf);
int build_uninvite_msg(int32_t source_id, int32_t our_sink_id, unsigned char *buf);
int build_peer_ping_msg(int64_t token, unsigned char *buf);
int build_format_msg(AppState *state, int32_t sink_id, int32_t source_id, int32_t salt, unsigned char *buf);
int build_data_msg(int32_t sink_id, int32_t source_id, int32_t salt, int32_t seq, const unsigned char *data, int data_len, unsigned char *buf);
int build_source_ping_msg(int32_t sink_id, int32_t source_id, unsigned char *buf);

int parse_peer_join(const unsigned char *buf, int len, char *name, char *ip, int *port, int64_t *token);
int parse_peer_leave(const unsigned char *buf, int len, char *name);
int discover_public_ip(AppState *state, struct sockaddr_in *srv);

void peer_list_init(PeerList *list);
void peer_list_destroy(PeerList *list);
void peer_list_compact(PeerList *list);
Peer *find_peer_by_addr_locked(AppState *state, struct sockaddr_in *addr);
Peer *add_peer_locked(AppState *state, const char *name, const char *ip, int port, int64_t token);
void remove_peer_locked(AppState *state, const char *name);
int32_t generate_salt(Peer *p);
void send_invite_to_peer(AppState *state, const Peer *p);
void send_format_to_peer(AppState *state, const Peer *p);
void send_uninvite_to_peer(AppState *state, const Peer *p);
void send_source_ping(AppState *state, const Peer *p);

void ring_init(AudioRing *r, int channels, bool backpressure);
void ring_destroy(AudioRing *r);
void ring_signal_done(AudioRing *r);
int ring_available(AudioRing *r);
void ring_drain(AudioRing *r);
void ring_put_float(AudioRing *r, const float *data, int samples);
int ring_get_float(AudioRing *r, float *out, int samples);
void ring_shutdown(AudioRing *r);

int init_opus(AppState *state);
void compute_opus_mapping(int channels, bool joint_stereo, int *streams,
                          int *coupled_streams, unsigned char *mapping);
int init_opus_decoder_ex(OpusMSDecoder **decoder, int rate, int channels,
                        int streams, int coupled_streams,
                        const unsigned char *mapping);
int init_opus_decoder(AppState *state);
int opus_decode_to_s16(OpusMSDecoder *decoder, const unsigned char *data, int data_len,
                       int16_t *s16, float *pcm, int channels);
#ifdef ALSA
int init_alsa(AppState *state);
int alsa_list_devices(void);
#endif
#ifdef _WIN32
int wasapi_list_devices(void);
int wasapi_init_capture(AppState *state);
#endif

void *stdin_reader_thread(void *arg);
#ifdef _WIN32
void *wasapi_reader_thread(void *arg);
#endif
void *audio_thread(void *arg);

#define OGG_PRESKIP 312
#define OGG_SAMPLE_RATE 48000

typedef struct {
    ogg_stream_state os;
    int channels;
    uint64_t granule;
    uint32_t packetno;
    unsigned char last_pkt[4096];
    uint32_t last_len;
    uint64_t last_granule;
    uint32_t last_pktno;
    int have_last;
    int started;
} OggWriter;

void ogg_writer_init(OggWriter *w, int channels, const char *vendor, FILE *out);
void ogg_writer_packet(OggWriter *w, const unsigned char *data, int len, FILE *out);
void ogg_writer_finish(OggWriter *w, FILE *out);

#endif
// EOF
