#include "trx.h"
#include "rtty.h"
#include "snd.h"
#include "baudot.h"
#include "parity.h"
#include "prefs.h"

static inline double nco(struct rtty *s, double freq)
{
	s->phaseacc += 2.0 * M_PI * freq / SampleRate;

	if (s->phaseacc > M_PI)
		s->phaseacc -= 2.0 * M_PI;

	return cos(s->phaseacc);
}

static void send_symbol(struct trx *trx, int symbol)
{
	struct rtty *s = (struct rtty *) trx->modem;
	float buf[MaxSymLen];
	double freq;
	int i, rev;

	rev = (trx->reverse != 0) ^ (s->reverse != 0);

	if (rev)
		symbol = !symbol;

	if (symbol)
		freq = trx->frequency;
	else
		freq = trx->frequency + s->shift;

	freq += prefs.txoffset;

	for (i = 0; i < s->symbollen; i++)
		buf[i] = nco(s, freq);

	sound_write(buf, s->symbollen);
}

static void send_stop(struct trx *trx)
{
	struct rtty *s = (struct rtty *) trx->modem;
	float buf[MaxSymLen];
	double freq;
	int i, rev;

	rev = (trx->reverse != 0) ^ (s->reverse != 0);

	if (rev)
		freq = trx->frequency + s->shift;
	else
		freq = trx->frequency;

	freq += prefs.txoffset;

	for (i = 0; i < s->stoplen; i++)
		buf[i] = nco(s, freq);

	sound_write(buf, s->stoplen);
}

static void send_char(struct trx *trx, int c)
{
	struct rtty *s = (struct rtty *) trx->modem;
	int i, j;

	if (s->nbits == 5) {
		if (c == BAUDOT_LETS)
			c = 0x1F;
		if (c == BAUDOT_FIGS)
			c = 0x1B;
	}

	/* start bit */
	send_symbol(trx, 0);

	/* data bits */
	for (i = 0; i < s->nbits; i++) {
		j = (s->msb) ? (s->nbits - 1 - i) : i;
		send_symbol(trx, (c >> j) & 1);
	}

	/* parity bit */
	if (s->parity != PARITY_NONE)
		send_symbol(trx, parity(c, s->nbits, s->parity));

	/* stop bit(s) */
	send_stop(trx);

	if (s->nbits == 5)
		c = baudot_dec(&s->rxmode, c);

	if (c != 0)
		trx_put_echo_char(c);
}

static void send_idle(struct trx *trx)
{
	struct rtty *s = (struct rtty *) trx->modem;

	if (s->nbits == 5)
		send_char(trx, BAUDOT_LETS);
	else
		send_char(trx, 0);
}

int rtty_txprocess(struct trx *trx)
{
	struct rtty *s = (struct rtty *) trx->modem;
	int c;

	if (trx->tune) {
		trx->tune = 0;
		send_symbol(trx, 1);
		return 0;
	}

	if (s->preamble > 0) {
		send_symbol(trx, 1);
		s->preamble--;
		return 0;
	}

	c = trx_get_tx_char();

	/* TX buffer empty */
	if (c == -1) {
		/* stop if requested to... */
		if (trx->stopflag)
			return -1;

		/* send idle character */
		send_idle(trx);
		s->txmode = BAUDOT_LETS;
		return 0;
	}

	if (s->nbits != 5) {
		send_char(trx, c);
		return 0;
	}

	/* unshift-on-space */
	if (c == ' ') {
		send_char(trx, BAUDOT_LETS);
		send_char(trx, 0x04);
		s->txmode = BAUDOT_LETS;
		return 0;
	}

	if ((c = baudot_enc(c)) < 0)
		return 0;

	/* switch case if necessary */
	if ((c & s->txmode) == 0) {
		if (s->txmode == BAUDOT_FIGS) {
			send_char(trx, BAUDOT_LETS);
			s->txmode = BAUDOT_LETS;
		} else {
			send_char(trx, BAUDOT_FIGS);
			s->txmode = BAUDOT_FIGS;
		}
	}

	send_char(trx, c & 0x1F);

	return 0;
}
