#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <assert.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/spi/spidev.h>

extern volatile unsigned int *mxgpioregs;
extern void nbuslock(void);

static FILE *xopen(const char *f) {
	int l = strlen(f);
	char b[512];
	struct stat s;

	if (strcmp("-", f) == 0) return stdin;

	if (stat(f, &s) != 0) return NULL;

	if (strcmp(".jed.gz", &f[l-7]) == 0 ||
	  strcmp(".jed.bz2", &f[l-8]) == 0 ||
	  strcmp(".jed", &f[l-4]) == 0) {
		snprintf(b, 512, "exec jed2vme '%s'", f);
		return popen(b, "r");
	} else if (strcmp(".bin.gz", &f[l-7]) == 0) {
		snprintf(b, 512, "exec gunzip -c '%s'", f);
		return popen(b, "r");
	} else if (strcmp(".bin.bz2", &f[l-8]) == 0) {
		snprintf(b, 512, "exec bunzip2 -c '%s'", f);
		return popen(b, "r");
	} else return fopen(f, "r");
}

static unsigned char *memstore_buf;
static int memstore_len;
static int memstore_idx;

static void memstore(FILE *f) {
	int sz = 0x10000;
	int rem = sz;
	int i;
	unsigned char *b, *t;

	if (memstore_buf) free(memstore_buf);
	b = memstore_buf = malloc(sz);
	assert(b != NULL);

	while (!feof(f)) {
		if (rem == 0) {
			sz += 0x10000;
			rem = 0x10000;
			t = realloc(memstore_buf, sz);
			assert (t != NULL);
			b = t + (b - memstore_buf);
			memstore_buf = t;
		}

		i = fread(b, 1, rem, f);
		rem -= i;
		b += i;
	}

	memstore_idx = 0;
	memstore_len = sz - rem;
	assert(memstore_len > 0);
	memstore_buf = realloc(memstore_buf, memstore_len);
	assert(memstore_buf != NULL);
}

signed char ispSPI(const char *filename, int nvcm_prog) {
	FILE *file = NULL;
	int spidev = open("/dev/spidev1.0" , O_RDWR), ret;
	char mode = SPI_CPOL | SPI_CPHA;
	struct spi_ioc_transfer tr = {
		//.tx_buf = memstore_buf,
		//.rx_buf = NULL,
		.len = 0,
		.delay_usecs = 0,
		.speed_hz = 25000000,
		.bits_per_word = 8,
	};

	assert(spidev > 0);

	/* Set up iCE40 to be ready to be programmed */
	mxgpioregs[0x718/4] = 0x60000; //CS and reset low
	mxgpioregs[0x724/4] = 0x10000; //clk high
	usleep(1);
	mxgpioregs[0x714/4] = 0x20000; //reset high
	usleep(201);                   
	mxgpioregs[0x158/4] = 0x3; //Set clk back to SPI

	file = xopen(filename);
	if(!file) return -2;

	memstore(file);
	if (pclose(file) == -1) fclose(file);
	file = NULL;
	
	ret = ioctl(spidev, SPI_IOC_WR_MODE, &mode);
	if(ret == -1) return ret;
	ret = ioctl(spidev, SPI_IOC_RD_MODE, &mode);
	if(ret == -1) return ret;
	tr.tx_buf = (unsigned long)memstore_buf;

	nbuslock();
	for(;memstore_len > 0;) {
		tr.tx_buf += tr.len;
		if(memstore_len < 2048) tr.len = memstore_len;
		else tr.len = 2048;

		memstore_len -= tr.len;
				
		ret = ioctl(spidev, SPI_IOC_MESSAGE(1), &tr);
		if(ret < 1) return ret;
	}

	if(!(mxgpioregs[0x930/4] & 0x4)) return -1;

	tr.len = 49;
	ret = ioctl(spidev, SPI_IOC_MESSAGE(1), &tr);
	if(ret < 1) return ret;

	return 0;

	
}
