/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Echo application -- play back what you hear to evaluate latency
 * 
 * Copyright (C) 1999, Mark Spencer
 *
 * Mark Spencer <markster@linux-support.net>
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License
 */

#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "isdn_lib.h"
#include "chan_misdn.h"

static char *tdesc = "Simple V.110 Application";

static char *app = "V110";

static char *synopsis = "Make V.110 connection.";

static char *descrip = 
	"  V110():  Make V.110 connection.\n";

STANDARD_LOCAL_USER;

LOCAL_USER_DECL;

static unsigned char V110_OnMatrix_9600[] =
{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd,
 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd};

static unsigned char V110_OffMatrix_9600[] =
{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

struct v110_state {
	unsigned char vframe_in[10];
	unsigned vframe_in_len;
	unsigned char ibuf[8192];
	unsigned ibuflen;
	unsigned nextbit;
	int ofd;
	int cts, rts;
};

void v110_input_frame_x4(struct v110_state *vs, struct ast_frame *);

static int echo_v110(struct ast_channel *chan, void *data)
{
	misdn_bchannel_t *bc;
	int res=-1;
	struct localuser *u;
	struct ast_frame *f;
	int syncframes = 0;
	struct v110_state *vs = malloc(sizeof(*vs));

	struct ast_frame send_f = {
		.frametype = AST_FRAME_VOICE,
		.datalen = 40,
		.samples = 40,
	};

	bc = misdn_get_bc_te_by_ast(chan);
	if (!bc) {
		ast_log(LOG_DEBUG, "V.110: Not mISDN channel\n");
		return 0;
	}
	if (!bc->digital || bc->user1 != 1 || bc->async != 1) {
		ast_log(LOG_DEBUG, "V.110: Not an async V.110 call\n");
		return 0;
	}
	if (bc->urate != 8) {
		/* We only handle 9600 for now. In fact most mobile telcos
		   only seem to ask for 9600 even when they're doing more */
		ast_log(LOG_NOTICE, "V.110 call at rate %d not supported\n",
			bc->urate);
		return -1;
	}

	LOCAL_USER_ADD(u);

	/* Set transparent mode before we answer */
	ast_log(LOG_NOTICE, "Accepting V.110 call at rate %d\n", bc->urate);
	bc->digital_trans = 1;
	ast_answer(chan);
	ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
	ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
	send_f.data = V110_OffMatrix_9600;
	send_f.subclass = chan->readformat;
#if 0
	ast_write(chan, &send_f);
	ast_write(chan, &send_f);
	ast_write(chan, &send_f);
	ast_write(chan, &send_f);
	ast_write(chan, &send_f);
	send_f.data = V110_OnMatrix_9600;
	ast_write(chan, &send_f);
#endif
	vs->vframe_in_len = 0;
	vs->ibuflen = 0;
	vs->nextbit = 0;
	vs->ofd = open("/tmp/v110out", O_WRONLY|O_CREAT|O_TRUNC, 0644);
		/* Do our thing here */
	while(ast_waitfor(chan, -1) > -1) {
		f = ast_read(chan);
		if (!f)
			break;
		f->delivery.tv_sec = 0;
		f->delivery.tv_usec = 0;
		if (f->frametype == AST_FRAME_VOICE) {
			if (syncframes) {
				syncframes--;
				if (!syncframes)
					send_f.data = V110_OnMatrix_9600;
			}
			v110_input_frame_x4(vs, f);
			if (ast_write(chan, f)) 
				break;
		} else if (f->frametype == AST_FRAME_VIDEO) {
			if (ast_write(chan, f)) 
				break;
		} else if (f->frametype == AST_FRAME_DTMF) {
			if (f->subclass == '#') {
				res = 0;
				break;
			} else
				if (ast_write(chan, f))
					break;
		}
		ast_frfree(f);
	}
	LOCAL_USER_REMOVE(u);
	return res;
}

int unload_module(void)
{
	STANDARD_HANGUP_LOCALUSERS;
	return ast_unregister_application(app);
}

void v110_process_frame(struct v110_state *vs) 
{
	int bad;
	int octet, bit;

	/* Check that line 5 (E-bits) starts '1011'. */
	if ((vs->vframe_in[5] & 0xf0) != 0xb0) 
		return;

	/* Check that each other octet starts with '1' */
	if (!(vs->vframe_in[1] & vs->vframe_in[2] & vs->vframe_in[3] & 
	      vs->vframe_in[4] & vs->vframe_in[6] & vs->vframe_in[7] & 
	      vs->vframe_in[8] & vs->vframe_in[9] & 0x80))
		return;

	if (vs->vframe_in[1] != 0xfe ||
	    vs->vframe_in[2] != 0xfe ||
	    vs->vframe_in[3] != 0xfe ||
	    vs->vframe_in[4] != 0xfe ||
	    vs->vframe_in[6] != 0xfe ||
	    vs->vframe_in[7] != 0xfe ||
	    vs->vframe_in[8] != 0xfe ||
	    vs->vframe_in[9] != 0xfe)
		ast_log(LOG_NOTICE, "Data frame : %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", 
			vs->vframe_in[ 0] ,
			vs->vframe_in[ 1] ,
			vs->vframe_in[ 2] ,
			vs->vframe_in[ 3] ,
			vs->vframe_in[ 4] ,
			vs->vframe_in[ 5] ,
			vs->vframe_in[ 6] ,
			vs->vframe_in[ 7] ,
			vs->vframe_in[ 8] ,
			vs->vframe_in[ 9] );
	return;

}

/* We don't handle multiple multiplexed channels. Nobody really does */
void v110_input_frame_x4(struct v110_state *vs, struct ast_frame *f)
{
	int datalen = f->datalen;
	unsigned char *frame_data = f->data;

	while (datalen) {
		if (vs->vframe_in_len < 4) {
			/* Find zero octet in buffer */
			if ( (*frame_data) & 3 ) {
				vs->vframe_in_len = 0;
				frame_data++;
				datalen--;
				continue;
			}
			/* Found a suitable byte. Add it. */
			if (++vs->vframe_in_len == 4)
				memset(vs->vframe_in, 0, 10);
			frame_data++;
			datalen--;
			continue;
		}
		/* Shift left by two, add the two new bits (reversed) */
		vs->vframe_in[vs->vframe_in_len/4] = 
			vs->vframe_in[vs->vframe_in_len/4] << 2 | 
			(((*frame_data) & 1) << 1) |
			(((*frame_data) & 2) >> 1);

		vs->vframe_in_len++;
		frame_data++;
		datalen--;

		if (vs->vframe_in_len == 40) {
			v110_process_frame(vs);
			vs->vframe_in_len = 0;
		}
	}
}

void v110_input_frame_x2(struct v110_state *vs, struct ast_frame *f)
{
	int datalen = f->datalen;
	unsigned char *frame_data = f->data;

	while (datalen) {
		if (vs->vframe_in_len < 2) {
			/* Find zero octet in buffer */
			if ( (*frame_data) & 7 ) {
				vs->vframe_in_len = 0;
				frame_data++;
				datalen--;
				continue;
			}
			/* Found a suitable byte. Add it. */
			if (++vs->vframe_in_len == 2)
				memset(vs->vframe_in, 0, 10);
			frame_data++;
			datalen--;
			continue;
		}
		/* Shift left by four, add the four new bits (reversed) */
		vs->vframe_in[vs->vframe_in_len/2] = 
			vs->vframe_in[vs->vframe_in_len/2] << 4 | 
			(((*frame_data) & 1) << 3) |
			(((*frame_data) & 2) << 1) |
			(((*frame_data) & 4) >> 1) |
			(((*frame_data) & 8) >> 3);

		vs->vframe_in_len++;
		frame_data++;
		datalen--;

		if (vs->vframe_in_len == 20) {
			v110_process_frame(vs);
			vs->vframe_in_len = 0;
		}
	}
}

void v110_input_frame_x1(struct v110_state *vs, struct ast_frame *f)
{
	int datalen = f->datalen;
	unsigned char *frame_data = f->data;

	while (datalen) {
		if (!vs->vframe_in_len) {
			/* Find zero octet in buffer */
			if ( (*frame_data)) {
				vs->vframe_in_len = 0;
				frame_data++;
				datalen--;
				continue;
			}
			/* Found a suitable byte. Add it. */
			vs->vframe_in_len++;
			memset(vs->vframe_in, 0, 10);
			frame_data++;
			datalen--;
			continue;
		}
		/* Reverse bits, add to v.110 frame */
		vs->vframe_in[vs->vframe_in_len] = 
			(((*frame_data) & 1) << 7) |
			(((*frame_data) & 2) << 5) |
			(((*frame_data) & 4) << 3) |
			(((*frame_data) & 8) << 1) |
			(((*frame_data) & 16) >> 1) |
			(((*frame_data) & 32) >> 3) |
			(((*frame_data) & 64) >> 5) |
			(((*frame_data) & 128) >> 7);

		vs->vframe_in_len++;
		frame_data++;
		datalen--;

		if (vs->vframe_in_len == 10) {
			v110_process_frame(vs);
			vs->vframe_in_len = 0;
		}
	}
}

int load_module(void)
{
	return ast_register_application(app, echo_v110, synopsis, descrip);
}

char *description(void)
{
	return tdesc;
}

int usecount(void)
{
	int res;
	STANDARD_USECOUNT(res);
	return res;
}

char *key()
{
	return ASTERISK_GPL_KEY;
}
