#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <poll.h>

/*
 * A text-mode spectrum analyzer for Ubiquiti AirView2. Tested on Mac OS X 10.x but with minor tweaks should work on linux too.
 *
 * (c) 2013 Andrew Yourtchenko 
 *
 * license: MIT
 * 
 */
  
// #define BAUDRATE B115200
#define BAUDRATE B230400
#define MODEMDEVICE "/dev/tty.usbmodem1421"

#include <errno.h>
#include <termios.h>
#include <unistd.h>

#define error_message printf

char graph(int val, int avg) {
  if ((val - avg) > 47) { 
    return '#';
  } else if ((val - avg) > 24) {
    return 'X';
  } else if ((val - avg) > 21) {
    return '*';
  } else if ((val - avg) > 18) {
    return 'o';
  } else if ((val - avg) > 15) {
    return '+';
  } else if ((val - avg) > 12) {
    return '=';
  } else if ((val - avg) > 9) {
    return '~';
  } else if ((val - avg) > 6) {
    return '-';
  } else if ((val - avg) > 3) {
    return '.';
  } else {
    return ' ';
  }
}


void scanline(char *buf) {
  char *scanstart = "scan|0,";
  int vals[500];
  char chans[] = "||123456789ABCD|E|||||||||";
  int avg;
  char *p1, *p2;
  int i = 0;
  int n;
  char gr;
  int ix;
  static int counter = 0;

  time_t rawtime;
  struct tm * timeinfo;
  char timebuf[30];

  time (&rawtime);
  timeinfo = localtime (&rawtime);

  if (strstr(buf, scanstart) != NULL) {
    p1 = buf + strlen(scanstart);
    do {
      vals[i] = strtol(p1, &p2, 10);
      if (p1 == p2) {
        break;
      } else {
        i++;
        while(isspace(*p2)) { p2++; }
	p1 = p2;
      }
    } while (*p2 && i < 500); 
    n = i;
    avg = 0;
    for(i=0;i<n;i++) {
      avg += vals[i]; 
    }
    // printf("%d vals read, avg %d\n", n, avg);
    asctime_r(timeinfo, timebuf); 
    timebuf[strlen(timebuf)-1] = 0;
    counter++;
    printf("%s |  %5.2f ", timebuf, ((float)avg)/n);
    avg = avg / n;
    avg = -95; // !!!! 
    for(i=0;i<n;i++) {
      gr = graph(vals[i], avg);
      if ((gr == ' ') || ((counter%10) == 0)) {
        if(((i*5 + 23990 - 24020) % 50) == 0) {
          ix = (i*5 + 23990 - 24020) / 50;
          gr = chans[ix];
        }
      }
      putchar(gr);
    }
    printf("\n");
  } else {
    printf("%s\n", buf);
  }
}

int set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0) {
                error_message ("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // ignore break signal
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0) {
                error_message ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void set_blocking (int fd, int should_block) {
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 1;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                error_message ("error %d setting term attributes", errno);
}


int main(int argc, char *argv[]) {
  char out[16384];
  int o = 0;
  char buf [100];
  char start[] = "\rinit\r\rgdi\r\rbs\r";
  char cr = '\n';
  struct pollfd fds[1];

  int i;
  int fd = open (argv[1], O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
  if (fd < 0) {
        error_message ("error %d opening %s: %s", errno, argv[1], strerror (errno));
        return;
  }
  set_interface_attribs(fd, BAUDRATE, 0);  // set speed to 115,200 bps, 8n1 (no parity)
  set_blocking(fd, 0);                // set no blocking
  write (fd, start, strlen(start) );           // send 7 character greeting
  printf("Reaing...\n");
  while (1) {
    fds[0].fd = fd;
    fds[0].revents = 0;
    fds[0].events = POLLIN; 
    poll(fds, 1, 1000);
    int n = read (fd, buf, sizeof(buf));
    if (n > 0) {
      buf[n] = 0;
      i = 0;
      while(buf[i]) {
        if ((buf[i] == '\r') || (buf[i] == '\n')) {
          out[o++] = '\000';
          buf[i] = '\n';
        } else {
          out[o++] = buf[i];
        }
        if (buf[i] == '\n') {
          out[o] = 0;
	  scanline(out);
          //write(1, out, o);
          o = 0;
        }
        if (o > 4096) {
          write(1, out, o);
          o = 0;
        }
        i++;
      }
    }
  }
}


