/*
 * Gnophone: A client for the Asterisk PBX
 *
 * Copyright (C) 2000, Linux Support Services, Inc.
 *
 * Written by Mark Spencer
 *
 * Linux/UNIX version distributed under the terms of
 * the GNU General Public License
 *
 * audio-zaptel.c: Linux Telephony Driver for the TigerJet card
 * Coded by Rob Flynn
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/zaptel.h>
#include <zap.h>
#include <tonezone.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include "phonecore.h"
#include "audio.h"
#include <iax/iax-client.h>
#include "lin2mu.h"

#define MAX_DEVS 256

#define BUFFERLEN 204
#define BUFFERS 20

#define CLIP 32635 /* These are used for the mulaw/linear16 stuff */
#define BIAS 0x84

#define ZT_STATE_NONE	   0x0000
#define ZT_STATE_ONHOOK    0x0000
#define ZT_STATE_OFFHOOK   0x0001
#define ZT_STATE_RINGING   0x0002
#define ZT_STATE_RING      0x0004
#define ZT_STATE_BUSY      0x0008
#define ZT_STATE_DIALING   0x0010

struct zt_buffer {
	int full;
	char buf[BUFFERLEN];
};

struct zt_pvt  {
	/* State */
	int state;
	
	/* Real fd, used for writing and pre-reading */
	int writefd;
	
	/* Filename of phone device */
	char fn[80];
	
	/* Where in our circle we're writing to */
	int inbuf;

	/* Where in our circle we're reading from */
	int outbuf;
	
	/* How far into our current "in" buffer we've filled */
	int buflen;

	/* Buffer playback id */
	int bpid;

	/* Dialtone id */
	int dtid;

	/* Dial tone position */
	int dtpos;

	/* File descriptors */
	int fds[2];

	/* ZAP ZAP ZAP ZAP WHOOOSH */
	ZAP *z;

	unsigned char buf[BUFFERLEN];
	int bufferlen;
};

static int zt_open(struct audio_channel *ac);
static int zt_deactivate(struct audio_channel *ac);
static int zt_close(struct audio_channel *ac);
static int zt_activate(struct audio_channel *ac);
static int zt_configure(struct audio_channel *ac);
static int zt_sendaudio(struct audio_channel *ac, int format, void *data, int len);
static int zt_readaudio(struct audio_channel *ac, int format, void *buffer, int *len);
static int zt_busy(struct audio_channel *ac);

/*
static int zt_setspeed(struct audio_channel *ac, int speed);
static int zt_flush(struct audio_channel *c);
*/

static char zt_driver[] = "Gnophone/zaptel";

/* This is a very very bad hack. I am a very bad man. */
extern int fullmulaw[];

#if 0
static unsigned char linear2ulaw(short sample) 
{

	static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
   		 		   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};

	int sign, exponent, mantissa;
	unsigned char ulawbyte;

	/* Get the sample into sign-magnitude. */
	sign = (sample >> 8) & 0x80;          /* set aside the sign */
	if (sign != 0) sample = -sample;              /* get magnitude */
	if (sample > CLIP) sample = CLIP;             /* clip the magnitude */

	/* Convert from 16 bit linear to ulaw. */
	sample = sample + BIAS;
	exponent = exp_lut[(sample >> 7) & 0xFF];
	mantissa = (sample >> (exponent + 3)) & 0x0F;
	ulawbyte = ~(sign | (exponent << 4) | mantissa);
#ifdef ZEROTRAP
	if (ulawbyte == 0) ulawbyte = 0x02;   /* optional CCITT trap */
#endif

	return(ulawbyte);
}
#endif

static int zt_play_digit(struct audio_channel *ac, char digit)
{
	ZT_DIAL_OPERATION zo;

	struct zt_pvt *pvt = ac->pvt;
	int res;

	if ( (pvt->state & ZT_STATE_RINGING) || (pvt->state & ZT_STATE_ONHOOK))
		return -1;

	zo.op = ZT_DIAL_OP_APPEND;
	zo.dialstr[0] = 'T';
	zo.dialstr[1] = digit;
	zo.dialstr[2] = 0;

	if ((res = ioctl(zap_fd(pvt->z), ZT_DIAL, &zo)))
		fprintf(stderr, "There was an error dialing.\n");
	else
		pvt->state |= ZT_STATE_DIALING;

	return res;

}

static int zt_busy(struct audio_channel *ac)
{
	struct zt_pvt *pvt = ac->pvt;
	int res;

	/* I really don't see how this could happen, but just in case... */
	if (pvt->state & ZT_STATE_OFFHOOK) {
		pvt->state |= ZT_STATE_BUSY;
		res = tone_zone_play_tone(zap_fd(pvt->z), ZT_TONE_BUSY);
	}
	
	return 0;
}

static int zt_readaudio(struct audio_channel *ac, int format, void *buffer, int *len)
{
	int x;
	int res;
	int bytes=0;
	short *stuff = buffer;
	struct zt_pvt *pvt = ac->pvt;
	
	if (pvt->bufferlen < BUFFERLEN)  {
		/* Send other half */
		for (x = pvt->bufferlen; (x<BUFFERLEN) && (bytes < *len/2); x++)  {
			stuff[bytes] = fullmulaw[pvt->buf[x]];
			bytes++;
		}
		
		*len = bytes * 2;
		pvt->bufferlen += bytes;

		return 0;
	}
				
	res = zap_recchunk(pvt->z, pvt->buf, BUFFERLEN, ZAP_DTMFINT);

	if (res < 0)
		printf("DAMN!\n");
	else if (res == BUFFERLEN) {
	 	for (x = 0; bytes < *len/2; x++)  {
			stuff[bytes] = fullmulaw[pvt->buf[x]];
			bytes++;
		}
		
		pvt->bufferlen = x;
		*len = bytes * 2;	
	} else {
		/* Well, if we're here then we probably have DTMF */
		if (zap_dtmfwaiting(pvt->z) && !strlen(zap_dtmfbuf(pvt->z)) && (!(pvt->state & ZT_STATE_DIALING))) { 
			/* Get the DTMF digits */
			zap_getdtmf(pvt->z, 1, NULL, 0, 1, 1, 0);

			/* Tell our GUI to dial the number */
			pc_dial(SOURCE_PC, zap_dtmfbuf(pvt->z)[0]);

			/* We also don't want a dial tone. It confuses people :-) */
			res = tone_zone_play_tone(zap_fd(pvt->z), ZT_TONE_STOP);
		}

		zap_clrdtmfn(pvt->z);
	}
	

	return 0;
}

static int zt_sendaudio(struct audio_channel *ac, int format, void *data, int len)
{
	short *buf = data;
	unsigned char tmp[160];
	int x, res;
	struct zt_pvt *pvt = ac->pvt;

	for (x = 0; x < len/2; x++)
		tmp[x] =linear2ulaw(buf[x]);
	
	res = write(zap_fd(pvt->z), tmp, len/2);
	
	if (res != len/2) 
		fprintf(stderr, "Error, wrote %d of %d (%s)\n", res, len/2, strerror(errno));

	return 0;
}

static int zt_exception(struct audio_channel *ac)
{
	int x;
	int res;
	struct zt_pvt *pvt = ac->pvt;
	
	x = 0;
	res = ioctl(zap_fd(pvt->z), ZT_GETEVENT, &x);
	
	/* Looks like they took the phone off hook. */
	if (x == ZT_EVENT_RINGOFFHOOK) {
		
		printf("OFF HOOK.\n");

		/* I don't see any problem with this. Maybe later.  I guess I found a problem
		* with it. Oh well, its fixed now =) */
		pvt->state |= ZT_STATE_OFFHOOK;

		/* Was it ringing? */
		if (pvt->state & ZT_STATE_RING) {
			/* Looks like it. Lets make it STOP */ 
			x = ZT_RINGOFF;
			ioctl(zap_fd(pvt->z), ZT_HOOK, &x);

			/* We're off hook, too :-) */
			x = ZT_OFFHOOK;
			ioctl(zap_fd(pvt->z), ZT_HOOK, &x);

			/* Turn off the ringing in our state variable. */
			pvt->state ^= ZT_STATE_RING;
			
			/* MOOOOMMM! PHOOONEEE!!! */
			pc_offhook(SOURCE_PC, -1);
		} else {
			/* Otherwise, I don't think so. Let's play a dial tone */
			res = tone_zone_play_tone(zap_fd(pvt->z), ZT_TONE_DIALTONE);

			pc_dial_tbd(-1);
		}

	} else if (x == ZT_EVENT_DIALCOMPLETE) {
		/* Well, we can reset our dialing flag, I think */
		pvt->state ^= ZT_STATE_DIALING;
			
	} else if (x == ZT_EVENT_ONHOOK) {
	
		printf("ON HOOK.\n");

		/* I done told you, we don't want none */
		pc_onhook(SOURCE_PC, -1);

		/* Make sure we reset our hook state */
		x = ZT_ONHOOK;
		res = ioctl(zap_fd(pvt->z), ZT_HOOK, &x);

		res = tone_zone_play_tone(zap_fd(pvt->z), ZT_TONE_STOP);

		pvt->state = ZT_STATE_ONHOOK;
	}
	
	else if (x == ZT_EVENT_WINKFLASH)
		printf("GOT A WINK OR FLASH.\n");
	else {
		printf("SOMETHING ELSE [%d].\n", x);
	}

	return 0;
}

static int zt_activate(struct audio_channel *ac)
{
	/* Should I even do anything here? */
	return 0;
}

static int zt_configure(struct audio_channel *ac)
{
	/* Should I even do anything here? */
	return 0;
}

static int zt_deactivate(struct audio_channel *ac)
{
	struct zt_pvt *pvt = ac->pvt;
	int res;

	if (pvt->state & ZT_STATE_OFFHOOK) {
		/* If we get hung up on, we should play the congestion tone */
		res = tone_zone_play_tone(zap_fd(pvt->z), ZT_TONE_CONGESTION);
	}
	
	if (pvt->state & ZT_STATE_RING) {
		/* Stop playing the ring sound on the phone, just incase */
		tone_zone_play_tone(zap_fd(pvt->z), ZT_TONE_STOP);
		pvt->state ^= ZT_STATE_RING;

		/* Looks like it. Lets make it STOP */ 
		res = ZT_RINGOFF;
		ioctl(zap_fd(pvt->z), ZT_HOOK, &res);

	}

	if (!(pvt->state & ZT_STATE_OFFHOOK)) {	
		/* Make sure we reset our hook state */
		res = ZT_ONHOOK;
		ioctl(zap_fd(pvt->z), ZT_HOOK, &res);
	}

	return 0;
}

static int zt_ring(struct audio_channel *ac)
{
	struct zt_pvt *pvt = ac->pvt;
	int x;
	int res;

	pvt->state |= ZT_STATE_RING;

	do {
		/* Let's crank up this mamma jamma */
		x = ZT_RING;
		res = ioctl(zap_fd(pvt->z), ZT_HOOK, &x);

		if (res) {
			switch(errno) {
				case EBUSY:
				case EINTR:
					usleep(10000);
					continue;
				case EINPROGRESS:
					res = 0;
					break;
				default:
					res = 0;
			}
		}

	} while (res);

	return res;
}

static int zt_close(struct audio_channel *ac)
{
	struct zt_pvt *pvt = ac->pvt;

	zap_close(pvt->z);
	ac->fd = -1;

	return 0;
}

static int zt_open(struct audio_channel *ac)
{
	struct zt_pvt *pvt = ac->pvt;
	int x, res;
	ZT_BUFFERINFO bi;
	if (ac->fd > -1) {
		fprintf(stderr, "Channel %s already open?\n", ac->name);
		return 0;
	}
	pvt->z = zap_open(ac->name, 1);
	if (!pvt->z) {
		fprintf(stderr, "Unable to open zap device '%s': %s\n", ac->name, strerror(errno));
		return -1;
	}

	res = ioctl(zap_fd(pvt->z), ZT_GET_BUFINFO, &bi);
	
	if (!res) {
		bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
		bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
		bi.numbufs = 4;
		res = ioctl(zap_fd(pvt->z), ZT_SET_BUFINFO, &bi);
	}
	ac->fd = zap_fd(pvt->z);

	pvt->bufferlen = BUFFERLEN;
	
	x = ZT_ONHOOK;
	res = ioctl(zap_fd(pvt->z), ZT_HOOK, &x);

	return 0;
}

static int zt_ringing(struct audio_channel *ac)
{
	struct zt_pvt *pvt = ac->pvt;
	int res;
	
	res = tone_zone_play_tone(zap_fd(pvt->z), ZT_TONE_RINGTONE);

	return res;
}

static struct audio_channel *zt_channel_new(ZAP *z, char *filename)
{
	struct audio_channel *ac;
	struct zt_pvt *pvt = (struct zt_pvt *)malloc(sizeof(struct zt_pvt));

	ac = audio_new();
	pvt = (struct zt_pvt *)malloc(sizeof(struct zt_pvt));
	bzero(pvt, sizeof(struct zt_pvt));

	if (ac) {
		pvt->dtid = -1;
		pvt->bpid = -1;

		pvt->state = ZT_STATE_ONHOOK;

		snprintf(ac->name, sizeof(ac->name), "%s", filename);
		ac->priority = 100;
		ac->driver = zt_driver;
		ac->open = zt_open;
		ac->close = zt_close;
		ac->play_digit = zt_play_digit;
		ac->activate = zt_activate;
		ac->ring = zt_ring;
		ac->busy = zt_busy;
		ac->hz = 8000;	
/*		ac->fastbusy = zt_busy; */
		ac->ringing = zt_ringing; 
		ac->deactivate = zt_deactivate;
		ac->configure = zt_configure;
		ac->sendaudio = zt_sendaudio;
		ac->readaudio = zt_readaudio;
		ac->exception = zt_exception;
/*		ac->flush = zt_flush; */
/*		ac->setspeed = zt_setspeed; */
		ac->sformats = AST_FORMAT_SLINEAR;
		ac->cananswer = 1;
		ac->duplex = 1;
/*		ac->simduplex = NULL */
/*		ac->echocancelled = 0; */
		pvt->z = z;
		ac->fd = zap_fd(z);
		ac->pvt = pvt;
	} 

	return ac;
}

int init()
{
	char fn[80];
	struct audio_channel *ac = NULL;
	int x;
	ZAP *z;

	/* Let's check for a valid zaptel device */
	for (x = 0; x < MAX_DEVS; x++) {
		sprintf(fn, "/dev/zap/%d", x);

		z = zap_open(fn, 1);

		if (z) {
			ac = zt_channel_new(z, fn);
			zt_close(ac);


			if (ac)
				audio_register_channel(ac);

			break;
		}

	}

	return 0;
}

char *key() 
{
	return KEY;
}

char *name()
{
	return "zaptel";
}
