#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);
}