#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// High Selectivity Receiver?? Huh ...

// device seems to be a SPI bus
typedef struct {
  int csn;     // Chip Select, active low 
  int clk;    // clock
  int dout;   // data out
  int din;    // data in
} hsr_dev_t;

void del_dev(hsr_dev_t* dev) {
  if ( NULL != dev) {
    if ( -1 != dev->csn) {
      close(dev->csn);
    }
    if ( -1 != dev->clk) {
      close(dev->clk);
    }
    if ( -1 != dev->dout) {
      close(dev->dout);
    }
    if ( -1 != dev->din) {
      close(dev->din);
    }
    dev->csn = -1;
    dev->clk = -1;
    dev->dout = -1;
    dev->din = -1;
  }
}

int create_pin(int edesc, int num, char* dir) {
  char cline[1024];
  int ret, ret1, err;
  int pin_flags;

  sprintf(cline, "%d", num);
  write(edesc, cline, strlen(cline));

  sprintf(cline, "/sys/class/gpio/gpio%d/direction", num);
  ret = open(cline, O_WRONLY);
  if ( -1 == ret) {
    printf("Can't create pin #%d : %s\n", num, strerror(errno));
    return -1;
  }

  ret1 =  write(ret, dir, strlen(dir));
  err = errno;
  close(ret);

  if ( -1 == ret1) {
    printf("Can't configure pin #%d : %s\n", num, strerror(err));
    return -1;
  }

  pin_flags = !strcmp(dir, "out") ? O_WRONLY : O_RDONLY;
  sprintf(cline, "/sys/class/gpio/gpio%d/value", num);

  if ( -1 == (ret = open(cline, pin_flags))) {
    return -1;
  }
    
  return ret;
} 

int create_dev(hsr_dev_t*dev) {
  int ret = 0;
  int gpio_export;

  gpio_export = open("/sys/class/gpio/export", O_WRONLY);
  if ( -1 == gpio_export) {
    printf("Can't open export : %s \n", strerror(errno));
    return -1;
  }

  if ( -1 == (ret = create_pin(gpio_export, 8, "out"))) {
    goto over;
  }
  dev->csn = ret;

  if ( -1 == (ret = create_pin(gpio_export, 6, "out"))) {
    goto over;
  }
  dev->clk = ret;

  if ( -1 == (ret = create_pin(gpio_export, 7, "out"))) {
    goto over;
  }
  dev->dout = ret;

  if ( -1 == (ret = create_pin(gpio_export, 5, "in"))) {
    goto over;
  }
  dev->din = ret;


over:
  close(gpio_export);

  return ret;
}

int dev_write_value(int desc, uint8_t val) {
  char buf[5];
  sprintf(buf, "%d", val%10); 
  int ret = write(desc, buf, strlen(buf));
  //printf("ret %d %s \n", ret, ret < 0 ? strerror(errno) : " ");
  return ret;
}

int dev_read_value(int desc) {
  char buf[32];
  long rval;

  if ( -1 == read(desc, buf, 32)) {
    printf("can't read a value: %s \n", strerror(errno));
    return 1;
  }

  rval = strtol(buf, NULL, 10);

  return (rval >= 1) || (rval < 0) ? 1 : 0;
}

void hsr_init(hsr_dev_t* dev) {
    dev_write_value(dev->dout, 0);
    dev_write_value(dev->clk, 0);
    dev_write_value(dev->csn, 1);
}

uint32_t hsr_write_byte(hsr_dev_t *dev, int delay, uint32_t value){
  int i;
  usleep(delay);
  uint32_t rval = 0;

  dev_write_value(dev->csn, 0);
  usleep(100);

  for( i = 0; i < 8; ++i) {
    rval = rval << 1;

    // pattern is left to right, that is 7-th bit runs first
    // (value >> (7-i))&0x1 will be equal to 
    // seb v1,a1 --> mips32r2
    // srl v0,v1,0x1f
    // sll a1,v1,1 --> next to seb
    dev_write_value(dev->dout, (value >> (7 - i)) & 0x1);
    usleep(100);
    
    dev_write_value(dev->clk, 1);
    usleep(100);

    rval |= dev_read_value(dev->din);

    dev_write_value(dev->clk, 0);
    usleep(100);
  }

  dev_write_value(dev->csn, 1);
  usleep(100);

  printf("write byte %d return value is %x \n", value, rval);
  
  return rval & 0xff;
}

void write_a_chain(hsr_dev_t* d, uint8_t* chain, int items) {
		int i = 0;
    int status = 0;
    // a preabmle
    hsr_write_byte(d, 75, 0); 
    status = hsr_write_byte(d, 75, 0);

    if ( status) {
      int loop = 2;
      do {
       status = hsr_write_byte(d, 75, 0);
       loop = (loop + 1) & 0xffff;
       if ( loop < 2) {
        continue;
       } 
      } while(status);
    }

    for ( i =0; i < items; ++i) {
   	  hsr_write_byte(d, 75, chain[i]);
    }

    hsr_write_byte(d, 75, 0);  

    usleep(20000);
}

// known good commands: disable 98

int main(int argc, char** argv) {
  hsr_dev_t dev = {-1, -1, -1, -1};
  int b = 40, cf = -1;
  if ( argc < 2) {
    printf("%s: bandwidth [center frequency kHz] \n", argv[0]);
    return 1;
  } else if ( argc < 3) {
		b = 40; //strtol(argv[1], NULL, 10);
		if ( (b != 10) && (b != 20) && (b != 40)) {
			printf("Incorrect bandwidth : valid values are 10, 20, 40 \n");
			return -1;
		}
		printf("Send a disable command \n");
	} else {
		b = strtol(argv[1], NULL, 10);
		cf = strtol(argv[2], NULL, 10);
		if ( (b != 10) && (b != 20) && (b != 40)) {
			printf("Incorrect bandwidth : valid values are 10, 20, 40 \n");
			return -1;
		}
		printf("Sent central freq %d and a bandwidth %d \n", cf, b);
	}

  if ( -1 != create_dev(&dev)) {
		uint8_t chain[10];
		uint8_t v1;

    // do a device's initialization
    hsr_init(&dev); 
		// write bandwidth
		memset(chain, 0, sizeof(chain));
		chain[0] = 98;
		snprintf((char*)(chain + 1), 3, "%02d", b);

		write_a_chain(&dev, chain, strlen((char*)chain));

		if ( -1 != cf ) {

			v1 = 120;
			write_a_chain(&dev, &v1, 1);

			v1 = 109;
			write_a_chain(&dev, &v1, 1);

			memset(chain, 0, sizeof(chain));
			chain[0] = 102;
			snprintf((char*)(chain + 1), 6, "%05d", cf);

			write_a_chain(&dev, chain, strlen((char*)chain));
		}

  }
  del_dev(&dev);

  return 0;
}
