#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <math.h>

#include <sys/ioctl.h>
#include <dev/i2c/i2c_io.h>

#define IIC "/dev/iic2"
#define ADR 0x54

int
iic_write(int fd, i2c_addr_t addr, uint8_t *cmd, size_t cmdlen, uint8_t *buf, size_t buflen)
{
	i2c_ioctl_exec_t iie;

	iie.iie_op = I2C_OP_WRITE_WITH_STOP;
	iie.iie_addr = addr;
	iie.iie_cmd = cmd;
	iie.iie_cmdlen = cmdlen;
	iie.iie_buf = buf;
	iie.iie_buflen = buflen;

	if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1)
		return errno;
	return 0;
}

int
iic_read(int fd, i2c_addr_t addr, uint8_t *cmd, size_t cmdlen, uint8_t *buf, size_t buflen)
{
	i2c_ioctl_exec_t iie;

	iie.iie_op = I2C_OP_READ_WITH_STOP;
	iie.iie_addr = addr;
	iie.iie_cmd = cmd;
	iie.iie_cmdlen = cmdlen;
	iie.iie_buf = buf;
	iie.iie_buflen = buflen;

	if (ioctl(fd, I2C_IOCTL_EXEC, &iie) == -1)
		return errno;
	return 0;
}

void rgb(uint8_t *data, int i, int r, int g, int b)
{
	if (i >= 0 && i <= 5) {
		data += 2;
		data[3*i+0] = b;
		data[3*i+1] = g;
		data[3*i+2] = r;
	}
}

static
uint8_t clamp(unsigned long x) {
	return x < 0 ? 0 : x > 255 ? 255 : x;
}

void rgbshade(uint8_t *data, int i, int a)
{
	if (i >= 0 && i <= 5) {
		data += 2;
		data[3*i+0] = clamp(data[3*i+0] * a / 256);
		data[3*i+1] = clamp(data[3*i+1] * a / 256);
		data[3*i+2] = clamp(data[3*i+2] * a / 256);
	}
}

unsigned long
hsv2rgb(double h, double s, double v)
{
	double r, g, b;
	double f, p, q, t;
	int i;

	i = (int)floor(h * 6);
	f = h * 6 - i;
	p = v * (1 - s);
	q = v * (1 - f * s);
	t = v * (1 - (1 - f) * s);
	switch (i) {
	case 0: r = v; g = t; b = p; break;
	case 1: r = q; g = v; b = p; break;
	case 2: r = p; g = v; b = t; break;
	case 3: r = p; g = q; b = v; break;
	case 4: r = t; g = p; b = v; break;
	case 5: r = v; g = p; b = q; break;
	}

	return
		((int)round(r * 255)) << 24 |
		((int)round(g * 255)) << 16 |
		((int)round(b * 255));
}

int main()
{
	int fd, i, k, p;
	uint8_t data[] = {
		0x00,
		0x01,
		0x00, 0x00, 0x00,
		0x00, 0x00, 0x00,
		0x00, 0x00, 0x00,
		0x00, 0x00, 0x00,
		0x00, 0x00, 0x00,
		0x00, 0x00, 0x00,
		0x3f, 0x3f, 0x3f,
		0x00
	};
	uint8_t reset[] = {
		0x17,
		0x00
	};

	fd = open(IIC, O_RDWR);
	if (fd == -1)
		err(1,"open %s",IIC);

	p = 0;
	for (k=0; k<=144; ++k) {
		for (i=0; i<6; ++i) {
			double hue;
			unsigned long pix;
			int r,g,b;

			hue = (6 * i + p) % 36 / 36.0;

			pix = hsv2rgb(hue, 1.0, 1.0);

			r = (pix >> 24) & 0xff;
			g = (pix >> 16) & 0xff;
			b = (pix >> 0) & 0xff;
			rgb(data, i, r, g, b);
		}
		p = (p+1) % 144;

		iic_write(fd, ADR, NULL, 0, data, sizeof(data));
		usleep(100);
	}

	for (i=0; i<6; ++i)
		rgbshade(data, i, 85);
	iic_write(fd, ADR, NULL, 0, data, sizeof(data));
	// iic_write(fd, ADR, NULL, 0, reset, sizeof(reset));

	close(fd);
}
