#include <inttypes.h>
#include <stdlib.h> // for NULL

#include "lights.h"

static pattern_t panic_pattern[] = {
	{ 3, 1 }, // FIXME: will be 4, but let's be safe while testing
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	{ 3, 1 },
	{ 0, 1 },
	PATTERN_END
};

pattern_t on1_pattern [] = {
	{ 1, 0x10 },
	PATTERN_END
};

static pattern_t on2_pattern [] = {
	{ 2, 0x10 },
	PATTERN_END
};

static pattern_t on3_pattern [] = {
	{ 3, 0x10 },
	PATTERN_END
};

static pattern_t brake_pattern [] = {
	{ 4, 0x2 },
	{ 3, 0x8 },
	PATTERN_END
};

static pattern_t normal2_pattern[] = {
	{ 2, 0x1 },
	{ 0, 0x1 },
	{ 2, 0x1 },
	{ 0, 0x8 },
	{ 1, 0x1 },
	{ 0, 0x1 },
	{ 1, 0x1 },
	{ 0, 0x8 },
	PATTERN_END
};

static pattern_t normal3_pattern[] = {
	{ 3, 0x1 },
	{ 0, 0x1 },
	{ 3, 0x1 },
	{ 0, 0x8 },
	{ 1, 0x1 },
	{ 0, 0x1 },
	{ 1, 0x1 },
	{ 0, 0x8 },
	PATTERN_END
};

static pattern_t normal4_pattern[] = {
	{ 4, 0x1 },
	{ 0, 0x1 },
	{ 4, 0x1 },
	{ 0, 0x8 },
	{ 1, 0x1 },
	{ 0, 0x1 },
	{ 1, 0x1 },
	{ 0, 0x8 },
	PATTERN_END
};

static pattern_t slow1_pattern[] = {
	{ 1, 0x01 },
	{ 0, 0x10 },
	PATTERN_END
};

static pattern_t slow2_pattern[] = {
	{ 2, 0x01 },
	{ 0, 0x10 },
	PATTERN_END
};

static pattern_t slow3_pattern[] = {
	{ 3, 0x01 },
	{ 0, 0x10 },
	PATTERN_END
};

static unsigned char dim_mode, towbar_mode, braking;

void init_control()
{
	dim_mode = 0;
	towbar_mode = 0;
	braking = 0;
}

void brake_on()
{
	braking = 1;
	gpio_set(0, 1);
	led_set_pattern(N_PWMLEDS, status_led_pattern_select());
	led_set_pattern(0, pwmled0_pattern_select());
}

void brake_off()
{
	braking = 0;
	gpio_set(0, 0);
	led_set_pattern(N_PWMLEDS, status_led_pattern_select());
	led_set_pattern(0, pwmled0_pattern_select());
}

void toggle_dim_mode()
{
	dim_mode = !dim_mode;
	pattern_reload();
}

void set_panic_mode()
{
	if (!dim_mode)
		led_set_pattern(0, panic_pattern);

	led_set_pattern(1, panic_pattern);
	led_set_pattern(2, panic_pattern);
	led_set_pattern(4, panic_pattern);
}

pattern_t *pwmled0_pattern_select()
{
	if (battery_critical)
		return on1_pattern;

	if (towbar_mode)
		return NULL;

	if (braking)
		return brake_pattern;

	switch (ambient_zone) {
	case 0: return dim_mode ? NULL : number_pattern(2, 1);
	case 1: return dim_mode ? slow1_pattern : normal2_pattern;
	case 2: return dim_mode ? slow2_pattern : normal3_pattern;
	case 3:
	default: return dim_mode ? slow3_pattern : normal4_pattern;
	}
}

pattern_t *pwmled1_pattern_select()
{
#ifndef TESTING_FW
	return NULL;
#else
	if (battery_critical)
		return on1_pattern;
#endif

	if (towbar_mode) {
		switch (ambient_zone) {
		case 0:
		case 1:
			return dim_mode ? on2_pattern : on1_pattern;
		case 2: return dim_mode ? NULL : on2_pattern;
		case 3:
		default: return dim_mode ? NULL : on3_pattern;
		}
	} else {
		switch (ambient_zone) {
		case 0: return dim_mode ? slow1_pattern : normal2_pattern;
		case 1: return dim_mode ? slow2_pattern : normal3_pattern;
		case 2: return dim_mode ? NULL : normal4_pattern;
		case 3:
		default: return NULL;
		}
	}
}

pattern_t *pwmled2_pattern_select()
{
#ifndef TESTING_FW
	if (battery_critical)
		return on1_pattern;
#endif

	switch (ambient_zone) {
	case 0: return dim_mode ? on2_pattern : on3_pattern;
	case 1: return dim_mode ? slow1_pattern : normal2_pattern;
	case 2:
	case 3:
	default:
		return dim_mode ? slow2_pattern : normal3_pattern;
	}
}

pattern_t *status_led_pattern_select()
{
	if (braking)
		return on1_pattern;

	if (buttons_setup_in_progress())
		return buttons_setup_status0_pattern_select();

	// FIXME: do something sane
	return number_pattern(battery_gauge(), 0);
}

pattern_t *illumination_led_pattern_select()
{
	if (battery_critical)
		return NULL;

	switch (ambient_zone) {
	case 0: return dim_mode
		? number_pattern(1, 1)
		: on1_pattern;
	case 1: return dim_mode
		? number_pattern(2, 1)
		: number_pattern(3, 1);
	case 2: return dim_mode
		? number_pattern(1, 0)
		: number_pattern(2, 0);
	case 3:
	default: return dim_mode
		? number_pattern(3, 0)
		: number_pattern(4, 0);
	}
}

pattern_t *laser_pattern_select()
{
	if (!dim_mode && ambient_zone <= 1)
		return number_pattern(2, 1);
	else
		return NULL;
}
