/*
 * bootmenu.c - Boot menu
 *
 * Copyright (C) 2006-2007 by OpenMoko, Inc.
 * Written by Werner Almesberger <werner@openmoko.org>
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include <common.h>
#include <environment.h>

#include "neo1973.h"


#define ANSI_CLEAR	"\e[2J"
#define ANSI_REVERSE	"\e[7m"
#define	ANSI_NORMAL	"\e[m"
#define ANSI_GOTOYX	"\e[%d;%dH"

#define DEBOUNCE_LOOPS		1000	/* wild guess */
#define BOOT_MENU_TIMEOUT	60	/* 60 second */
#define AFTER_COMMAND_WAIT	3	/* wait (2,3] after running commands */
#define	MAX_MENU_ITEMS		10	/* cut off after that many */

#define BOOT_TXT	"Boot"
#define	FACTORY_TXT	"Factory reset"


static int options = 0;
static int width = sizeof(FACTORY_TXT)-1;


static int debounce(int (*fn)(void))
{
	int on, i;

again:
	on = fn();
	for (i = DEBOUNCE_LOOPS; i; i--)
		if (on != fn())
			goto again;
	return on;
}


static char *get_option(int n)
{
	char name[] = "menu_XX";

	sprintf(name+5,"%d",n);
	return getenv(name);
}


static void print_option_n(int n)
{
	char *s, *colon;
	int len;

	if (!n) {
		printf("  %-*s  ", width, BOOT_TXT);
		return;
	}
	if (n == options+1) {
		printf("  %-*s  ", width, FACTORY_TXT);
		return;
	}
	s = get_option(n);
    	if (!s)
		return;
	colon = strchr(s, ':');
	if (colon)
		*colon = 0;
	len = strlen(s);
	if (len > width)
		width = len;
	printf("  %-*s  ", width, s);
	if (colon)
		*colon = ':';
}


static void print_option(int n, int reverse)
{
	printf(ANSI_GOTOYX, n+4, 1);
	if (reverse)
		printf(ANSI_REVERSE);
	print_option_n(n);
	if (reverse)
		printf(ANSI_NORMAL);
}


static const char *option_command(int n)
{
	const char *s, *colon;

	s = get_option(n);
	if (!s)
		return NULL;
	colon = strchr(s, ':');
	return colon ? colon+1 : s;
}


static int do_bootmenu(void)
{
	int aux = 1, on = 1;
	int n, seconds = 0;

	printf(ANSI_CLEAR ANSI_GOTOYX "*** BOOT MENU ***\n\n", 2, 1);
	print_option(0, 1);
	while (options < MAX_MENU_ITEMS && option_command(options+1))
		print_option(++options, 0);
	print_option(options+1, 0);
	printf("\n\nPress [AUX] to select, [POWER] to execute.\n");

	n = 0;
	while (1) {
		int tmp;

		tmp = debounce(neo1973_911_key_pressed);
		if (tmp && !aux) {
			print_option(n, 0);
			n++;
			if (n == options+2)
				n = 0;
			print_option(n, 1);
			seconds = 0;
		}
		aux = tmp;
		tmp = debounce(neo1973_on_key_pressed);
		if (tmp && !on)
			return n;
		on = tmp;
		if (neo1973_new_second())
			if (++seconds > BOOT_MENU_TIMEOUT)
				return -1;
	}
}


void bootmenu(void)
{
	console_assign(stdout, "vga");
	console_assign(stderr, "vga");
	while (1) {
		int n, seconds;

		options = 0;
		n = do_bootmenu();
		if (n < 0)
			return;

		printf(ANSI_CLEAR ANSI_GOTOYX, 1, 1);
		if (!n || n == options+1) {
			if (n) {
				default_env();
				run_command("dynpart", 0);
			}
			run_command("bootd", 0);
			if (!n)
				return;
		}
		else
			run_command(option_command(n), 0);
		seconds = AFTER_COMMAND_WAIT;
		while (seconds)
			if (neo1973_new_second())
				seconds--;
	}
}
