#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>

#define FREQUENCY 20


/* Designed with http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html */
/* 0.5 Hz */
double filterloop(double input)
{
  static double xv[9] = { 0,0,0,0,0,0,0,0,0 };
  static double yv[9] = { 0,0,0,0,0,0,0,0,0 };
  xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8]; 
  xv[8] = input / 1.016715697e+09;
  yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8]; 
  yv[8] =   (xv[0] + xv[8]) + 8 * (xv[1] + xv[7]) + 28 * (xv[2] + xv[6])
    + 56 * (xv[3] + xv[5]) + 70 * xv[4]
    + ( -0.4465339824 * yv[0]) + (  3.9317654915 * yv[1])
    + (-15.1656710590 * yv[2]) + ( 33.4719203140 * yv[3])
    + (-46.2364258410 * yv[4]) + ( 40.9350834660 * yv[5])
    + (-22.6850629990 * yv[6]) + (  7.1949243584 * yv[7]);
  return yv[8];
}


int16_t lowpass(int16_t *input, int16_t *output, int n)
{
  int i;
  for (i = 0; i < n; i++) {
    double f = filterloop((double)input[i]);
    if (f > 32767) f = 32767;
    if (f < -32767) f = -32767;
    output[i] = (int16_t)f;
  }
}

void usage(const char **argv)
{
  printf("Usage: %s [-d] [log interval] [output file] [input device] [raw output file]\n", argv[0]);
  printf("       -d = deamon mode\n");
  printf("       Default values:\n");
  printf("           log interval: 60 (seconds)\n");
  printf("           output file:  stdout\n");
  printf("           input device: stdin\n");
}

void printlog(FILE *output, time_t ts, int16_t max, int16_t min)
{
  char timestamp[15];
  struct tm* scratch_time = gmtime(&ts);

  strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", scratch_time);
  fprintf(output, "%s\t%d\t%d\n", timestamp, (int)max, (int)min);
}

/* Log raw data: 16 bit big endian.  0x8000 marks that a 32 bit big endian
   timestamp (seconds since 1970-01-01 00:00:00 UTC) follows */
void lograw(FILE *output, int16_t *log, unsigned int n)
{
  int i = 0;
  uint32_t t = (uint32_t)time(0) - i / FREQUENCY;

  fputc(0x80, output);
  fputc(0x00, output);
  fputc((t >> 24) & 0xff, output);
  fputc((t >> 16) & 0xff, output);
  fputc((t >>  8) & 0xff, output);
  fputc((t >>  0) & 0xff, output);
  while (i < n) {
    int16_t v = log[i] == -32768 ? -32767 : log[i];
    fputc((v >> 8) & 0xff, output);
    fputc((v >> 0) & 0xff, output);
    i++;
  }
}

void process(const int16_t *log, unsigned int i, int16_t *max, int16_t *min)
{
  *max = -32768;
  *min = 32767;

  while (i--) {
    if (*max < log[i]) *max = log[i];
    if (*min > log[i]) *min = log[i];
  }
}

int main(int argc, const char** argv)
{
  FILE *input;
  FILE *output;
  FILE *rawoutput = 0;
  unsigned int interval;
  time_t logtime;
  int16_t *log, *filtered;
  unsigned int logindex;
  const char **cmdline = argv;
  int forked = 0;
  int i = 0;
  int level;

  while (argc > 1 && argv[1] && *argv[1] == '-') {
    switch (argv[1][1]) {
    case 'd':
      if (!forked) {
	forked = 1;
	umask(0);
	setpgrp();
	if (fork())
	  return 0;
      }
      break;
    default:
      printf("%s: Unknown option -%c\n", cmdline[0], argv[1][1]);
      usage(cmdline);
      return -1;
    }
    argc--;
    argv++;
  }

  input = argc > 3 && argv[3] ? fopen(argv[3], "r") : stdin;
  interval = argc > 1 && argv[1] ? atoi(argv[1]) : 60;
  logtime = time(NULL);
  logindex = 0;

  log = malloc(FREQUENCY * interval * sizeof(int16_t) + sizeof(int16_t));
  filtered = malloc(FREQUENCY * interval * sizeof(int16_t) + sizeof(int16_t));

  if (!log) {
    printf("%s: Could allocate memory.\n", cmdline[0]);
    return -1;
  }

  if (!input) {
    printf("%s: Could not open input.\n", cmdline[0]);
    return -1;
  }

  if (!(argc > 2 && argv[2]))
    output = stdout;

  fscanf(input, "%d", &level);
  
  while (1) {
    time_t now;

    fscanf(input, "%d", &level);
    log[logindex] = level;
    logindex += logindex <= FREQUENCY * interval;
    if (logindex > FREQUENCY * interval)
      memmove(log, log + 1, FREQUENCY * interval * sizeof(int16_t));

    now = time(NULL);
    if (now - logtime >= interval) {
      int i;
      double avg;
      int16_t max, min;

      rawoutput = argc > 4 && argv[4] ? fopen(argv[4], "a") : 0;
      if (rawoutput) {
	lograw(rawoutput, log, logindex);
	fclose(rawoutput);
      }
      lowpass(log, filtered, logindex);
      process(filtered, logindex, &max, &min);

      if (output != stdout)
	output = fopen(argv[2], "a");

      if (output) {
	printlog(output, now/2 + logtime/2, max, min);
	if (output != stdout)
	  fclose(output);
      }

      logindex = 0;
      logtime = now;
    }
  }

  fclose(input);
  return 0;
}

