#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <CSTAapdu.h>
#include <CSTAServices.h>
#include <sys/types.h>
#include <malloc.h>
#include <CSTAEventReportArgument.h>
#include <MonitorStartArgument.h>

int write_out(const void *buffer, size_t size, void *app_key);

typedef struct buffer_s {
	char	*buf;
	int	len;
} buffer_t;

/* Pakiet (prawdopodobnie) resetujący interfejs USB centralki */
char reset_pkt[] = { 0x00, 0x27, 0x00, 0x25, 0x60, 0x23, 0x80, 0x02, 
		     0x07, 0x80, 0xa1, 0x07, 0x06, 0x05, 0x2b, 0x0c, 
		     0x00, 0x81, 0x5a, 0xbe, 0x14, 0x28, 0x12, 0x06, 
		     0x07, 0x2b, 0x0c, 0x00, 0x82, 0x1d, 0x81, 0x48, 
		     0xa0, 0x07, 0xa0, 0x05, 0x03, 0x03, 0x00, 0x08, 
		     0x00 };

FILE *fp;
int setup = 1;

#define	READ	0
#define WRITE	1

void timestamp(int dir)
{
	time_t	t;
	struct tm *l;
	
	t = time(NULL);
	l = localtime(&t);
	printf("\n[%4d.%02d.%02d %02d:%02d:%02d] ", l->tm_year+1900, l->tm_mon+1, l->tm_mday, l->tm_hour, l->tm_min, l->tm_sec);

	if (dir == READ)
		printf("---- PBX -> PC ----\n");
	else
		printf("---- PC -> PBX ----\n");
}

void pktdump(char *buf, int len)
{
	int	i, j;
	char	id[256], *cp, type, tmp[256], *c, *orig;
	long	seq, l, origlen;
	CSTAapdu_t *apdu = 0; /* Type to decode */
	RORSapdu_t *rors;
	asn_dec_rval_t rval; /* Decoder return value	*/
	size_t size;		 /* Number of bytes read	*/
	buffer_t *out;
	
	buf += 4;
	len -= 4;
	timestamp(READ);
 
	/* Decode the input buffer */
	rval = ber_decode(0, &asn_DEF_CSTAapdu,	(void **)&apdu, buf, len);
	if(rval.code != RC_OK)
		fprintf(stderr, "Broken encoding at byte %ld\n", (long)rval.consumed);

	switch (apdu->present) {
		case CSTAapdu_PR_svcRequest:
			printf("invokeID %d, serviceID %d\n", apdu->choice.svcRequest.invokeID, apdu->choice.svcRequest.serviceID);
			switch (apdu->choice.svcRequest.serviceID) {
				case 211:
					printf("Keepalive\n");
					/* Przed wysłaniem setup_pkt nie należy wysyłać keepalive */
					if (setup) return;
					
					rors = calloc(1, sizeof(RORSapdu_t));
					rors->invokeID = apdu->choice.svcRequest.invokeID;
					rors->result.serviceID = apdu->choice.svcRequest.serviceID;
					
					out = calloc(1, sizeof(buffer_t));
					out->buf = malloc(1024);
					der_encode(&asn_DEF_RORSapdu, rors, write_out, out);
					
					timestamp(WRITE);
					xer_fprint(stdout, &asn_DEF_RORSapdu, rors);

					memmove(out->buf+4, out->buf, out->len);
					out->buf[0] = 0x00;
					out->buf[1] = out->len + 2;
					out->buf[2] = 0x00;
					out->buf[3] = out->len;
					out->len += 4;
			
					fwrite(out->buf, 1, out->len, fp);
					free(out->buf);
					break;
				case CSTAServices_cSTAeventReportSID:
					parseEventReport(apdu->choice.svcRequest.serviceArgs.buf, apdu->choice.svcRequest.serviceArgs.size);
					break;
				default:			
					printf("Unknown svcRequest:\n");
					dump(apdu->choice.svcRequest.serviceArgs.buf, apdu->choice.svcRequest.serviceArgs.size);
					break;
			}
			break;
		case CSTAapdu_PR_svcResult:
		case CSTAapdu_PR_svcError:
		case CSTAapdu_PR_svcReject:
		default:
			xer_fprint(stdout, &asn_DEF_CSTAapdu, apdu);
			break;
	}
	fflush(stdout);
}

void read_pkt()
{
	unsigned char buf[64], sum[1024];
	int	ret, ofs, done = 0, i;
	time_t	t1, t2;

	t1 = time(NULL);
	while (!done) {
		t2 = time(NULL);
		if (t2 > t1+5) break;		
	
		do {
			ret = fread(buf, 1, 64, fp);
		} while (ret != 64);

		switch (buf[0]) {
			case 0x00:
				pktdump(buf, buf[1]+2);
				done = 1;
				break;
			case 0x01:
				/* Początek nowego bufora */
				memcpy(sum, buf+2, buf[1]+2);	/* Nie wiadomo dlaczego długość w buf[1] jest mniejsza o 2 */
				ofs = buf[1]+2;
				break;
			case 0x02:
				/* Kontynuacja */
				memcpy(sum+ofs, buf+2, buf[1]);
				ofs += buf[1];
				break;
			case 0x03:
				/* Koniec bufora */
				memcpy(sum+ofs, buf+2, buf[1]);
				ofs += buf[1];
				pktdump(sum, sum[1]+2);
				done = 1;
				break;
		}
	}
}

int write_out(const void *buffer, size_t size, void *app_key) 
{
	buffer_t *dest = app_key;
	u_char *buf = buffer;
	int i;

	for (i = 0; i < size; i++)
		dest->buf[dest->len+i] = buf[i];
	dest->len += size;
	return 0;
}

int sendID = 1;
                       
void start_monitor(FILE *fp, char *numer)
{
	ROIVapdu_t *pdu;
	MonitorStartArgument_t *rep;
	int i;
	buffer_t *buf;
	
	rep = calloc(1, sizeof(MonitorStartArgument_t));
	rep->monitorObject.present = CSTAObject_PR_deviceObject;
	rep->monitorObject.choice.deviceObject.deviceIdentifier.present = DeviceID__deviceIdentifier_PR_dialingNumber;
	rep->monitorObject.choice.deviceObject.deviceIdentifier.choice.dialingNumber.buf = numer;
	rep->monitorObject.choice.deviceObject.deviceIdentifier.choice.dialingNumber.size = strlen(numer);

	pdu = calloc(1, sizeof(ROIVapdu_t));
	pdu->invokeID = sendID++;
	pdu->serviceID = CSTAServices_monitorStartSID;
	pdu->serviceArgs.buf = malloc(1024);

	buf = calloc(1, sizeof(buffer_t));
	buf->buf = pdu->serviceArgs.buf;
	der_encode(&asn_DEF_MonitorStartArgument, rep, write_out, buf);
	pdu->serviceArgs.size = buf->len;

	buf->len = 0;
	buf->buf = malloc(1024);
	der_encode(&asn_DEF_ROIVapdu, pdu, write_out, buf);

	timestamp(WRITE);
	xer_fprint(stdout, &asn_DEF_MonitorStartArgument, rep);
	
	/* Nagłówek */
	memmove(buf->buf+4, buf->buf, buf->len);
	buf->buf[0] = 0x00;
	buf->buf[1] = buf->len + 2;
	buf->buf[2] = 0x00;
	buf->buf[3] = buf->len;
	buf->len += 4;

	fwrite(buf->buf, 1, buf->len, fp);
	free(buf->buf);
	free(pdu->serviceArgs.buf);
	read_pkt();
}

void dump(char *buf, int len)
{
	int i, j;
	
	for (i = 0; i < len; i+=16) {
		for (j = 0; j < 16; j++)
			if (i+j < len)
				printf("0x%02x ", (unsigned char)buf[i+j]);
			else
				printf("     ");
		for (j = 0; j < 16; j++)
			if (i+j < len) {
				if (buf[i+j] >= 0x20)
					printf("%c", (unsigned char)buf[i+j]);
				else
					printf(".");
			}
		printf("\n");
	}
}

void parseEventReport(char *data, int length)
{
	CSTAEventReportArgument_t *rep = 0; /* Type to decode */
	asn_dec_rval_t rval; /* Decoder return value	*/
	size_t size;		 /* Number of bytes read	*/
 
	/* Decode the input buffer */
	rval = ber_decode(0, &asn_DEF_CSTAEventReportArgument,
		(void **)&rep, data, length);
	if(rval.code != RC_OK)
		fprintf(stderr, "Broken encoding at byte %ld\n", (long)rval.consumed);
 
	/* Print the decoded Rectangle type as XML */
	xer_fprint(stdout, &asn_DEF_CSTAEventReportArgument, rep);
 
	return 0; /* Decoding finished successfully */
}

void flushread(FILE *fp)
{
	char buf[1024];
	int	ret;
	
	/* Wyczyść bufor */
	fcntl(fileno(fp), F_SETFL, fcntl(fileno(fp), F_GETFL) | O_NONBLOCK);
	do {
		ret = fread(buf, 1, 128, fp);
		printf("flushread %d\n", ret);
	} while (ret > 0);
	fcntl(fileno(fp), F_SETFL, fcntl(fileno(fp), F_GETFL) & ~O_NONBLOCK);
}

main()
{
	char buf[1024];
	int	ret, i;

	fp = fopen("/dev/usb/skel0", "rb+");

	flushread(fp);

	/* Inicjalizacja */
	fwrite(reset_pkt, 1, sizeof(reset_pkt), fp);
	read_pkt();
	read_pkt();
	start_monitor(fp, "101");
	start_monitor(fp, "102");
	start_monitor(fp, "103");
	start_monitor(fp, "104");

	setup = 0;
	while(1) read_pkt();
}
