// Oscilloscope Declarations *******************************************
enum data_type{
        Digital,
        Analog
};

int scope_initialized = FALSE;

struct scope_arg{
  enum data_type type;
  int            block_count;
  int            block_size;
  int            delay;
  int            t_scale_expansion;
  int            display_width;
  enum dlp_port  digital_port;
  enum port_pin  digital_pin;
  enum analog_port analog_input;
};

// Threading
pthread_t scope_thread1 = 0;
pthread_t scope_thread2 = 0;

struct timespec remaining_time;
struct timespec delay = {(time_t)0, (long int)1000*1000000};

int get_data();
void redraw();
void* scope(void* ptr);
void scope_deinit();

void scope_deinit(){
  if (scope_thread1)
    pthread_join(scope_thread1, NULL);
  if (scope_thread2)
    pthread_join(scope_thread2, NULL);
//    printf("%d signals received\n",signals);
  close(fd);
}

// X-Window Routines are based on "A Brief Intro to X11 Programming" found on
// http://www.math.msu.su/~vvb/2course/Borisenko/CppProjects/GWindow/xintro.html
void* scope(void* arg){
  XEvent event;
  KeySym key;
  char text[255];
  XColor tmp;
  char title[100];
  int screen;
  Display *display;
  Window win;
  GC gc;
  struct scope_arg* settings = (struct scope_arg*)arg;
  unsigned long black, white, darkgreen, red;
  int i,j;
  int pass;
  enum data_type type  = settings->type;
  enum analog_port analog_input_port = settings->analog_input;
  enum dlp_port digital_input_port = settings->digital_port;
  enum port_pin pin = settings->digital_pin;
  int t_scale_expansion = settings->t_scale_expansion;
  double* V;
  XPoint* points;
  int point_count;
  int burst_size;
  unsigned char block_count = settings->block_count;
  unsigned char block_size = settings->block_size;
  long analog_height = 600;
  long digital_height = 400;
  long height_per_channel = 20;
  int** state;
  int channels;
  int reading_delay = settings->delay;  // units are microseconds
  long width = settings->display_width;

  burst_size = get_burst_size(block_count, block_size);

  if (pin == All){
    channels = 8;
  }
  else{
    channels = burst_size/width;
    if (channels*width != burst_size)
      channels++;
    digital_height = height_per_channel*channels;
  }

  if (type == Analog){
    setup_a_to_d(Vdd_Vss, 3, 32);
    V = (double*)malloc(burst_size*sizeof(double));
    get_voltage(analog_input_port, block_count, block_size, reading_delay, V);
    point_count = burst_size;

    // Convert the voltages and times to XPoints
    points = (XPoint*)malloc(point_count*sizeof(XPoint));
    for (i = 0; i < point_count; i++) {
      points[i].x = i * t_scale_expansion;
      points[i].y = analog_height - 1 - 10 - V[i]/10;
    }
  }
  else{
    state = (int**)malloc(burst_size*sizeof(int));
    for (i = 0; i < burst_size; i++) 
      state[i]=(int*)malloc(channels*sizeof(int));
    read_port(digital_input_port, block_count, block_size, reading_delay, state, channels);
    point_count = burst_size;
    points = (XPoint*)malloc(channels*point_count*sizeof(XPoint));
    for (i = 0; i < point_count; i++){
      if (pin != All){
	pass = i / width;
	j = i % width;
	points[pass*width+j].x = j * t_scale_expansion;
	points[pass*width+j].y = digital_height -1 -5 - height_per_channel*pass - (height_per_channel/2)*state[i][pin];
      }
      else {
        for (j = 0; j < channels; j++){
          points[i*channels+j].x = i * t_scale_expansion;
          points[i*channels+j].y = digital_height - 1 - 10 - 50*j - 20* state[i][j];
        }
      }
    }
    printf("\n");
  }
  // Create the X-window and the required colors
  display = XOpenDisplay((char*)0);
  screen = DefaultScreen(display);
  black = BlackPixel(display, screen);
  white = WhitePixel(display, screen);
  XParseColor(display, DefaultColormap(display, screen), "darkgreen", &tmp);
  XAllocColor(display, DefaultColormap(display, screen), &tmp);
  darkgreen = tmp.pixel;
  XParseColor(display, DefaultColormap(display, screen), "red", &tmp);
  XAllocColor(display, DefaultColormap(display, screen), &tmp);
  red = tmp.pixel;

  if (type == Analog){
    win = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, width, analog_height, 5, white, black);
    sprintf(title, "Analog Oscilloscope (%.1f uS/point)", 24.0+reading_delay);
    XSetStandardProperties(display, win, title, "Scope", None, NULL, 0 , NULL);
  }
  else{
    win = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, width, digital_height, 5, white, black);
    sprintf(title, "8-Channel Logic Probe (%.1f uS/point)", 4.0+reading_delay);
    XSetStandardProperties(display, win, title, "LogicProbe", None, NULL, 0 , NULL);
  }
  XSelectInput(display, win, ExposureMask | ButtonPressMask | KeyPressMask);
  gc = XCreateGC(display, win, 0, 0);
  XSetBackground(display, gc, white);
  XSetForeground(display, gc, black);
  XClearWindow(display, win);
  XMapRaised(display, win);
  while(1) {
    XNextEvent(display, &event);
    if (event.type == Expose && event.xexpose.count == 0) {

      // Draw dark green grid at 100x100 pixels
      XSetForeground(display, gc, darkgreen);
      if (type == Analog){
        XDrawLine(display, win, gc, 0, 0, 0, analog_height-1);
        for (i = 0; i < analog_height; i+=100) {
          XDrawLine(display, win, gc, 0, analog_height-1-i-10, width-1, analog_height-1-i-10);
        }
        for (i = 0; i < width; i+=100) {
          XDrawLine(display, win, gc, width-1-i, 0, width-1-i, analog_height-1-10);
        }

        // Plot red lines between the points
        XSetForeground(display, gc, red);
        for (i = 1; i< point_count; i++) {
          if (pin != All && (i % width == 0))
            continue;
          XDrawLine(display, win, gc, points[i-1].x, points[i-1].y, points[i].x, points[i].y);
        }

        // Plot the actual data points as white dots
        XSetForeground(display, gc, white);
        XDrawPoints(display, win, gc, points, point_count, NotifyNormal);
      }
      else if (type == Digital && pin != All){
        XDrawLine(display, win, gc, 0, 0, 0, digital_height-1);
        for (i = 0; i < digital_height; i+=height_per_channel) {
          XDrawLine(display, win, gc, 0, digital_height-1-i-5, width-1, digital_height-1-i-5);
        }
        for (i = 0; i < width; i+=26) {
          XDrawLine(display, win, gc, width-1-i, 0, width-1-i, digital_height-1-10);
        }

        // Plot red lines between the points
        XSetForeground(display, gc, red);
        for (i = 1; i< point_count; i++) {
          if (i % width == 0)
            continue;
          XDrawLine(display, win, gc, points[i-1].x, points[i-1].y, points[i].x, points[i].y);
        }

        // Plot the actual data points as white dots
        XSetForeground(display, gc, white);
        XDrawPoints(display, win, gc, points, point_count, NotifyNormal);
      }
      else {
        // Draw dark green grid at 100x100 pixels
        XDrawLine(display, win, gc, 0, 0, 0, digital_height-1);
        for (i = 0; i < digital_height; i+=height_per_channel) {
          XDrawLine(display, win, gc, 0, digital_height-1-i-10, width-1, digital_height-1-i-10);
        }
        for (i = 0; i < width; i+=height_per_channel) {
          XDrawLine(display, win, gc, width-1-i, 0, width-1-i, digital_height-1-10);
        }

        // Plot red lines between the points
        XSetForeground(display, gc, red);
        for (i = 1; i < point_count; i++) {
          if (i > (8*width))
            continue;
          for (j = 0; j < 8; j++){
            XDrawLine(display, win, gc, points[i*8+j-8].x, points[i*8+j-8].y, points[i*8+j].x, points[i*8+j].y);
          }
        }

        // Plot the actual data points as white dots
        XSetForeground(display, gc, white);
        XDrawPoints(display, win, gc, points, point_count*8, NotifyNormal);
      }
    }
    if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1) {
      if (text[0] == 'q') {
        break;
      }
      printf("You pressed the %c key!\n", text[0]);
    }

    if (event.type == ButtonPress) {
      printf("You pressed a button at (%i, %i)\n", event.xbutton.x, event.xbutton.y);
      break;
    }
  }

  // Free allocations for X-window system
  free(points);
  if (type == Analog)
    free(V);
  XFreeGC(display, gc);
  XDestroyWindow(display, win);
  XCloseDisplay(display);
  return NULL;
}

