#include <stdio.h>
#include <errno.h>
//#include <string.h>
// these are for open() and sysctl()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
// these are for gettimeofday()
#include <sys/time.h>
#include <time.h>
// these are for the ppdev ioctl numbers
#include <linux/ppdev.h>
#include <linux/parport.h>
// this is for inb/iopl/ioperm
/*#include <unistd.h>
#include <sys/io.h>*/

#define DEVNAME "/dev/parport0"
#define WAIT_INTERVAL 1000000

int main(int argc, char* argv[]) 
{
	int fd, diff_usec, diff_last[8], out_val[8], tmp, i;
	fd_set rfds;
	struct timeval tv_select, tv_measure, tv_measure_last[8];
	unsigned char val, val_last, val_diff, changed;
	struct ppdev_frob_struct tmp2;
	FILE* out_file;

	if ((fd = open(DEVNAME, O_RDWR | /*O_DIRECT |*/ O_EXCL)) == -1) {
		printf("Error: could not open %s: %s\n", DEVNAME, strerror(errno));
		return 1;
	}

	// claim access to the port (see http://www.linuxfocus.org/common/src/article205/ppdev.html)
	if (ioctl(fd, PPCLAIM) == -1) {
		printf("Error: could not claim access to %s: %s\n", DEVNAME, strerror(errno));
		return 2;
	}
	/*if (ioctl(fd, PPEXCL) == -1) {
		printf("Error: could not claim exclusive access to %s: %s\n", DEVNAME, strerror(errno));
		return 2;
	}*/
	// and set the mode (SPP bi-directional, sometimes calls PS/2)
	tmp = IEEE1284_MODE_BYTE;
	if (ioctl(fd, PPSETMODE, &tmp) == -1) {
		printf("Error: could not set mode on %s: %s\n", DEVNAME, strerror(errno));
		return 2;
	}
	// and direction (in)
	tmp = 1;
	if (ioctl(fd, PPDATADIR, &tmp) == -1) {
		printf("Error: could not set direction to in on %s: %s\n", DEVNAME, strerror(errno));
		return 2;
	}
	// then allow us to use inb()
	/*if (ioperm(0x378, 1, 1) == -1) {
	//if (iopl(3) == -1) {
		printf("Error: can't set up I/O permissions for process, not root? Error is: %s\n", strerror(errno));
		return 2;
	}*/

	// last part of initialization: turn on the INIT line to power the sensors
	tmp2.mask = PARPORT_CONTROL_INIT;
	tmp2.val = PARPORT_CONTROL_INIT;
	if (ioctl(fd, PPFCONTROL, &tmp2) == -1) {
		printf("Error: could not turn on SELECT on %s: %s\n", DEVNAME, strerror(errno));
		return 2;
	}

	// also open a file for reading
	if ((out_file = fopen("/tmp/data.log", "w")) == NULL) {
		printf("Error: could not open log file for writing: %s\n", strerror(errno));
		return 5;
	}

	// and read from the port - interrupt based by using select()
	val_last = 0;
	if (gettimeofday(&tv_measure, NULL) == -1) {
		printf("Error: could not get timestamp: %s\n", strerror(errno));
		return 4;
	}
	for (i=0; i<8; i++) {
		tv_measure_last[i] = tv_measure;
		out_val[i] = 0;
	}

	while (1) {
		/*printf("selecting\n");
		// prepare for the select call
		// select never gets called and read never gets data because (presumably) it waits for strobe
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);
		tv_select.tv_sec = 0;
		tv_select.tv_usec = WAIT_INTERVAL;
		if (select(fd + 1, &rfds, NULL, NULL, &tv_select)) {*/
			//printf("reading\n");
			// ok, something changed, read it (just 1 byte)
			/*if (read(fd, &val, 1) != 1) {
				printf("Error: could not read from %s: %s\n", strerror(errno));
				return 3;
			}*/
			// ioctl is slow.....
			if (ioctl(fd, PPRDATA, &val) == 1) {
				printf("Error: could not read data lines: %s\n", strerror(errno));
				return 3;
			}
//			val = inb(0x378);
			
			if (gettimeofday(&tv_measure, NULL) == -1) {
				printf("Error: could not get timestamp: %s\n", strerror(errno));
				return 4;
			}
			// check which bits have changed
			changed = 0;
			val_diff = val ^ val_last;
//			printf("=== %02x %02x %02x %d\n", val, val_last, val_diff, tv_measure.tv_usec);
			if (val_diff) {
				for (i=0; i<8; i++) {
//					printf("?? %d\n", i);
					if (val_diff & 0x1) {
//					printf("yes\n");
						diff_usec = tv_measure.tv_usec - tv_measure_last[i].tv_usec +
						1000000 * (tv_measure.tv_sec - tv_measure_last[i].tv_sec);
						tv_measure_last[i] = tv_measure;
//						printf("%d: was %d, now %d, %dus\n", i, (val_last >> i) & 0x1, (val >> i) & 0x1, diff_usec);
						if (! ((val >> i) & 0x1)) {
							// 1->0 flank, remember time
							diff_last[i] = diff_usec;
						}
						else {
							// 0->1 flank, cycle complete, compute
							// attention: 1 byte accuracy for now!
							// reject high-frequency jitter
							if (diff_usec + diff_last[i] > 1000) {
								out_val[i] = diff_last[i] * 0x100 / (diff_usec + diff_last[i]);
//								printf("%d: %dus out of %dus --> %d\n", i, diff_last[i], diff_usec + diff_last[i], out_val[i]);
								// some part of out_val changed
								changed = 1;
							}
						}
					}
					val_diff >>= 1;
				}

				// and write to the file for now (if since at least 1 value changed)
				if (changed) {
					fprintf(out_file, "%010d.%06d ", tv_measure.tv_sec, tv_measure.tv_usec);
					for (i=0; i<8; i++)
						fprintf(out_file, "%d ", out_val[i]);
					fprintf(out_file, "%d\n");
				}
			}
			val_last = val;
		//}
	}

/*for (;;)
    {
      int irqc;
      int busy = nAck | nFault;
      int acking = nFault;
      int ready = Busy | nAck | nFault;
      char ch;

      // Set up the control lines when an interrupt happens. 
      ioctl (fd, PPWCTLONIRQ, &busy);

      // Now we're ready. 
      ioctl (fd, PPWCONTROL, &ready);

      // Wait for an interrupt. 
      {
        fd_set rfds;
        FD_ZERO (&rfds);
        FD_SET (fd, &rfds);
        if (!select (fd + 1, &rfds, NULL, NULL, NULL))
          // Caught a signal? 
          continue;
      }

      // We are now marked as busy. 

      // Fetch the data. 
      ioctl (fd, PPRDATA, &ch);

      // Clear the interrupt. 
      ioctl (fd, PPCLRIRQ, &irqc);
      if (irqc > 1)
        fprintf (stderr, "Arghh! Missed %d interrupt%s!\n",
         irqc - 1, irqc == 2 ? "s" : "");

      // Ack it. 
      ioctl (fd, PPWCONTROL, &acking);
      usleep (2);
      ioctl (fd, PPWCONTROL, &busy);

      putchar (ch);
    }*/

	fclose(out_file);

	if (ioctl(fd, PPRELEASE) == -1) {
		printf("Error: could not release access to %s: %s\n", DEVNAME, strerror(errno));
		// ignore that here
		//return 2;
	}

	return 0;
}

