#include <Arduino.h>

#include "SPI.h"
// #include "GC9A01.h"
#include <Arduino_GFX_Library.h>
#include <U8g2lib.h>

extern "C" {
#include "hsv2rgb.h"
#include "sin1.h"
#include "sin7.h"
}


const char name[] = "true";



#define SPI0_SCK		2
#define SPI0_MISO		GFX_NOT_DEFINED
#define SPI0_MOSI		3

#define DISP_NSS		1
#define DISP_RESET  	4
#define DISP_DC     	5

#define TEXT_SIZE       5
#define ROT_SIZE        70

#define LETTER_SPACING  1898    // user-configurable


Arduino_DataBus *bus = new Arduino_RPiPicoSPI(DISP_DC, DISP_NSS, SPI0_SCK, SPI0_MOSI, SPI0_MISO, spi0);
Arduino_GFX *disp = new Arduino_GC9A01(bus, DISP_RESET, 0 /* rotation */, true /* IPS */);

Arduino_Canvas *dout  = new Arduino_Canvas(240 /* width */, 240 /* height */, disp);
Arduino_Canvas *drot  = new Arduino_Canvas(ROT_SIZE, ROT_SIZE, disp);


color_hsv hsv = {0, 255, 255};
color_rgb rgb;


/*
void GC9A01_set_reset(uint8_t val) {
    digitalWrite(DISP_RESET, val);
}

void GC9A01_set_data_command(uint8_t val) {
    digitalWrite(DISP_DC, val);
}

void GC9A01_set_chip_select(uint8_t val) {
    digitalWrite(DISP_NSS, val);
}

void GC9A01_spi_tx(uint8_t *data, size_t len) {
    while (len--) {
        SPI.transfer(*data);
        data++;
    }
}

void GC9A01_delay(uint16_t ms) {
    delay(ms);
}

void setup() {
    
    pinMode(DISP_RESET, OUTPUT);
    pinMode(DISP_DC, OUTPUT);
    pinMode(DISP_NSS, OUTPUT);

	SPI.setRX(SPI0_MISO);
	SPI.setSCK(SPI0_SCK);
	SPI.setTX(SPI0_MOSI);

    SPI.begin();

    GC9A01_init();
    struct GC9A01_frame frame = {{0,0},{239,239}};
    GC9A01_set_frame(frame);
}

void loop() {
    uint8_t color[3];

    // Triangle
    color[0] = 0xFF;
    color[1] = 0xFF;
    for (int x = 0; x < 240; x++) {
        for (int y = 0; y < 240; y++) {
            if (x < y) {
                color[2] = 0xFF;
            } else {
                color[2] = 0x00;
            }
            if (x == 0 && y == 0) {
                GC9A01_write(color, sizeof(color));
            } else {
                GC9A01_write_continue(color, sizeof(color));
            }
        }
    }

    delay(1000);

    // Rainbow
    float frequency = 0.026;
    for (int x = 0; x < 240; x++) {
        color[0] = sin(frequency*x + 0) * 127 + 128;
        color[1] = sin(frequency*x + 2) * 127 + 128;
        color[2] = sin(frequency*x + 4) * 127 + 128;
        for (int y = 0; y < 240; y++) {
            if (x == 0 && y == 0) {
                GC9A01_write(color, sizeof(color));
            } else {
                GC9A01_write_continue(color, sizeof(color));
            }
        }
    }

    delay(1000);

    // Checkerboard
    for (int x = 0; x < 240; x++) {
        for (int y = 0; y < 240; y++) {
            if ((x / 10) % 2 ==  (y / 10) % 2) {
                color[0] = 0xFF;
                color[1] = 0xFF;
                color[2] = 0xFF;
            } else {
                color[0] = 0x00;
                color[1] = 0x00;
                color[2] = 0x00;
            }
            if (x == 0 && y == 0) {
                GC9A01_write(color, sizeof(color));
            } else {
                GC9A01_write_continue(color, sizeof(color));
            }
        }
    }

    delay(1000);
}
*/

#define FONT_JUMPTABLE_BYTES	4

#define FONT_JUMPTABLE_MSB		0
#define FONT_JUMPTABLE_LSB		1
#define FONT_JUMPTABLE_SIZE		2
#define FONT_JUMPTABLE_WIDTH	3
#define FONT_JUMPTABLE_START	4

#define FONT_WIDTH_POS			0
#define FONT_HEIGHT_POS			1
#define FONT_FIRST_CHAR_POS		2
#define FONT_CHAR_NUM_POS		3

int16_t font_get_str_width(const uint8_t *font, const char *str, uint8_t len, int8_t extra_spacing)
{
	uint8_t first = font[FONT_FIRST_CHAR_POS];
	uint8_t chr_width;
	uint8_t str_width = 0;
	uint8_t w;

	w = len;

	while (w--) {
		chr_width = font[FONT_JUMPTABLE_START + (str[w] - first) * FONT_JUMPTABLE_BYTES + FONT_JUMPTABLE_WIDTH];
		str_width += chr_width;
	}

	str_width += extra_spacing * (len - 1);

	return str_width;
}

void setup()
{
    Serial.begin(115200);

    Serial.println("starting...");

    delay(50);

    bus->begin(90000000);

    if (!disp->begin()) {
        Serial.println("failed to start disp");
    }

    dout->begin();
    dout->fillScreen(BLACK);
    dout->flush();

    drot->begin();
    drot->setFont(u8g2_font_cubic11_h_cjk);     // u8g2_font_spleen16x32_mr
    drot->setTextSize(TEXT_SIZE);
}

void rotate_letter(uint8_t idx, int16_t rot, int16_t out_x, int16_t out_y, uint16_t color)
{
    int16_t x, y;
    int16_t nx, ny;
    int16_t sx, sy;

    int16_t start_x, start_y;

    int16_t x_width;

    uint16_t *src, *dst;
    uint16_t src_pxl;
    uint16_t bgcolor = CYAN;

    // clear the rotation buffer
    drot->fillScreen(CYAN);

    // print the letter to the center of the rotation buffer
    drot->setCursor(0, TEXT_SIZE * 10);
    drot->setTextColor(color);
    drot->print(name[idx]);
    x_width = drot->getCursorX() - TEXT_SIZE;

    // framebuffers
    src = drot->getFramebuffer();
    dst = dout->getFramebuffer();

    // position
    start_x = out_x + 120;
    start_y = out_y + 120;

    // rotate+copy the buffer
    for (y = 0; y < ROT_SIZE; y++) {
		for (x = 0; x < x_width; x++) {
            src_pxl = src[x + (y * ROT_SIZE)];
            sx = x                + 1 - (x_width >> 1);
            sy = y - (ROT_SIZE/2) + 1;
			if (src_pxl != bgcolor) {
				nx = ((sx * cos1(rot)) - (sy * sin1(rot))) >> 15;
				ny = ((sx * sin1(rot)) + (sy * cos1(rot))) >> 15;

				nx += start_x;
                ny += start_y;
                
                if ((nx >= 0) && (nx < 240)) {
					if ((ny >= 0) && (ny < 240)) {
						dst[nx + (ny*240)] = src_pxl;
					}
				}
			}
		}
	}
}

int16_t rot = 0;

void loop()
{
    uint16_t hue;

    hsv.h += 24;

    // background fill
    hue = hsv.h + 512;
    hsv.h %= 1536;
    hsv2rgb_8b(hue, 255, 128, &rgb.r, &rgb.g, &rgb.b);
    // dout->fillScreen(dout->color565(rgb.r, rgb.g, rgb.b));
    dout->fillScreen(BLACK);

    hue = hsv.h;
    
    for (int8_t i = 120; i > 0; i--) {
        hue += 2;
        hue %= 1536;
        hsv2rgb_8b(hue, 220, (i * 2), &rgb.r, &rgb.g, &rgb.b);

        dout->drawCircle(120, 120, i, dout->color565(rgb.r, rgb.g, rgb.b));
    }

    hue = hsv.h + 768;
    hue %= 1536;
    hsv2rgb_8b(hue, 224, 64, &rgb.r, &rgb.g, &rgb.b);

    rot += 420 - 69;

    int16_t x, y;
    int16_t d;
    d = rot;
    for (uint8_t i = 0; i < strlen(name); i++) {    
        x = cos1(d);
        x = (x >> 9) + (x >> 11) + (x >> 12);
        
        y = sin1(d); // sin1(d) * 21) / 32;
        y = (y >> 9) + (y >> 11) + (y >> 12);

        rotate_letter(i, d + 0x6000, x, y, dout->color565(rgb.r, rgb.g, rgb.b));
        // d -= 9; // for 7-bit version
        d -= LETTER_SPACING; // for 15-bit version
    }

    dout->flush();
}