/*
 * menu0_main.c
 * begin 20190527 true
 *
 * main menu functions
 * nametag display
 */

#include "menu.h"

#include "render/font.h"

#include "misc/accel.h"
#include "misc/sin7.h"

#include "global.h"
#include "user_config.h"

#include <string.h>
#include <stdio.h>



#define ROTATE_TARGET_WIDTH		        40
#define ROTATE_TARGET_HEIGHT	        32

#define ROTATE_NAME_LEN_FULLRATE		15		// set to length in characters of name to run at
#define ROTATE_NAME_LEN_HALFRATE		15		// the specified rate, max. exceeding half rate
												// name length will not allow dynamic names.
												// if your MCU has i2c DMA, set these to max name.
												// if no i2c DMA, then set based on reported CPU
												// usage per update rate, and tweak update rate.

#define DEMOWAVE_SPD1                   3       // how much to add each wave frame (max 256)
#define DEMOWAVE_ADD1                   13      // how much to add to each character during rendering
#define DEMOWAVE_SPD2                   5
#define DEMOWAVE_ADD2                   25



uint8_t rotsrc_fb[(ROTATE_TARGET_WIDTH * (ROTATE_TARGET_HEIGHT >> 3)) + 1];
uint8_t rotdst_fb[(ROTATE_TARGET_WIDTH * (ROTATE_TARGET_HEIGHT >> 3)) + 1];

SSD1306 rotsrc;
SSD1306 rotdst;


uint8_t wiggle = 1;
int8_t waves = 0;



static void menu_none_init()
{
	rotsrc.width = rotdst.width   = ROTATE_TARGET_WIDTH;
	rotsrc.height = rotdst.height = ROTATE_TARGET_HEIGHT;

	rotsrc.mode = rotsrc_fb;
	rotsrc.fb = rotsrc_fb + 1;

	rotdst.mode = rotdst_fb;
	rotdst.fb = rotdst_fb + 1;
}

static void menu_none_set_halfwidth(SSD1306 *dst)
{
	if (uconf.nameconf & UCONF_NAME_MODE_HALFWIDTH) {
		dst->state |=  SSD1306_STATE_STR_HALFWIDTH;
	} else {
		dst->state &= ~SSD1306_STATE_STR_HALFWIDTH;
	}
}

static void menu_none_set_flipmirror(uint8_t flip_flag)
{
	if (uconf.nameconf & UCONF_NAME_MODE_AUTOROTATE) {
		if (sysflags & flip_flag) {
			ssd1306_set_flipmirror(1);
		} else {
			ssd1306_set_flipmirror(0);
		}
	} else if (uconf.nameconf & UCONF_NAME_MODE_ROTATE180) {
		ssd1306_set_flipmirror(1);
	} else {
		ssd1306_set_flipmirror(0);
	}
}


const uint8_t err_namewide_1[] = "Name Too Wide";
const uint8_t err_namewide_2[] = "Shorten the name, reduce";
const uint8_t err_namewide_3[] = "spacing or try a smaller font.";

const uint8_t err_namelong_1[] = "Name Too Long";
const uint8_t err_namelong_2[] = "Names 10 to 15 chars long";
const uint8_t err_namelong_3[] = "must use Horizontal orientation.";

static void menu_none_print_error(uint8_t *err1, uint8_t *err2, uint8_t *err3)
{
	ssd1306fb_set_cursor(0, 0);
	ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, (const char *)err1, 0);
	ssd1306fb_set_cursor(0, 12);
	ssd1306fb_draw_str(font_Dialog_plain_8,           (const char *)err2, 0);
	ssd1306fb_set_cursor(0, 20);
	ssd1306fb_draw_str(font_Dialog_plain_8,           (const char *)err3, 0);
	return;
}

__HIGH_CODE
static int8_t rotate_copy(int8_t top, int8_t left, int8_t rot, uint8_t font_idx, uint8_t *txt)
{
    int8_t o, w;

    // set target buffer, need to do this before width calc
    // as this will have the halfwidth bit set appropriately
    ssd1306fb_set_target(&rotsrc);

    // get left drawing position, so the character is rendered centered
    o = ssd1306fb_get_str_width(font_table[font_idx].font, txt, 1, 0);
    w = ((rotsrc.width >> 1) - (o >> 1));

    // clear buffers before work
    ssd1306_cls(&rotdst);
    ssd1306_cls(&rotsrc);

    // render and rotate
    ssd1306fb_set_cursor(w, top);
    ssd1306fb_draw_str(font_table[uconf.font_idx].font, txt, 0);
    ssd1306fb_rotate(&rotsrc, &rotdst, rot);

    // copy dst buffer to oled buffer in correct position
    ssd1306fb_set_target(&oled);
    ssd1306fb_copy(left - w, 0, ROTATE_TARGET_WIDTH, ROTATE_TARGET_HEIGHT, rotdst.fb);

    return o;
}

__HIGH_CODE
void menu_none_disp(uint8_t idx)
{
	uint8_t i, j;
	uint8_t o;
	uint16_t w;

	int8_t left, top;
	int8_t rot;
	int8_t v, s;

	int8_t add, speed;

	uint8_t disp_mode;

	char txt[6] = {0};


	menu_none_init();

	// total width and left / starting boundary
	menu_none_set_halfwidth(&rotsrc);
	ssd1306fb_set_target(&rotsrc);

	w = ssd1306fb_get_str_width(font_table[uconf.font_idx].font,
				uconf.name,
				strlen(uconf.name),
				uconf.char_spacing);

	if (w > oled.width + 16) {
		menu_none_print_error((uint8_t *)err_namewide_1, (uint8_t *)err_namewide_2, (uint8_t *)err_namewide_3);
	}

	left = (oled.width >> 1) - (w >> 1);
	top = oled.height - ssd1306fb_get_font_height(font_table[uconf.font_idx].font) - 1;
	top += uconf.y_offset;

	// get rotation
	rot = accel_get_rotation(&accel);

	// render modes
	disp_mode = uconf.nameconf & UCONF_NAME_DISP_MASK;
	switch (disp_mode) {
		case UCONF_NAME_DISP_STATIC_HORIZ: {
			menu_none_set_flipmirror(SYS_OLED_ROTATE_X);

			menu_none_set_halfwidth(&oled);
			ssd1306fb_set_target(&oled);

			ssd1306fb_set_cursor(left, top);
			ssd1306fb_draw_str(font_table[uconf.font_idx].font, uconf.name, uconf.char_spacing);
			break;
		}

		case UCONF_NAME_DISP_STATIC_VERT: {
			menu_none_set_flipmirror(SYS_OLED_ROTATE_Y);

			rot = 95;

			goto MENU_0_DISP_CHAR_ROTATE;
		}
		case UCONF_NAME_DISP_WIGGLE: {
			menu_none_set_flipmirror(SYS_OLED_ROTATE_X);

			// why this value? why not. value is the "speed" of the wiggle
			wiggle += MISC_WIGGLE_RATE;
			wiggle += (uconf.framemod == UCONF_FRAMERATE_FULL) ? 0 : MISC_WIGGLE_RATE;
			// the shift is the maximum "angle" of the wiggle
			rot = (cos7(wiggle) >> MISC_WIGGLE_SHIFT);

			goto MENU_0_DISP_CHAR_ROTATE;
		}
		case UCONF_NAME_DISP_CHAR_ROTATE: {
			ssd1306_set_flipmirror(0);

MENU_0_DISP_CHAR_ROTATE:
			// set pixel printing color - only set it on the output, not on rotation buffers
			ssd1306fb_set_target(&oled);
			ssd1306fb_set_color(uconf.nameconf & UCONF_NAME_MODE_COLOR_INVERT ?
						SSD1306_STATE_INVERT_PIXEL : SSD1306_STATE_SET_PIXEL);

			j = strlen(uconf.name);

			// set framerate, or abort if invalid
			if (j > ROTATE_NAME_LEN_FULLRATE) {
				if (j > ROTATE_NAME_LEN_HALFRATE) {
					menu_none_print_error((uint8_t *)err_namelong_1, (uint8_t *)err_namelong_2, (uint8_t *)err_namelong_3);
					return;
				} else {
					uconf.framemod = UCONF_FRAMERATE_HALF;
				}
			} else {
				uconf.framemod = UCONF_FRAMERATE_FULL;
			}

			// configure rotation buffer
			ssd1306fb_set_target(&rotsrc);
			ssd1306fb_set_color(SSD1306_STATE_SET_PIXEL);
			menu_none_set_halfwidth(&rotsrc);

			for (i = 0; i < strlen(uconf.name); i++) {
				j = i;

				// change character order
				if ((uconf.nameconf & UCONF_NAME_DISP_MASK) == UCONF_NAME_DISP_CHAR_ROTATE) {
					// this method is a bit jumpy, but works for now
					if (sysflags & SYS_OLED_REVERSE_CHARS) {
						j = strlen(uconf.name) - i - 1;
					}
				}

				txt[0] = uconf.name[j];

				o = rotate_copy(top, left, rot, uconf.font_idx, txt);

				// update position of character based on spacing
				left += o + uconf.char_spacing;
			}

			break;
		}
		case UCONF_NAME_DISP_DEMOWAVE1:
		case UCONF_NAME_DISP_DEMOWAVE2: {
		    add   = (disp_mode == UCONF_NAME_DISP_DEMOWAVE1) ? DEMOWAVE_ADD1 : DEMOWAVE_ADD2;
		    speed = (disp_mode == UCONF_NAME_DISP_DEMOWAVE1) ? DEMOWAVE_SPD1 : DEMOWAVE_SPD2;

		    menu_none_set_flipmirror(SYS_OLED_ROTATE_X);

		    // set pixel printing color - only set it on the output, not on rotation buffers
            ssd1306fb_set_target(&oled);
            ssd1306fb_set_color(uconf.nameconf & UCONF_NAME_MODE_COLOR_INVERT ?
                        SSD1306_STATE_INVERT_PIXEL : SSD1306_STATE_SET_PIXEL);

            j = strlen(uconf.name);

            v = waves;                                  // per-character angle
            v = (uint8_t)v + (uint8_t)speed;
            waves = v;                                  // per-frame starting angle

            ssd1306fb_set_target(&oled);

            top -= (ssd1306fb_get_font_height(font_table[uconf.font_idx].font) - 1) >> 2;
            top--;

            for (j = 0; j < strlen(uconf.name); j++) {
                txt[0] = uconf.name[j];
                o = ssd1306fb_get_str_width(font_table[uconf.font_idx].font, txt, 1, 0);

                v = (uint8_t)v + (uint8_t)add;

                s = sin7(v);                    // get character's position on y
                s += 0x7f;                      // offset center
                s = (uint8_t)s >> 4;            // decrease magnitude

                ssd1306fb_set_cursor(left, top + s);
                ssd1306fb_draw_str(font_table[uconf.font_idx].font, txt, 0);

                left += o + uconf.char_spacing; // next character
            }

            break;
		}
	}

	ssd1306fb_set_target(&oled);

	if (uconf.flags & UCONF_FLAGS_SHOW_ACCEL_ANGLE) {
		sprintf(txt, "%+3d", accel_get_rotation(&accel));
		ssd1306fb_set_cursor(90, 0);
		ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
	}

	if (uconf.flags & UCONF_FLAGS_SHOW_CPU_USAGE) {
		sprintf(txt, "%3u%%", cpu_pct);
		ssd1306fb_set_cursor(90, 20);
		ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
	}

	oled.state &= ~SSD1306_STATE_STR_HALFWIDTH;
}

__HIGH_CODE
void menu_0_disp(uint8_t idx)
{
	char txt[14];
	// uint8_t w;

	ssd1306fb_set_color(SSD1306_STATE_SET_PIXEL);

	// circle next to item
	ssd1306fb_draw_circle(24, 16, (mtick & 0x04) ? 2 : 1);

	// which item selected?
	ssd1306fb_set_cursor(32, 9);
	switch (idx) {
		case 0: strcpy(txt, "Nametag!");	    break;
		case 1: strcpy(txt, "Name Setup");	    break;
		case 2: strcpy(txt, "RGB Setup");	    break;
		case 3: strcpy(txt, "Messages");	    break;
		case 4: {
		    ssd1306fb_set_cursor(32, 9);
		    ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "I", 1);

		    ssd1306fb_set_cursor(39, 8);
		    ssd1306fb_draw_str(font_Dialog_plain_8, "2", 1);

		    ssd1306fb_set_cursor(45, 9);
		    strcpy(txt, "C Sniffer");
		    break;
		}
		case 5: strcpy(txt, "Options");		    break;
		case 6: strcpy(txt, "About & Dbg");     break;
	}
	ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);

	// disabled / incomplete entries
	/*
	switch (idx) {
		case 4: {
			w = ssd1306fb_get_str_width(font_DejaVu_Sans_Mono_Bold_11, txt, strlen(txt), 1);
			ssd1306fb_draw_hline(32, w + 32, 16);
			ssd1306fb_draw_hline(32, w + 32, 17);
		}
	}
	*/

	// draw extras
	menu_draw_tabs(idx);
	menu_draw_buttons(MENU_BTNSTYLE_MAIN, idx);
}

void menu_0_enter(uint8_t idx)
{
	switch (idx) {
		case 0: {
			menu_stop(0);
			return;
		}
		case 1: {
			menu = (MenuItem *)&menu_1_name_setup;
			menu_idx = 0;
			return;
		}
		case 2: {
			menu = (MenuItem *)&menu_2_led_setup;
			menu_idx = 0;
			return;
		}
		case 3: {
			return;
		}
		case 4: {
			return;
		}
		case 5: {
			menu = (MenuItem *)&menu_5_options;
			menu_idx = 0;
			return;
		}
		case 6: {
			menu = (MenuItem *)&menu_6_about;
			menu_idx = 0;
			return;
		}
	}
}
