/*
 * LED ring
 *
 * A charlieplexed 20 LED blinker with ATTiny45. Space efficient
 * daisychained LED placing with shared anode/cathode soldering pads.
 *
 * Video of the real device: https://www.youtube.com/embed/Tuk5kyZv42A
 *
 */
//
// With (PB5 PB4 PB3 PB2 PB1 PB0) (ATTiny)
// build a lo, hi, tri-state matrix.
//
// Powerdown after UPTIME_MAX xxx (~xx sec). Press reset to wake up.
//
// ATMEL ATTINY13a
//
//       +-\/-+
// PB5  1|.   |8  Vcc
// PB3  2|    |7  PB2
// PB4  3|    |6  PB1
// GND  4|    |5  PB0
//       +----+

extern "C" {
#include "led_map.h"
#include "power_down.h"
#include "delay.h"
}
#include <avr/io.h>
#include <avr/pgmspace.h>
// #include "bootloader.h"

#define STEP_LENGTH  4000
#define UPTIME_SECONDS(s) (s /* s */ * 1000000 / STEP_LENGTH)
#define UPTIME_MAX UPTIME_SECONDS(60)

#define BUTTON_RELEASED(_) (PINB & (1 << 5))

static
uint32_t uptime = 0;

static inline
void setup(void) {
//	MCUSR = 0;	// reset watchdog
	PORTB = _BV(5);			// All input with pull up on PB5
	DDRB = 0x00;			// Input
	delay_ms(10);	// wait for stable PB5
}

static const unsigned char period_length[] PROGMEM = {
	21, 22 //, 23, 24
};
#define N_ID (sizeof(period_length)/sizeof(period_length[0]))

typedef struct {
	unsigned char period;
	unsigned char led;
} animation_state_t;

animation_state_t animation_state[N_ID];

void animation_step(uint8_t id) {
	animation_state_t *s = animation_state + id;
	s->period++;

	if (s->period < pgm_read_byte(period_length + id)) {
		return;
	}
	s->period = 0;

	led_map_set(s->led, !led_map_get(s->led));

	s->led = (s->led + 1) % LED_COUNT;
}

static inline
void uptime_step(void) {
	if (++uptime > UPTIME_MAX) {
		do {
			power_down();

			// Wait for high
		} while (!BUTTON_RELEASED());

		// Debounce
		delay_ms(50);
		uptime = 0;
	}
}

static inline
void comm_step(void) {
	if (!BUTTON_RELEASED()) { // && (uptime > UPTIME_SECONDS(2))) {
		// power down in 0.2 sec or earlier
		uint32_t newuptime = UPTIME_MAX - UPTIME_SECONDS(0.2);
		if (newuptime > uptime) {
			uptime = newuptime;
		}
	}
}

static inline
void loop() {
	unsigned id;
	led_map_step();
	for (id = 0; id < N_ID; id++) {
		animation_step(id);
	}

	uptime_step();
	comm_step();
	delay_us(STEP_LENGTH);
}

/*
int main(void)
{
	setup();
	while (1) loop();
	return 0;
}

/*
 *  compile-command: "make sflash@led_circle"
 * Local Variables:
 *  compile-command: "make flash@led_circle"
 * End:
 */
ATTINY8520PU