/*
 *   Copyright 1992, 1993, 1994 John Melton (G0ORX/N6LYT)
 *              All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
	xweber.c

	Weber Photo Image Capture

	John Melton
	G0ORX, N6LYT

	4 Charlwoods Close
	Copthorne
	West Sussex
	RH10 3QZ
	England

	INTERNET:	g0orx@amsat.org
			n6lyt@amsat.org
			john@images.demon.co.uk
			J.D.Melton@slh0613.icl.wins.co.uk

	History:
	-------

	0.1	Initial version.
	0.2	Changed code to update line in file.
	0.3	Experimental SPECTRUM capture.
	0.4	Converted to Xaw.
*/


/*
#define DEBUG
#define USEDATA
#define SAVEDATA
*/

#define VERSION_STRING "(version 0.4 by g0orx/n6lyt/g4klx)"

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Command.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <linux/ax25.h>

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <termios.h>

#include "weber.h"

Display *dpy;

XtAppContext app_context;

typedef struct
{
	XFontStruct *button_font, *text_font;
}
Resources;

Resources  resources;

Widget toplevel, compwindow, quitbutton, datawindow;

char writebuf[100];

XtResource resource_list[] =
{
	{"buttonFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
		XtOffsetOf(Resources, button_font), XtRString, XtDefaultFont},
	{"textFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
		XtOffsetOf(Resources, text_font), XtRString, XtDefaultFont}
};

Arg shell_args[] =
{
	{XtNtitle,		(XtArgVal)NULL}
};

Arg form_args[] =
{
	{XtNdefaultDistance,	(XtArgVal)0}
};

Arg button_args[] =
{
	{XtNcallback,		(XtArgVal)NULL},
	{XtNlabel,		(XtArgVal)NULL},
	{XtNfromHoriz,		(XtArgVal)NULL},
	{XtNfont,		(XtArgVal)NULL},
	{XtNresize,		(XtArgVal)False},
	{XtNvertDistance,	(XtArgVal)6},
	{XtNhorizDistance,	(XtArgVal)8},
	{XtNtop,		XtChainTop},
	{XtNbottom,		XtChainTop},
	{XtNleft,		XtChainLeft},
	{XtNright,		XtChainLeft}
};

Arg window_args[] =
{
	{XtNfromVert,		(XtArgVal)NULL},
	{XtNbackground,		(XtArgVal)NULL},
	{XtNfont,		(XtArgVal)NULL},
	{XtNcursor,		(XtArgVal)NULL},
	{XtNwidth,		(XtArgVal)600},
	{XtNheight,		(XtArgVal)150},
	{XtNvertDistance,	(XtArgVal)6},
	{XtNhorizDistance,	(XtArgVal)0},
	{XtNtop,		XtChainTop},
	{XtNbottom,		XtChainBottom},
	{XtNleft,		XtChainLeft},
	{XtNright,		XtChainRight},
	{XtNeditType,		XawtextEdit},
	{XtNtype,		XawAsciiString},
	{XtNwrap,		XawtextWrapLine},
	{XtNdisplayNonprinting,	False},
	{XtNdisplayCaret,	False},
	{XtNsensitive,		False}
};

char satelliteId[16];

int s_raw;
XtInputId infd;

#define MAXBUFFER 512
unsigned char buf[MAXBUFFER];
int bufSize;
int bytes;

unsigned char expanded[MAXBUFFER*2];
int nExpanded;

int imageSeen = 0;
char photoName[256];
unsigned char image[160000];

int spectrumSeen = 0;
char spectrumName[256];
unsigned char spectrum[SPECTRUMLENGTH];

char text[256];

int frames    = 0;
int bytes     = 0;
int crcErrors = 0;

int x, y;
int offset;
unsigned char *imagePtr;

int fileBytes = 0;

void writetext(char *text)
{
	XawTextPosition pos;
	XawTextBlock tt;

	return;

	tt.firstPos = 0;
	tt.ptr      = text;
	tt.length   = strlen(text);
	tt.format   = FMT8BIT;

	pos = XawTextGetInsertionPoint(datawindow);

	XawTextReplace(datawindow, pos, pos, &tt);

	pos += tt.length;
	XawTextSetInsertionPoint(datawindow, pos);
}

/*
 *	Convert a call from the shifted ascii form used in an
 *	AX.25 packet.
 */
int ConvertCall(char *c, char *call)
{
	char *ep = c + 6;
	int ct =0;

	while (ct < 6)
	{
		if (((*c >> 1) & 127) == ' ') break;
	
		*call = (*c >> 1) & 127;
		call++;
		ct++;
		c++;
	}
	
	if ((*ep & 0x1E) != 0)
	{	
		*call = '-';
		call++;
		call += sprintf(call, "%d", (int)(((*ep) >> 1) & 0x0F));
	}

	*call = '\0';
	
	if (*ep & 1) return 0;

	return 1;
}

void SetPixel(unsigned char pixel)
{
	*imagePtr = pixel;
	imagePtr += 3;
}

/*
 *	process photodata
 */

void PhotoData(char *photoId, unsigned char *buffer, int length)
{
	HEADER *p;
	unsigned char *data;
	unsigned char line[WIDTH];
	int i,j,n;
	int f;

	strcpy(photoName, photoId);

	p = (HEADER *)buffer;

	if ((p->flags & 0x03) == 0x03)
	{
		int bytes;

		/* ascii text info */
		bytes = length-1;
		if (bytes > 255) bytes = 255;
		sprintf(writebuf, "%s: ascii text (%d)\n", photoId, bytes);
		writetext(writebuf);
#ifdef DEBUG
fprintf(stderr, "%s: ascii text (%d)\n", photoId, bytes);
fflush(stderr);
#endif
		strncpy(text, buffer + 1, bytes);

		sprintf(writebuf, "%s\n", buffer + 1);
		writetext(writebuf);
#ifdef DEBUG
fprintf(stderr, "%s\n", buffer + 1);
fflush(stderr);
#endif

		/* update the text info in the front of the file */
		if ((f = open(photoName, O_RDWR, 0660)) != -1)
		{
			write(f, text, 256);
		}
		else
		{
			f = creat(photoName, 0660);
			write(f, text, 256);
		}
		
		close(f);
	}
	else
	{

		x = (p->flags << 8) | p->xLoc;
		y = p->yLoc / 2;

		offset = (y * WIDTH) + 256;
		data   = buffer + sizeof(HEADER);
		imagePtr = line + x;

		sprintf(writebuf, "%s: x=%d y=%d\n", photoId, x, y);
		writetext(writebuf);
#ifdef DEBUG
fprintf(stderr, "%s: x=%d y=%d flags=%d offset=%d ptr=%x\n", photoId, x, y, p->flags, offset, (int)imagePtr);
fflush(stderr);
#endif

		if ((f = open(photoName, O_RDWR, 0660)) != -1)
		{
			lseek(f, offset, SEEK_SET);

			if (read(f, line, WIDTH) <= 0)
				for (i = 0; i < WIDTH; i++)
					line[i] = 0;
		}
		else
		{
			for (i = 0; i < WIDTH; i++)
				line[i] = 0;

			f = creat(photoName, 0660);
		}

		for (i = 0; i < (length-sizeof(HEADER)); i++)
		{
			if (data[i])
			{
				*imagePtr = data[i];
				imagePtr += 3;
			}
			else
			{
				i++;
				n = data[i++];
				for (j = 0; j < n; j++)
				{
					*imagePtr = data[i];
					imagePtr += 3;
				}
			}
		}
#ifdef DEBUG
fprintf(stderr, "updating image file\n");
fflush(stderr);
#endif
		lseek(f, offset, SEEK_SET);
		write(f, line, WIDTH);
		close(f);
	}
}

void SpectrumData(char *spectrumId, unsigned char *buffer, int length)
{
	int f;
	int start, end;
	int i;

	if (spectrumSeen == 0)
	{
		strcpy(spectrumName, spectrumId);

		if ((f = open(spectrumName, O_RDWR, 0660)) != -1)
		{
			read(f, text, 256);
			read(f, spectrum, SPECTRUMLENGTH);
			close(f);
		}
		
		spectrumSeen = 1;
	}

	if (buffer[0] == 0x0D && buffer[1] == 0x0A)
	{
		int bytes;

		/* ascii text info */
		bytes = length;
		if (bytes > 255) bytes = 255;

		sprintf(writebuf, "%s: ascii text (%d)\n", spectrumId, bytes);
		writetext(writebuf);
#ifdef DEBUG
fprintf(stderr, "%s: ascii text (%d)\n", spectrumId, bytes);
#endif
		strncpy(text, buffer, bytes);

		sprintf(writebuf, "%s\n", buffer);
		writetext(writebuf);
#ifdef DEBUG
fprintf(stderr, "%s\n", buffer);
#endif
	}
	else
	{
		start = buffer[0] * 128;
		end   = buffer[1] * 128;
		sprintf(writebuf, "%s: start=%d end=%d\n", spectrumId, start, end );
		writetext(writebuf);
#ifdef DEBUG
fprintf(stderr, "%s: start=%d end=%d\n", spectrumId, start, end);
#endif
		for (i = 0; i < 128; i++)
			spectrum[i] = buffer[i + 2];
	}

#ifdef DEBUG
fprintf(stderr, "updating spectrum file\n");
#endif
	/* update the image file */
/*
	f = creat(spectrumName, 0660);
	write(f, text, 256);
	write(f, spectrum, SPECTRUMLENGTH);
	close(f);
*/

#ifdef DEBUG
fprintf(stderr, "exiting\n");
#endif
}

/*
 *	decode a received frame.
 */
void ProcessFrame(void)
{
	int n;
	int via;
	unsigned char protocol;
	char toCall[10];
	char fromCall[10];
	char viaCall[10];

	frames++;
	bytes+=bufSize;


	/* check that frame is a kiss data frame */
	/* ignore control frames - should not happen */
	n = 0;
	if ((buf[n] & 0x0F) == 0)
	{
		n++;

		/* decode the to/from address */
		/* dont expect via address, but saves last if any */
		via = ConvertCall(buf + n, toCall);
		n+=7;
		
		via = ConvertCall(buf + n, fromCall);
		n+=7;
		
		while (via)
		{
			via = ConvertCall(buf + n, viaCall);
			n += 7;
		}

		/* check for a UI frame */
		if ((buf[n] & 0xEF) == 0003)
		{

			n++;
			protocol = buf[n++];

			/* see if this is a photo frame */
			if (strncmp(toCall, "PHOTO", 5) == 0)
			{
				PhotoData(toCall, buf + n, bufSize - n);
			}
			else if (strncmp(toCall, "SPECT", 5) == 0)
			{
				SpectrumData(toCall, buf + n, bufSize - n);
			}
			else if (strcmp(toCall, "PBLIST") == 0 ||
				 strcmp(toCall, "BBSTAT") == 0 ||
				 strcmp(toCall, "QST")    == 0 ||
				 strcmp(toCall, "AMSAT")  == 0 ||
				 strcmp(toCall, "LSTAT")  == 0 ||
				 strcmp(toCall, "BSTAT")  == 0 ||
				 strcmp(toCall, "HITVER") == 0 ||
				 strcmp(toCall, "TIME-1") == 0)
			{
				buf[bufSize] = '\0';
				sprintf(writebuf, "%s\n", buf + n);
				writetext(writebuf);
#ifdef DEBUG
fprintf(stderr, "%s\n", buf + n);
fflush(stderr);
#endif
			}
		}
	}
}

/*
 *	callback function when a frame is received.
 */
void GetFrame(XtPointer closure, int *s, XtInputId *Id)
{
#ifdef USEDATA

	if (ioctl(s_raw, FIONREAD, &bytes) == -1 || bytes == 0)
	{
		XtRemoveInput(infd);
		return;
	}

	if ((bytes = read(s_raw, (char *)&bufSize, sizeof(bufSize))) > 0)
	{
		if ((bufSize = read(s_raw, buf, bufSize)) == -1)
		{
			perror("read 1");
			return;
		}
	}
	else
	{
		perror("read 2");
		return;
	}
#else
	if ((bufSize = recv(s_raw, buf, MAXBUFFER, 0)) == -1)
	{
		perror("recv");
		return;
	}
#endif

#ifdef SAVEDATA
{
	int f;

	if ((f = open("DATA", O_RDWR | O_CREAT, 0666)) != -1)
	{
		lseek(f, 0L, SEEK_END);
		write(f, (char *)&bufSize, sizeof(bufSize));
		write(f, buf, bufSize);
		close(f);
	}
}
#endif
	fileBytes += bufSize;
	ProcessFrame();
}

/*
 *	the user wants to exit this program
 */
void QuitCb(Widget w, XtPointer client_data, XtPointer call_data)
{
	int f;

	/* print some stats */
	fprintf(stderr, "\nReceived:\n" );
	fprintf(stderr, "    totalFrames=%d\n", frames);
	fprintf(stderr, "    totalBytes=%d\n", bytes);
	fprintf(stderr, "    crcErrors=%d\n", crcErrors);
	fprintf(stderr, "    fileBytes=%d\n", fileBytes);

	/* close the sockets */
	close(s_raw);

	if (imageSeen == 1)
	{
		f = creat(photoName, 0660);
		write(f, text, 256);
		write(f, image, WIDTH*HEIGHT);
		close(f);
	}

	/* get out */
	XtDestroyApplicationContext(app_context);

	exit(0);
}

int main(int argc, char **argv)
{
	static XtCallbackRec callback[2];
	char *s, title[80];

	if ((s = getenv("SATELLITE")) == NULL)
	{
		printf("SATELLITE environment variable not set.\n");
		return(1);
	}

	strcpy(satelliteId, s);

	sprintf(title, "xweber:%s %s", satelliteId, VERSION_STRING);

	toplevel = XtAppInitialize(&app_context, "Xpb", NULL, 0, &argc, argv,
				NULL, shell_args, XtNumber(shell_args));
	XtVaSetValues(toplevel, XtNtitle, title, NULL);

	dpy  = XtDisplay(toplevel);

	XtGetApplicationResources(toplevel, &resources,
				resource_list, XtNumber(resource_list),
				NULL, ZERO);

	compwindow = XtCreateManagedWidget("appForm", formWidgetClass,
				toplevel, form_args, XtNumber(form_args));

	callback[0].callback = QuitCb;
	callback[0].closure  = toplevel;
	button_args[0].value = (XtArgVal)callback;
	button_args[1].value = (XtArgVal)"Quit";
	button_args[3].value = (XtArgVal)resources.button_font;
	quitbutton = XtCreateManagedWidget("quitButton", commandWidgetClass,
				compwindow, button_args, XtNumber(button_args));

	window_args[0].value = (XtArgVal)quitbutton;
	window_args[1].value = (XtArgVal)WhitePixel(dpy, DefaultScreen(dpy));
	window_args[2].value = (XtArgVal)resources.text_font;
	datawindow = XtCreateManagedWidget("monitorText", asciiTextWidgetClass,
				compwindow, window_args, XtNumber(window_args));

#ifdef USEDATA
	/* open up the data file */
	if ((s_raw = open("DATA", O_RDONLY)) == -1)
	{
		perror("open");
		return(1);
	}
#else
	/* open up the raw socket to receive all packets */
	if ((s_raw = socket(AF_INET, SOCK_PACKET, htons(2))) == -1)
	{
		perror("socket");
		return(1);
	}
#endif

	/* we want to be notified whenever a frame is received on the raw socket */
	infd = XtAppAddInput(app_context, s_raw, (XtPointer)XtInputReadMask, GetFrame, NULL);

	XtRealizeWidget(toplevel);

	XtAppMainLoop(app_context);
	
	return(0);
}

