/*
    sdrc.c - SDR client
    Copyright (C) 2016 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"

#include "sdrc.h"

#include "rc.h"
#include "sdrd.h"

static int sdrc_send_packet(struct dsp *dsp, void *packet){
    int wr, err, pktlen;
	char errbuf[256];

	if (dsp->sock <= 0) return -1;

	pktlen = sizeof(struct sdrd_header) + ((struct sdrd_header *)packet)->len;
	wr = send(dsp->sock, (char*)packet, pktlen, 0);
	err = errno;
	if (wr <= 0) {
		printf("Send failed sock=%d err=%d %s\n", dsp->sock, err, z_sock_strerror());
		closesocket(dsp->sock);
		dsp->sock = -1;
	}
	return wr;
}


int sdrc_open(struct dsp *dsp, int rec){
	int ret;
	struct sockaddr_in sin;
	struct hostent *he;
	struct sdrd_setup *packet;
	
    
    dbg("sdrc_open(%s)\n", rec?"record":"playback");

	if (dsp->rdbuf) zbinbuf_free(dsp->rdbuf);
	dsp->rdbuf = zbinbuf_init();
    
    dsp->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	// no nonblock
    
    memset(&sin, 0, sizeof(struct sockaddr_in));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SDRD_TCP_PORT);
	he = gethostbyname(cfg->sdr_remoterx);
	if (!he) goto err;
	memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length);

	ret = connect(dsp->sock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in));
	if (ret < 0) goto x;

    zg_free0(dsp->name);
    dsp->name = g_strdup_printf("sdrc://%s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
    
	dsp->samples = dsp->frames * dsp->channels;
	dsp->bytes = dsp->samples * sizeof(short);


	packet = g_new0(struct sdrd_setup, 1);
	packet->hdr.cmd = SDRD_CMD_SETUP;
	packet->hdr.len = sizeof(struct sdrd_setup) - sizeof(struct sdrd_header);
	packet->speed = dsp->speed;
	packet->channels = dsp->channels;
	packet->frames = dsp->frames;
	sdrc_send_packet(dsp, packet);
	g_free(packet);



	
    //log_addf("sdrc opened, rate=%d, bufsize=%df %ds %db", dsp->speed, dsp->frames, dsp->samples, dsp->bytes); 
    dbg("sdrc opened, rate=%d, bufsize=%df %ds %db\n", dsp->speed, dsp->frames, dsp->samples, dsp->bytes); 
    goto x;
    
err:;
    sdrc_close(dsp);

x:;
    return 0;
} 

int sdrc_close(struct dsp *dsp){
    dbg("sdrc_close()\n");
    dsp->reset(dsp);
	z_pipe_close(dsp->pipe[0]);
	z_pipe_close(dsp->pipe[1]);
	dsp->pipe_opened = 0;
	dsp->pipe_opened_for_play = 0;
	return 0;
}


int sdrc_write(struct dsp *dsp, void *data, int frames){
	/*int ret;
	int bytes = (frames * dsp->bytes) / dsp->frames;

	ret = z_pipe_write(dsp->pipe[1], data, bytes);
	if (ret > 0) ret = (ret * dsp->frames) / dsp->bytes;
	return ret;*/
	return -1;
}


int sdrc_read(struct dsp *dsp, void *adata, int frames){
	int ret;
	char buf[1028];
	int bytes = (frames * dsp->bytes) / dsp->frames;
	char *data = (char*)adata, *src;
	int datalen = 0, len;
	short *sh;

	while(datalen < bytes){
		fd_set fds;
		struct timeval tv = {0, 1000000};

		FD_ZERO(&fds);
		FD_SET(dsp->sock, &fds);
		ret = select(dsp->sock + 1, &fds, NULL, NULL, &tv);
		if (ret < 0) return ret;
		if (ret == 0) return -1; // timeout
		// must be dsp->sock, maybe no need to test FD_ISSET

		ret = recv(dsp->sock, buf, sizeof(buf), 0);
		sh = (short*)buf;
		dbg("recv(%d, len=%d) = %d (%d)   data=%d %d %d %d\n", dsp->sock, sizeof(buf), ret, z_sock_errno, sh[0], sh[1], sh[2], sh[3]);

		if (ret <= 0) return ret;

		zbinbuf_append_bin(dsp->rdbuf, buf, ret);

		while (datalen < bytes && dsp->rdbuf->len >= sizeof(struct sdrd_header)){
			struct sdrd_header *packet = (struct sdrd_header *)dsp->rdbuf->buf;
			if (dsp->rdbuf->len < sizeof(struct sdrd_header) + packet->len) break;

			switch (packet->cmd){
				case SDRD_CMD_DATA:
					len = packet->len;
					if (datalen + packet->len > bytes) 
						len = bytes - datalen; // some data will be lost
					src = (char *)packet;
					src += sizeof(struct sdrd_header);
					memcpy(data + datalen, src, len);
					datalen += len;
					zbinbuf_erase(dsp->rdbuf, 0, sizeof(struct sdrd_header) + packet->len);
					break;
			}
		}			
	}

	return frames;
}


int sdrc_reset(struct dsp *dsp){
	return 0;
}


int sdrc_sync(struct dsp *dsp){
	return 0;
}

#ifdef HAVE_SNDFILE
int sdrc_set_format(struct dsp *dsp, SF_INFO *sfinfo, int ret){
	dsp->speed = sfinfo->samplerate;
	dsp->channels = sfinfo->channels;
	dsp->frames = 0;
    return 0;
}    
#endif

int sdrc_set_sdr_format(struct dsp *dsp, int frames, int speed, int rec){
	dsp->speed = speed;
	dsp->channels = 2;
	dsp->frames = frames;

	if (dsp->speed != 0) dsp->period_time = frames * 1000 / dsp->speed;
	
    return 0;

}    


