#include <stdio.h>

#include "oswald.h"
#include "oswald_main.h"
#include "oswald_watch_faces.h"
#include "oswald_graphics.h"
#include "oswald_hal.h"

#include "dayslib.h"
#include "mymem.h"
#include "strutils.h"

#include "oswald_screens.h"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/startstopbutton_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/lapsebutton_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/upbutton_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/downbutton_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/leftbutton_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/rightbutton_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/enterbutton_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/checked_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/unchecked_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/main_menu_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/timesetup_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/stopwatch_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/alarm_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/bluetooth_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/info_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/acc_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/message_icon.xbm"

#if defined(__GNUC__) && (__MSP430X__ > 0)
__attribute__((__far__))
#endif
#include "bitmaps/exit_icon.xbm"


/*
 * Common event handler part of the watch faces
 */
typedef struct {
	void (*screendraw_func)(boolean show_seconds);
	boolean show_seconds;
	int curclock;
} idle_data_t;
static idle_data_t idle_screen = {
	DrawLcdDigitalClock,
	TRUE,
	0,
};
static struct {
	void (*screendraw_func)(boolean show_seconds);
} clocks[]={
		{DrawLcdDigitalClock},
		{DrawLcdDigitalClock2},
		{DrawLcdAnaClock},
		{DrawCalendarClock},
		{DrawHelpClock}
};

 
event_ret_t idle_handle_user_buttons(watch_button button)
{
	switch (button) {
		case BUTTON_A:
		case BUTTON_C:
			idle_screen.curclock+=(button==BUTTON_C)?1:((sizeof(clocks)/sizeof(clocks[0]))-1);
			idle_screen.curclock%=(sizeof(clocks)/sizeof(clocks[0]));
			idle_screen.screendraw_func = clocks[idle_screen.curclock].screendraw_func;
			break;
		case BUTTON_B:
			OswaldState.screen_id = CALENDAR_OVERVIEW_SCREEN;
			OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
			OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_E:
			OswaldState.screen_id = MAIN_MENU_SCREEN;
			OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
			OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_F:
			OswaldState.screen_id = DATETIME_SETTING_SCREEN;
			OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
			OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			return EVENT_RET_HANDLED;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};

	idle_screen.screendraw_func(idle_screen.show_seconds);

	return EVENT_RET_HANDLED;
}

event_ret_t idle_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_ONE_SEC_TIMER:
		case EVENT_SCREEN_VISIBLE:
			idle_screen.screendraw_func(idle_screen.show_seconds);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return idle_handle_user_buttons(*(watch_button *)data);
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_UNHANDLED;
}


/*
 * Main Menu Screen
 */
typedef struct {
	int8_t pos;
	uint8_t tmo;
} main_menu_data_t;
static main_menu_data_t main_menu_screen = {
	ALARM_SETUP_SCREEN,
	0,
};

#define MAIN_MENU_GRID_PIXEL	84
#define MAIN_MENU_GRID_X	3
// GRID_Y is +1 since there is one empty row for title icon
#define MAIN_MENU_GRID_Y	3
#define MAIN_MENU_GRID_SPACING	0
#define MAIN_MENU_OFFSET_X	6
#define MAIN_MENU_OFFSET_Y	8
#define oswald_draw_bitmap_i(a,b,c,d,e,f) if(f) oswald_draw_bitmap(a,b,c,d,1,e)
static struct struct_menu_items { 
	int screen_num;
	char *name;
	unsigned char *bits;
	int width;
	int height;
} menu_items[] = {
	{ALARM_SETUP_SCREEN,"Alarms",alarm_icon_bits,alarm_icon_width,alarm_icon_height},
	{STOP_WATCH_SCREEN,"Stopwatch",stopwatch_icon_bits,stopwatch_icon_width,stopwatch_icon_height},
	{BLUETOOTH_SCREEN,"Bluetooth",bluetooth_icon_bits,bluetooth_icon_width,bluetooth_icon_height},
	{ACCEL_DISPLAY_SCREEN,"Accel.",acc_icon_bits,acc_icon_width,acc_icon_height},
	{MESSAGES_SCREEN,"Messages",message_icon_bits,message_icon_width,message_icon_height},
	{INFO_SCREEN,"About",info_icon_bits,info_icon_width,info_icon_height},
	{LAST_SCREEN,"Close menu",exit_icon_bits,exit_icon_width,exit_icon_height}
};
void draw_main_menu_screen(main_menu_data_t *sdata)
{
	int x,y;
	int i,l;
	int w;
	char *t;
	hal_lcd_clear_display();
	i=sdata->pos-2;
	i=(i<0)?0:i;
	if(i>(sizeof(menu_items)/sizeof(menu_items[0]))-3) {
		i=(sizeof(menu_items)/sizeof(menu_items[0]))-3;
	}
	for(l=i+3,y=5;i<(sizeof(menu_items)/sizeof(menu_items[0])) && i<l;i++,y+=32) {
		x=15;
		if(i==sdata->pos)
			oswald_draw_rect(0,y-5,95,y+32-5,1);
		oswald_draw_bitmap(x,y,menu_items[i].width,menu_items[i].height,(i==sdata->pos)?0:1,menu_items[i].bits);
		oswald_writestr_font58_scaled(x+30, y+6,menu_items[i].name,&w,(i==sdata->pos)?0:1,(i==sdata->pos)?1:0,1,2);
	}
	

	hal_lcd_update_display();
}

event_ret_t handle_main_menu_buttons(watch_button button, main_menu_data_t *sdata)
{
	switch (button) {
		case BUTTON_C:
			sdata->pos++;
			if (sdata->pos >= (sizeof(menu_items)/sizeof(menu_items[0])))
				sdata->pos = 0;
			draw_main_menu_screen(&main_menu_screen);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_A:
			sdata->pos--;
			if (sdata->pos < 0)
				sdata->pos = (sizeof(menu_items)/sizeof(menu_items[0]))-1;
			draw_main_menu_screen(&main_menu_screen);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_B:
			if (menu_items[sdata->pos].screen_num!=LAST_SCREEN) {
				OswaldState.screen_id = menu_items[sdata->pos].screen_num;
				OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
				OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			} else {
				OswaldState.screen_id = IDLE_SCREEN;
				OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
				OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			}
			return EVENT_RET_HANDLED;
			break;
		default:
			break;
	}

	return EVENT_RET_UNHANDLED;
}

/* after MAIN_MENU_TIMEOUT seconds return to IDLE_SCREEN */
#define MAIN_MENU_TIMEOUT	10

event_ret_t main_menu_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			main_menu_screen.tmo = 0;
			main_menu_screen.pos = 0;
			draw_main_menu_screen(&main_menu_screen);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			main_menu_screen.tmo = 0;
			return handle_main_menu_buttons(*(watch_button *)data, &main_menu_screen);
			break;
		case EVENT_ONE_SEC_TIMER:
			main_menu_screen.tmo++;
			if (main_menu_screen.tmo > MAIN_MENU_TIMEOUT) {
				OswaldState.screen_id = IDLE_SCREEN;
				OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
				OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			}
			return EVENT_RET_HANDLED;
		default:
			break;
	};
	return EVENT_RET_UNHANDLED;
}


/*
 * Accelerometer and sensor display screen
 */
typedef struct {
	accel_data_t accdata;
} accelscreen_data_t;
static accelscreen_data_t accel_screen = {
	{ 0, 0, 0},
};

void draw_accel_screen(accel_data_t *accel_data)
{
	uint8_t x,y;

	hal_lcd_clear_display();

	oswald_draw_bitmap(36, 0, acc_icon_width, acc_icon_height, 1, acc_icon_bits);

	oswald_write_string(1, 40, FONT_6x9, FALSE, "X:");
	oswald_write_number(15, 40, FONT_6x9, FALSE, accel_data->x);
	oswald_write_string(1, 52, FONT_6x9, FALSE, "Y:");
	oswald_write_number(15, 52, FONT_6x9, FALSE, accel_data->y);
	oswald_write_string(1, 64, FONT_6x9, FALSE, "Z:");
	oswald_write_number(15, 64, FONT_6x9, FALSE, accel_data->z);

	oswald_write_string(1, 85, FONT_6x9, FALSE, "Light:");
	oswald_write_number(50, 85, FONT_6x9, FALSE, 0);

	oswald_draw_line(40, 30, 92, 30, 1);
	oswald_draw_line(92, 30, 92, 82, 1);
	oswald_draw_line(40, 82, 92, 82, 1);
	oswald_draw_line(40, 82, 40, 30, 1);

	x = 41+25+((accel_data->x * 50) / (254));
	y = 31+25-((accel_data->y * 50) / (254));
	oswald_draw_pixel(x, y, TRUE);
	oswald_draw_pixel(x+1, y, TRUE);
	oswald_draw_pixel(x-1, y, TRUE);
	oswald_draw_pixel(x, y+1, TRUE);
	oswald_draw_pixel(x, y-1, TRUE);

	hal_lcd_update_display();
}

event_ret_t accel_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			draw_accel_screen(&accel_screen.accdata);
			hal_accelerometer_enable();
			return EVENT_RET_HANDLED;
			break;
		case EVENT_SCREEN_DESTROY:
			hal_accelerometer_disable();
			return EVENT_RET_HANDLED;
			break;
		case EVENT_ACCEL_UPDATE: {
			accel_data_t *accel_data = (accel_data_t *)data;
			accel_screen.accdata.x = accel_data->x;
			accel_screen.accdata.y = accel_data->y;
			accel_screen.accdata.z = accel_data->z;
			draw_accel_screen(&accel_screen.accdata);
			};
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_UNHANDLED;
}


/*
 * Date / time setup screen
 */
typedef struct {
	uint8_t pos;
	boolean set_mode;
	boolean on;
} datetime_setup_data_t;
static datetime_setup_data_t dt_setup_screen = {
	0,
	FALSE,
	TRUE
};

void draw_datetime_setup_screen(datetime_setup_data_t *sdata)
{
	int w,sx=2,sy=3;
	hal_lcd_clear_display();

	oswald_draw_grid(1,1,94,94,100,100);
	oswald_draw_bitmap(36, 4, timesetup_icon_width, timesetup_icon_height, 1, timesetup_icon_bits);

	if ((sdata->pos == 0 && sdata->on) || sdata->pos != 0) {
		if(OswaldClk.hour>=10)
			oswald_draw_font58_scaled(12, 34, (OswaldClk.hour / 10)+'0',&w,1,0,sx,sy);
		oswald_draw_font58_scaled(25, 34, (OswaldClk.hour % 10)+'0',&w,1,0,sx,sy);
	}
	oswald_draw_font58_scaled(37, 38, ':',&w,1,0,1,2);

	if ((sdata->pos == 1 && sdata->on) || sdata->pos != 1) {
		oswald_draw_font58_scaled(44, 34, (OswaldClk.minute / 10)+'0',&w,1,0,sx,sy);
		oswald_draw_font58_scaled(57, 34, (OswaldClk.minute % 10)+'0',&w,1,0,sx,sy);
	}

	if ((sdata->pos == 2 && sdata->on) || sdata->pos != 2) {
		oswald_draw_font58_scaled(71, 40, (OswaldClk.second / 10)+'0',&w,1,0,sx-1,sy-1);
		oswald_draw_font58_scaled(79, 40, (OswaldClk.second % 10)+'0',&w,1,0,sx-1,sy-1);
	}


	if ((sdata->pos == 3 && sdata->on) || sdata->pos != 3) {
		oswald_write_number(18, 57, FONT_5x8, FALSE, OswaldClk.day);
	}
	oswald_write_character(31, 57, FONT_5x8, FALSE, '.');
	if ((sdata->pos == 4 && sdata->on) || sdata->pos != 4) {
		oswald_write_number(37, 57, FONT_5x8, FALSE, OswaldClk.month);
	}
	oswald_write_character(51, 57, FONT_5x8, FALSE, '.');
	if ((sdata->pos == 5 && sdata->on) || sdata->pos != 5) {
		oswald_write_number(59, 57, FONT_5x8, FALSE, OswaldClk.year);
	}

	if ((sdata->pos == 6 && sdata->on) || sdata->pos != 6) {
		if (OswaldClk.clk24hr) {
			oswald_draw_bitmap(12, 73, checked_icon_width, checked_icon_height, 1, checked_icon_bits);
		} else {
			oswald_draw_bitmap(12, 73, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits);
		}
	}
	oswald_write_string(25, 73, FONT_5x8, FALSE, "24hr");

	if ((sdata->pos == 7 && sdata->on) || sdata->pos != 7) {
		if (OswaldClk.day_first) {
			oswald_draw_bitmap(12, 83, checked_icon_width, checked_icon_height, 1, checked_icon_bits);
		} else {
			oswald_draw_bitmap(12, 83, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits);
		}
	}
	oswald_write_string(25, 83, FONT_5x8, FALSE, "dd.mm.  mm/dd");

	hal_lcd_update_display();
}

void datetime_handle_updown(uint8_t pos, int8_t incr)
{
	switch (pos) {
		case 0: // hour
			if (OswaldClk.hour == 0 && incr == -1) {
				OswaldClk.hour = 23;
				break;
			};
			OswaldClk.hour += incr;
			if (OswaldClk.hour > 23)
				OswaldClk.hour = 0;
			break;
		case 1: // minute
			if (OswaldClk.minute == 0 && incr == -1) {
				OswaldClk.minute = 59;
				break;
			};
			OswaldClk.minute += incr;
			if (OswaldClk.minute > 59)
				OswaldClk.minute = 0;
			break;
		case 2: // second
			OswaldClk.second = 0;
			break;
		case 3: // day
			if (OswaldClk.day == 1 && incr == -1) {
				OswaldClk.day = 31;
				break;
			};
			OswaldClk.day += incr;
			if (OswaldClk.day > 31)
				OswaldClk.day = 1;
			break;
		case 4: // month
			if (OswaldClk.month == 1 && incr == -1) {
				OswaldClk.month = 12;
				break;
			};
			OswaldClk.month += incr;
			if (OswaldClk.month > 12)
				OswaldClk.month = 1;
			break;
		case 5: // year
			OswaldClk.year += incr;
			break;
		case 6: // 24hr / 12hr
			if (OswaldClk.clk24hr)
				OswaldClk.clk24hr = FALSE;
			else
				OswaldClk.clk24hr = TRUE;
			break;
		case 7: // dd.mm. / mm/dd
			if (OswaldClk.day_first)
				OswaldClk.day_first = FALSE;
			else
				OswaldClk.day_first = TRUE;
			break;
		default:
			break;
	};
	if (pos == 2)
		hal_set_rtc(&OswaldClk, TRUE);
	else
		hal_set_rtc(&OswaldClk, FALSE);
}

event_ret_t handle_setup_datetime_buttons(watch_button button, datetime_setup_data_t *sdata)
{
	switch (button) {
		case BUTTON_A:
			datetime_handle_updown(sdata->pos, 1);
			break;
		case BUTTON_C:
			datetime_handle_updown(sdata->pos, -1);
			break;
		case BUTTON_B:
			sdata->pos++;
			sdata->pos %= 8;
			break;
		case BUTTON_E:
			sdata->pos=(sdata->pos+8-1)%8;
			break;
		case BUTTON_F:
			OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL);
			OswaldState.screen_id = IDLE_SCREEN;
			OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
			OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			return EVENT_RET_HANDLED;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	}
	draw_datetime_setup_screen(sdata);
	return EVENT_RET_HANDLED;
}

event_ret_t datetime_setup_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			dt_setup_screen.pos = 0;
			draw_datetime_setup_screen(&dt_setup_screen);
			hal_enable_halfsecond_timer();
			return EVENT_RET_HANDLED;
			break;
		case EVENT_SCREEN_DESTROY:
			hal_disable_halfsecond_timer();
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return handle_setup_datetime_buttons(*(watch_button *)data, &dt_setup_screen);
			break;
		case EVENT_HALF_SEC_TIMER:
			if (dt_setup_screen.on)
				dt_setup_screen.on = FALSE;
			else
				dt_setup_screen.on = TRUE;
			draw_datetime_setup_screen(&dt_setup_screen);
			return EVENT_RET_HANDLED;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_UNHANDLED;
}


/*
 * Alarm setup screen
 */
typedef struct {
	int num;
	int pos;
	boolean set_mode;
	boolean on;
} alarm_setup_data_t;
static alarm_setup_data_t alarm_setup_screen = {
	0,
	0,
	FALSE,
	TRUE
};

void draw_alarm_setup_screen(alarm_setup_data_t *sdata)
{
	static char initials[]={"SMTWTFS"};
	int w,i;
	hal_lcd_clear_display();
	if (sdata->set_mode) 
		oswald_draw_grid(1,1,94,94,100,100);

	oswald_draw_bitmap(36, 3, alarm_icon_width, alarm_icon_height, 1, alarm_icon_bits);
	oswald_draw_rect(43,8,52,20,0);
	oswald_draw_font58_scaled(44,9,sdata->num+'1',&w,1,0,2,2);

	if ((sdata->pos == 0 && sdata->on) || sdata->pos != 0) {
		oswald_draw_font58_scaled(18, 34, (OswaldAlarm[sdata->num].hour / 10)+'0',&w,1,0,2,3);
		oswald_draw_font58_scaled(31, 34, (OswaldAlarm[sdata->num].hour % 10)+'0',&w,1,0,2,3);
	}
	oswald_draw_font58_scaled(43, 38, ':',&w,1,0,1,2);

	if ((sdata->pos == 1 && sdata->on) || sdata->pos != 1) {
		oswald_draw_font58_scaled(50, 34, (OswaldAlarm[sdata->num].minute / 10)+'0',&w,1,0,2,3);
		oswald_draw_font58_scaled(63, 34, (OswaldAlarm[sdata->num].minute % 10)+'0',&w,1,0,2,3);
	}

	for(i=0;i<7;i++) {
		oswald_write_character(8+i*12, 65, FONT_6x9, FALSE, initials[i]);
		if ((sdata->pos == (i+2) && sdata->on) || sdata->pos != (i+2)) {
			if ((OswaldAlarm[sdata->num].wday & (1<<i)))
				oswald_draw_bitmap(7+i*12, 76, checked_icon_width, checked_icon_height, 1, checked_icon_bits);
			else
				oswald_draw_bitmap(7+i*12, 76, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits);
		}
	}

	hal_lcd_update_display();
}

void alarm_handle_updown(int num, int pos, int incr)
{
	switch (pos) {
		case 0: // hour
			if (OswaldAlarm[num].hour == 0 && incr == -1) {
				OswaldAlarm[num].hour = 23;
				break;
			};
			OswaldAlarm[num].hour += incr;
			if (OswaldAlarm[num].hour > 23)
				OswaldAlarm[num].hour = 0;
			break;
		case 1: // minute
			if (OswaldAlarm[num].minute == 0 && incr == -1) {
				OswaldAlarm[num].minute = 59;
				break;
			};
			OswaldAlarm[num].minute += incr;
			if (OswaldAlarm[num].minute > 59)
				OswaldAlarm[num].minute = 0;
			break;
		case 2: // sunday
			OswaldAlarm[num].wday ^= WDAY_SUNDAY;
			break;
		case 3: // monday
			OswaldAlarm[num].wday ^= WDAY_MONDAY;
			break;
		case 4: // tuesday
			OswaldAlarm[num].wday ^= WDAY_TUESDAY;
			break;
		case 5: // wednesday
			OswaldAlarm[num].wday ^= WDAY_WEDNESDAY;
			break;
		case 6: // thursday
			OswaldAlarm[num].wday ^= WDAY_THURSDAY;
			break;
		case 7: // friday
			OswaldAlarm[num].wday ^= WDAY_FRIDAY;
			break;
		case 8: // saturday
			OswaldAlarm[num].wday ^= WDAY_SATURDAY;
			break;
		default:
			break;
	};
}

event_ret_t handle_setup_alarm_buttons(watch_button button, alarm_setup_data_t *sdata)
{
	if (alarm_setup_screen.set_mode) {
		switch (button) {
			case BUTTON_A:
				alarm_handle_updown(sdata->num, sdata->pos, 1);
				break;
			case BUTTON_C:
				alarm_handle_updown(sdata->num, sdata->pos, -1);
				break;
			case BUTTON_E:
				sdata->pos+=9-1;
				sdata->pos %= 9;
				break;
			case BUTTON_B:
				sdata->pos++;
				sdata->pos %= 9;
				break;
			case BUTTON_F:
				alarm_setup_screen.set_mode = FALSE;
				alarm_setup_screen.pos = 0;
				break;
			default:
				return EVENT_RET_UNHANDLED;
				break;
		}
	} else {
		switch (button) {
			case BUTTON_A:
				sdata->num+=MAX_ALARM+((button==BUTTON_A)?-1:1);
				sdata->num%=MAX_ALARM;
				break;
			case BUTTON_C:
				sdata->num+=MAX_ALARM+((button==BUTTON_A)?-1:1);
				sdata->num%=MAX_ALARM;
				break;
			case BUTTON_F:
				alarm_setup_screen.set_mode = TRUE;
				break;
			default:
				return EVENT_RET_UNHANDLED;
				break;
		}
	}
	draw_alarm_setup_screen(sdata);

	return EVENT_RET_HANDLED;
}

event_ret_t alarm_setup_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			alarm_setup_screen.num = 0;
			alarm_setup_screen.pos = 0;
			alarm_setup_screen.on = TRUE;
			alarm_setup_screen.set_mode = FALSE;
			draw_alarm_setup_screen(&alarm_setup_screen);
			hal_enable_halfsecond_timer();
			return EVENT_RET_HANDLED;
			break;
		case EVENT_SCREEN_DESTROY:
			hal_disable_halfsecond_timer();
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return handle_setup_alarm_buttons(*(watch_button *)data, &alarm_setup_screen);
			break;
		case EVENT_HALF_SEC_TIMER:
			if (alarm_setup_screen.set_mode) {
				if (alarm_setup_screen.on)
					alarm_setup_screen.on = FALSE;
				else
					alarm_setup_screen.on = TRUE;
			} else
				alarm_setup_screen.on = TRUE;
			draw_alarm_setup_screen(&alarm_setup_screen);
			return EVENT_RET_HANDLED;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_UNHANDLED;
}


/*
 * Test menu
 */
typedef struct {
	uint8_t menu_pos;
} test_menu_t;
static test_menu_t test_menu = { 0 };

void draw_menu_test_screen(void)
{
	hal_lcd_clear_display();
#if 0
	SetFont(MetaWatch16);
	WriteLcdString(2, 2, "Menu");
	SetFont(MetaWatch7);
	WriteLcdString(2, 20, "Item 1");
	WriteLcdString(2, 29, "Item 2");
	WriteLcdString(2, 38, "Item 3");
	WriteLcdString(2, 47, "Item 4");
	WriteLcdString(2, 56, "Item 5");

	WriteLcdString(50, 20+(9*test_menu.menu_pos), "*");
#endif
	oswald_write_string(2, 2, FONT_DROID8x12, FALSE, "Menu");

	oswald_write_string(2, 20, FONT_DROID8x12, FALSE, "Item 1");
	oswald_write_string(2, 29, FONT_DROID8x12, FALSE, "Item 2");
	oswald_write_string(2, 38, FONT_DROID8x12, FALSE, "Item 3");
	oswald_write_string(2, 47, FONT_DROID8x12, FALSE, "Item 4");
	oswald_write_string(2, 56, FONT_DROID8x12, FALSE, "Item 5");

	oswald_write_character(50, 18+(9*test_menu.menu_pos), FONT_6x9, FALSE, 0x11);

	hal_lcd_update_display();
}

event_ret_t handle_menu_user_buttons(watch_button button)
{
	switch (button) {
		case BUTTON_A:
			test_menu.menu_pos--;
			test_menu.menu_pos %= 5;
			break;
		case BUTTON_B:
			test_menu.menu_pos++;
			test_menu.menu_pos %= 5;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	}
	draw_menu_test_screen();
	return EVENT_RET_HANDLED;
}

event_ret_t test_menu_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return handle_menu_user_buttons(*(watch_button *)data);
			break;
		case EVENT_SCREEN_VISIBLE:
			test_menu.menu_pos = 0;
			draw_menu_test_screen();
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_HANDLED;
}


/*
 * Stop Watch
 */
typedef enum estopw_item {
	stopw_startstop,
	stopw_split,
	stopw_reset,
	stopw_end
} stopw_item;
typedef struct {
	uint8_t hr;
	uint8_t min;
	uint8_t sec;
	uint8_t csec;
} timerecord;
#define MAX_LAPS 2
typedef struct {
	timerecord current;
	int laps;
	timerecord lapsdata[MAX_LAPS];
	timerecord avg;
	timerecord max;
	timerecord min;
	timerecord lastlap;
	boolean running;
	stopw_item item;
	boolean showstats;
} stopwatch_data_t;
static stopwatch_data_t stopwatch_screen = { {0, 0, 0, 0},0,{{0, 0, 0, 0}} , FALSE,0};

static void timerecord2str(timerecord *tr, char *timebuf /* sizeof(timebuf)>=9 */, char *csecbuf /*sizeof(csecbuf)>=3*/)
{
	num2str(tr->hr,timebuf,2,2);
	if(tr->hr<10)
		timebuf[0]=' ';
	timebuf[2]=':';
	num2str(tr->min,timebuf+3,2,2);
	timebuf[5]=':';
	num2str(tr->sec,timebuf+6,2,2);
	num2str(tr->csec,csecbuf,2,2);
}

static void timerecord_diff(timerecord *new, timerecord *old, timerecord *res)
{
	int d,od;
	mymemset(res,0,sizeof(timerecord));
	d=(old->csec>new->csec)?1:0;
	res->csec=(new->csec+d*100)-old->csec;
	od=d,d=(old->sec>(new->sec-od))?1:0;
	res->sec=(new->sec-od+d*60)-old->sec;
	od=d,d=(old->min>(new->min-od))?1:0;
	res->min=(new->min-od+d*60)-old->min;
	res->hr=(new->hr-d)-old->hr;
}

static void timerecord_div(timerecord *total, int div, timerecord *res)
{
	int n;
	mymemset(res,0,sizeof(timerecord));
	if(div<0)
		return;
	res->hr=total->hr/div;
	n=(((int)total->min)+(total->hr%div)*60);
	res->min=n/div;
	n=(((int)total->sec)+(n%div)*60);
	res->sec=n/div;
	n=(((int)total->csec)+(n%div)*100);
	res->csec=n/div;
}

static int timerecord_cmp(timerecord *a, timerecord *b)
{
	if(a->hr!=b->hr) 
		return(a->hr-b->hr);
	if(a->min!=b->min)
		return(a->min-b->min);
	if(a->sec!=b->sec)
		return(a->sec-b->sec);
	return(a->csec-b->csec);
}

typedef enum estopwmask {
	stopwm_icon=0x1,
	stopwm_laps=0x2,
	stopwm_split=0x4,
	stopwm_current=0x8,
	stopwm_menu=0x10,
	stopwm_all=0x1f
} stopwmask;

static void draw_stop_watch_screen(stopwatch_data_t *sdata, int mask)
{
	int w;
	char *hint;
	int x,y;
	char lapbuf[4];
	char timebuf[9];
	char csecbuf[3];
	int i;

	if(sdata->showstats) {
		hal_lcd_clear_display();
		oswald_draw_grid(1,1,94,94,100,100);
		oswald_draw_bitmap(36, 3, stopwatch_icon_width, stopwatch_icon_height, 1, stopwatch_icon_bits);
		/* laps */
		num2str(sdata->laps,lapbuf,3,3);
		oswald_writestr_font58_scaled(3,3,lapbuf,&w,1,0,2,2);
		/* stats */
		for(i=0;i<3;i++) {
			y=20+i*24;
			hint=((i==0)?"Average":(i==1)?"Min":"Max");
			oswald_writestr_font58(4,y+3,hint,&w,1,0);
			timerecord2str(i==0?&(sdata->avg):i==1?&(sdata->min):&(sdata->max),timebuf,csecbuf);
		        x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0);
		        oswald_writestr_font58_scaled(x,y+12,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,2);
		        oswald_writestr_font58_scaled(x+w+1,y+12+6,csecbuf,&w,1,0,2,1);
		}
		hal_lcd_update_display();
		return;
	}
	if(mask==stopwm_all)
		hal_lcd_clear_display();

	if(mask&stopwm_icon) {
		if(mask!=stopwm_all)
			oswald_draw_rect(36,3,36-1+stopwatch_icon_width,3-1+stopwatch_icon_height,0);
		oswald_draw_bitmap(36, 3, stopwatch_icon_width, stopwatch_icon_height, 1, stopwatch_icon_bits);
	}
	/* Current menu option */
	if(mask&stopwm_menu) {
		oswald_draw_rect(0,95-12,95,95,1);
		hint=(sdata->item==stopw_startstop)?"Start-Stop":
			(sdata->item==stopw_split)?"Start-Split":
			(sdata->item==stopw_reset)?"Reset":
			"Unknown";
		w=oswald_getwidth_font58_scaled(hint,2,1);
		oswald_writestr_font58_scaled((96-w)/2,95-8,hint,&w,0,1,2,1);
	}
	/* Lap number */
	if(mask&stopwm_laps) {
		if(mask!=stopwm_all)
			oswald_draw_rect(3,3,3+9*3,3+12,0);
		num2str(sdata->laps,lapbuf,3,3);
		oswald_writestr_font58_scaled(3,3,lapbuf,&w,1,0,2,2);
	}

	/* Split diff /Split diff */
	if(mask&stopwm_split) {
		if(mask!=stopwm_all)
			oswald_draw_rect(0,28,95,60,0);
		/* diff */
		timerecord2str(&(sdata->lastlap),timebuf,csecbuf);
	        x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0);
	        oswald_writestr_font58_scaled(x,28,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,2);
	        oswald_writestr_font58_scaled(x+w+1,34,csecbuf,&w,1,0,2,1);

		/* Split */
		if(sdata->laps>0)
			timerecord2str(sdata->lapsdata+((sdata->laps<MAX_LAPS)?sdata->laps-1:MAX_LAPS-1),timebuf,csecbuf);	
		else {
			mymemcpy(timebuf," 0:00:00",9);
			mymemcpy(csecbuf,"00",3);
		}
	        x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0);
	        oswald_writestr_font58_scaled(x,42,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,2);
	        oswald_writestr_font58_scaled(x+w+1,48,csecbuf,&w,1,0,2,1);
	}

	/* main display */
	if(mask&stopwm_current) {
		if(mask!=stopwm_all)
			oswald_draw_rect(0,61,95,95-13,0);
		timerecord2str(&(sdata->current),timebuf,csecbuf);
		x=8+((timebuf[0]==' ')?oswald_getwidth_font58_scaled("0",2,3):0);
		oswald_writestr_font58_scaled(x,61,timebuf+((timebuf[0]==' ')?1:0),&w,1,0,2,3);
		x+=w;
		if(sdata->running)
			csecbuf[1]='\0';
		oswald_writestr_font58_scaled(x+1,67,csecbuf,&w,1,0,2,2);
	}
		
	hal_lcd_update_display();
}

event_ret_t handle_stop_watch_buttons(watch_button button)
{
	if(stopwatch_screen.showstats) {
		if(button==BUTTON_F)
			stopwatch_screen.showstats=0;
		else if(button==BUTTON_D)
			return(EVENT_RET_UNHANDLED);
		draw_stop_watch_screen(&stopwatch_screen,stopwm_all);
		return(EVENT_RET_HANDLED);
	}
	switch (button) {
		case BUTTON_B: // start/stop
			if(stopwatch_screen.item==stopw_startstop) {
				if (stopwatch_screen.running) {
					hal_disable_centisecond_timer();
					stopwatch_screen.running = FALSE;
				} else {
					hal_enable_centisecond_timer();
					stopwatch_screen.running = TRUE;
				}
				draw_stop_watch_screen(&stopwatch_screen,stopwm_current);
			} else if(stopwatch_screen.item==stopw_split) {
				if (stopwatch_screen.running) {
					int l;
					if(stopwatch_screen.laps>=MAX_LAPS) {
						mymemmove(stopwatch_screen.lapsdata,stopwatch_screen.lapsdata+1,(MAX_LAPS-1)*sizeof(timerecord));
						l=MAX_LAPS-1;
					} else {
						l=stopwatch_screen.laps;
					}
					stopwatch_screen.laps++;
					mymemcpy(stopwatch_screen.lapsdata+l,&(stopwatch_screen.current),sizeof(timerecord));
					/* update statistics */
					if(l>0)
						timerecord_diff(stopwatch_screen.lapsdata+l,stopwatch_screen.lapsdata+l-1,&(stopwatch_screen.lastlap));
					else
						mymemcpy(&(stopwatch_screen.lastlap),stopwatch_screen.lapsdata+l,sizeof(timerecord));
					timerecord_div(stopwatch_screen.lapsdata+l,stopwatch_screen.laps,&(stopwatch_screen.avg));
					if(stopwatch_screen.laps==1 || timerecord_cmp(&(stopwatch_screen.lastlap),&(stopwatch_screen.min))<0)
						mymemcpy(&(stopwatch_screen.min),&(stopwatch_screen.lastlap),sizeof(timerecord));
					if(stopwatch_screen.laps==1 || timerecord_cmp(&(stopwatch_screen.lastlap),&(stopwatch_screen.max))>0)
						mymemcpy(&(stopwatch_screen.max),&(stopwatch_screen.lastlap),sizeof(timerecord));
					draw_stop_watch_screen(&stopwatch_screen,stopwm_split|stopwm_laps);
				} else {
					hal_enable_centisecond_timer();
					stopwatch_screen.running = TRUE;
				}
			} else if(stopwatch_screen.item==stopw_reset) {
				if(stopwatch_screen.running)
					hal_disable_centisecond_timer();
				mymemset(&(stopwatch_screen),0,sizeof(stopwatch_screen));
				draw_stop_watch_screen(&stopwatch_screen,stopwm_all);
			} else {
				return EVENT_RET_UNHANDLED;
			}
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_A: 
			stopwatch_screen.item+=stopw_end-1;
			stopwatch_screen.item%=stopw_end;
			draw_stop_watch_screen(&stopwatch_screen,stopwm_menu);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_C: 
			stopwatch_screen.item++;
			stopwatch_screen.item%=stopw_end;
			draw_stop_watch_screen(&stopwatch_screen,stopwm_menu);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_F:
			stopwatch_screen.showstats=1-stopwatch_screen.showstats;
			draw_stop_watch_screen(&stopwatch_screen,stopwm_all);
			return EVENT_RET_HANDLED;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	}
	return EVENT_RET_UNHANDLED;
}

event_ret_t stop_watch_handle_events(uint16_t event, void *data)
{
	event_ret_t ret;
	switch (event) {
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			ret=handle_stop_watch_buttons(*(watch_button *)data);
			return(ret);
			break;
		case EVENT_SCREEN_VISIBLE:
			draw_stop_watch_screen(&stopwatch_screen,stopwm_all);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_SCREEN_DESTROY:
			hal_disable_centisecond_timer();
			stopwatch_screen.running = FALSE;
			return EVENT_RET_HANDLED;
			break;
		case EVENT_CS_TIMER:
			stopwatch_screen.current.csec++;
			if (stopwatch_screen.current.csec > 99) {
				stopwatch_screen.current.csec = 0;
				stopwatch_screen.current.sec++;
			};
			if (stopwatch_screen.current.sec > 59) {
				stopwatch_screen.current.sec = 0;
				stopwatch_screen.current.min++;
			};
			if (stopwatch_screen.current.min > 59) {
				stopwatch_screen.current.min = 0;
				stopwatch_screen.current.hr++;
			};
			if (stopwatch_screen.current.hr > 59) {
				stopwatch_screen.current.hr = 0;
			};
			if ((stopwatch_screen.current.csec % 10)==0 || stopwatch_screen.running!=TRUE)
				draw_stop_watch_screen(&stopwatch_screen,stopwm_current);
			return EVENT_RET_HANDLED;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_HANDLED;
}


/*
 * Alarm screen, shown when alarm is fired
 */
void draw_alarm_screen(void)
{
	static char txt[]={"Alarm"};
	int w;
	hal_lcd_clear_display();
	oswald_draw_bitmap(36, 20, alarm_icon_width, alarm_icon_height, 1, alarm_icon_bits);
	w=oswald_getwidth_font58_scaled(txt,1,2);
	oswald_writestr_font58_scaled((96-1-w)/2+1,49,txt,&w,1,0,1,2);
	hal_lcd_update_display();
}

event_ret_t alarm_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			draw_alarm_screen();
			hal_enable_halfsecond_timer();
			hal_vibration_set_state(TRUE);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_SCREEN_DESTROY:
			hal_disable_halfsecond_timer();
			hal_lcd_set_backlight(FALSE);
			hal_vibration_set_state(FALSE);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return EVENT_RET_UNHANDLED;
			break;
		case EVENT_HALF_SEC_TIMER:
			hal_lcd_set_backlight(!hal_lcd_get_backlight());
			hal_vibration_set_state(!hal_vibration_get_state());
			dbg_out("timer\n");
			return EVENT_RET_HANDLED;
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_HANDLED;
}


/*
 * Bluetooth setup screen
 */
typedef struct {
	uint8_t pos;
	boolean bt_en;
	boolean set_mode;
	boolean on;
} bluetooth_data_t;
static bluetooth_data_t bluetooth_screen = {
	0,
	FALSE,
	FALSE,
	TRUE
};

void draw_bluetooth_screen(bluetooth_data_t *sdata)
{
	char bstr[20];
	uint8_t *bd_addr;

	hal_lcd_clear_display();

	oswald_draw_bitmap(36, 3, bluetooth_icon_width, bluetooth_icon_height, 1, bluetooth_icon_bits);

	if (sdata->set_mode) 
		oswald_draw_grid(1,1,94,94,100,100);

	oswald_write_string(25, 39, FONT_DROID8x12, FALSE, "State");
	switch (hal_bluetooth_get_state()) {
		case BLUETOOTH_OFF:
			oswald_write_string(53, 39, FONT_DROID8x12, FALSE, "off");
			break;
		case BLUETOOTH_ON:
			oswald_write_string(53, 39, FONT_DROID8x12, FALSE, "on");
			break;
		case BLUETOOTH_CONNECTED:
			oswald_write_string(53, 39, FONT_DROID8x12, FALSE, "conn.");
			break;
		default:
			break;
	};
	oswald_write_string(16, 54, FONT_DROID8x12, FALSE, "Enabled");
	if ((sdata->pos == 0 && sdata->on) || sdata->pos != 0) {
		if (bluetooth_screen.bt_en)
			oswald_draw_bitmap(53, 53, checked_icon_width, checked_icon_height, 1, checked_icon_bits);
		else
			oswald_draw_bitmap(53, 53, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits);
	}
	oswald_write_string(18, 69, FONT_DROID8x12, FALSE, "Visible");
	if ((sdata->pos == 1 && sdata->on) || sdata->pos != 1) {
		// oswald_write_character(53, 50, FONT_DROID8x12, hal_bluetooth_get_visible() ? 'x' : '_');
		if (hal_bluetooth_get_visible())
			oswald_draw_bitmap(53, 68, checked_icon_width, checked_icon_height, 1, checked_icon_bits);
		else
			oswald_draw_bitmap(53, 68, unchecked_icon_width, unchecked_icon_height, 1, unchecked_icon_bits);
	}

	if (hal_bluetooth_get_state() >= BLUETOOTH_ON) {
		int i,off,c;
		bd_addr = hal_bluetooth_get_local_bdaddr();
		for(i=5,off=0;i>=0;i--) {
			if(i!=5)
				bstr[off++]=':';
			c=(bd_addr[i]>>4)&0xf;
			bstr[off++]=((c<10)?(c+'0'):(c+'a'-10));
			c=bd_addr[i]&0xf;
			bstr[off++]=((c<10)?(c+'0'):(c+'a'-10));
		}
		bstr[off++]='\0';
		oswald_write_string(11, 85, FONT_5x7, FALSE, bstr);
	}

	hal_lcd_update_display();
}

void bluetooth_handle_updown(uint8_t pos, int8_t incr)
{
	switch (pos) {
		case 0:
			if (hal_bluetooth_get_state() >= BLUETOOTH_ON) {
				hal_bluetooth_set_state(BLUETOOTH_OFF);
				bluetooth_screen.bt_en = FALSE;
			} else {
				hal_bluetooth_set_state(BLUETOOTH_ON);
				bluetooth_screen.bt_en = TRUE;
			}
			break;
		case 1:
			if (hal_bluetooth_get_state() >= BLUETOOTH_ON && !hal_bluetooth_get_visible()) {
				hal_bluetooth_set_visible(TRUE);
			} else {
				hal_bluetooth_set_visible(FALSE);
			}
			break;
		default:
			break;
	};
}

event_ret_t handle_bluetooth_buttons(watch_button button, bluetooth_data_t *sdata)
{
	if (bluetooth_screen.set_mode) {
		switch (button) {
			case BUTTON_B:
				bluetooth_handle_updown(sdata->pos, 1);
				break;
			case BUTTON_E:
				bluetooth_handle_updown(sdata->pos, -1);
				break;
			case BUTTON_A:
				sdata->pos=1-sdata->pos;
				break;
			case BUTTON_C:
				sdata->pos=1-sdata->pos;
				break;
			case BUTTON_F:
				bluetooth_screen.set_mode = FALSE;
				break;
			default:
				return EVENT_RET_UNHANDLED;
				break;
		}
	} else {
		switch (button) {
			case BUTTON_F:
				bluetooth_screen.set_mode = TRUE;
				break;
			default:
				return EVENT_RET_UNHANDLED;
				break;
		}
	}

	draw_bluetooth_screen(sdata);

	return EVENT_RET_HANDLED;
}

event_ret_t bluetooth_screen_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			bluetooth_screen.pos = 0;
			bluetooth_screen.bt_en = (hal_bluetooth_get_state() > 0);
			draw_bluetooth_screen(&bluetooth_screen);
			hal_enable_halfsecond_timer();
			break;
		case EVENT_SCREEN_DESTROY:
			hal_disable_halfsecond_timer();
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return handle_bluetooth_buttons(*(watch_button *)data, &bluetooth_screen);
			break;
		case EVENT_HALF_SEC_TIMER:
			if (bluetooth_screen.set_mode) {
				if (bluetooth_screen.on)
					bluetooth_screen.on = FALSE;
				else
					bluetooth_screen.on = TRUE;
			} else
				bluetooth_screen.on = TRUE;
			draw_bluetooth_screen(&bluetooth_screen);
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_HANDLED;
}


/*
 * Info Screen
 */
void draw_info_screen(void)
{
	static char OsName[]={"Oswald"};
	char *ptr;
	int w,x;
	hal_lcd_clear_display();

	oswald_draw_bitmap(36, 0, info_icon_width, info_icon_height, 1, info_icon_bits);

	w=oswald_getwidth_font58_scaled(OsName,1,2);
	oswald_writestr_font58_scaled((96-w-1)/2,29,OsName,&w,1,0,1,2);
	w=oswald_getwidth_font58(OSWALD_VERSION);
	oswald_writestr_font58((96-w-1)/2, 44, OSWALD_VERSION, &w, 1, 0);
	oswald_write_string(2, 61, FONT_DROID8x12, FALSE, "HAL");
	oswald_write_string(35, 61, FONT_DROID8x12, FALSE, (char *)hal_get_version_string());
	oswald_write_string(2, 73, FONT_DROID8x12, FALSE, "Build");
	oswald_write_string(35, 73, FONT_DROID8x12, FALSE, (char *)hal_get_buildno_string());
	oswald_write_string(2, 85, FONT_DROID8x12, FALSE, "Radio");
	oswald_write_string(35, 85, FONT_DROID8x12, FALSE, (char *)hal_get_radio_version_string());

	hal_lcd_update_display();
}

event_ret_t info_screen_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			draw_info_screen();
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			break;
		default:
			return EVENT_RET_UNHANDLED;
			break;
	};
	return EVENT_RET_UNHANDLED;
}


/*
 * Messages Screens
 */
typedef struct {
	int8_t pos;	// marker position
	int8_t offset;	// offset in msg list
} messages_data_t;
static messages_data_t messages_screen = {
	0,
	0,
};

typedef struct {
	uint8_t day;
	uint8_t month;
	uint8_t year;	// without century, add +1900
	uint8_t hour;
	uint8_t minute;
} msg_timedate_t;

#define MSG_TYPE_READ		0
#define MSG_TYPE_NEW		1
#define MSG_TYPE_END		127

typedef struct {
	uint8_t type;
	msg_timedate_t td;
	char *msg;
} message_t;

uint8_t Msgs = 15;
message_t Msg[] = {
	{ MSG_TYPE_READ, {9,5,113,0,38}, "Testmessage with more text than fits into the menu." },
	{ MSG_TYPE_NEW, {9,5,113,0,39}, "Sitting in the train waiting to arrive." },
	{ MSG_TYPE_READ, {9,5,113,0,40}, "People in the train are annoying!" },
	{ MSG_TYPE_READ, {9,5,113,0,40}, "Auch auf Deutsch geht das hier und Text können lang sein." },
	{ MSG_TYPE_NEW, {8,5,113,0,40}, "Und hier noch eine neue Nachricht, die nun wirklich lang ist, laenger als die anderen." },
	{ MSG_TYPE_READ, {9,5,113,0,38}, "Testmessage with more text than fits into the menu." },
	{ MSG_TYPE_NEW, {9,5,113,0,39}, "Sitting in the train waiting to arrive." },
	{ MSG_TYPE_READ, {9,5,113,0,40}, "People in the train are annoying!" },
	{ MSG_TYPE_READ, {9,5,113,0,40}, "Auch auf Deutsch geht das hier und Text können lang sein." },
	{ MSG_TYPE_NEW, {8,5,113,0,40}, "Und hier noch eine neue Nachricht, die nun wirklich lang ist, laenger als die anderen." },
	{ MSG_TYPE_READ, {9,5,113,0,38}, "Testmessage with more text than fits into the menu." },
	{ MSG_TYPE_NEW, {9,5,113,0,39}, "Sitting in the train waiting to arrive." },
	{ MSG_TYPE_READ, {9,5,113,0,40}, "People in the train are annoying!" },
	{ MSG_TYPE_READ, {9,5,113,0,40}, "Auch auf Deutsch geht das hier und Text können lang sein." },
	{ MSG_TYPE_NEW, {8,5,113,0,40}, "Und hier noch eine neue Nachricht, die nun wirklich lang ist, laenger als die anderen." },
	{ MSG_TYPE_END, {0,0,0,0,0}, "Exit" },
};

static void msginfo2str(message_t *msginfo,char *dstr, int dstrsize)
{
	int off=0;
	dstr[off++]=(msginfo->type == MSG_TYPE_NEW) ? '*':' ';
	dstr[off++]=' ';
	off+=num2str(msginfo->td.day,dstr+off,2,2);
	dstr[off++]='.';
	off+=num2str(msginfo->td.month,dstr+off,2,2);
	dstr[off++]='.';
	off+=num2str(msginfo->td.year+1900,dstr+off,4,4);
	dstr[off++]=' ';
	off+=num2str(msginfo->td.hour,dstr+off,2,2);
	dstr[off++]=':';
	off+=num2str(msginfo->td.minute,dstr+off,2,2);
	dstr[off++]='\0';
}

void draw_message_screen(messages_data_t *sdata)
{
	char dstr[32];
	int off;
	uint8_t strpos, msglen;
	uint8_t line;

	hal_lcd_clear_display();

#if 0
	oswald_draw_bitmap(81, 6, upbutton_icon_width, upbutton_icon_height, upbutton_icon_bits);
	oswald_draw_bitmap(81, 38, downbutton_icon_width, downbutton_icon_height, downbutton_icon_bits);
#endif
	oswald_draw_bitmap(81, 70, enterbutton_icon_width, enterbutton_icon_height, 1, enterbutton_icon_bits);

	Msg[sdata->offset + sdata->pos].type = MSG_TYPE_READ;
	off=0;
	dstr[off++]='#';
	off+=num2str(sdata->pos + sdata->offset + 1,dstr+off,1,sizeof(dstr)-2-off);
	dstr[off++]='/';
	off+=num2str(Msgs,dstr+off,1,sizeof(dstr)-1-off);
	dstr[off++]='\0';
	oswald_write_string(30, 0, FONT_5x7, FALSE, dstr);
	oswald_draw_line(0,7,95,7, 1);


	// here goes the text
	msglen = strlen(Msg[sdata->pos+sdata->offset].msg);
	strpos=0;
	line=0;
	while ((strpos < msglen) && (line < 6)) {
		strpos += oswald_write_string_length(4, 9+(line*12), 84, FONT_DROID8x12, FALSE, &Msg[sdata->pos+sdata->offset].msg[strpos]);
		line++;
	}


	oswald_draw_line(0,87,95,87, 1);

	if (Msg[sdata->offset + sdata->pos].type != MSG_TYPE_END) {
		msginfo2str(Msg+sdata->pos+sdata->offset,dstr,sizeof(dstr));
		oswald_write_string(2, 89, FONT_5x7, FALSE, dstr);
	}

	hal_lcd_update_display();
}

event_ret_t handle_message_screen_buttons(watch_button button, messages_data_t *sdata)
{
	switch (button) {
		case BUTTON_A:
			break;
		case BUTTON_B:
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_C:
			// OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL);
			OswaldState.screen_id = MESSAGES_SCREEN;
			OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
			OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			return EVENT_RET_HANDLED;
			break;
		default:
			break;
	}

	return EVENT_RET_UNHANDLED;
}

event_ret_t message_screen_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			draw_message_screen(&messages_screen);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return handle_message_screen_buttons(*(watch_button *)data, &messages_screen);
			break;
		default:
			break;
	};
	return EVENT_RET_UNHANDLED;
}

void draw_messages_screen(messages_data_t *sdata)
{
	int w;
	static char msg[]={"(disabled)"};
	hal_lcd_clear_display();

	oswald_draw_bitmap(36, 3, message_icon_width, message_icon_height, 1, message_icon_bits);
	w=oswald_getwidth_font58(msg);
	oswald_write_string((96-w-1)/2, 44, FONT_5x8, FALSE, msg);

	hal_lcd_update_display();
#if 0
	char dstr[32];
	int off;
	int i;

	hal_lcd_clear_display();

	// oswald_draw_bitmap(36, 0, message_icon_width, message_icon_height, message_icon_bits);

	oswald_draw_bitmap(81, 6, upbutton_icon_width, upbutton_icon_height, upbutton_icon_bits);
	oswald_draw_bitmap(81, 38, downbutton_icon_width, downbutton_icon_height, downbutton_icon_bits);
	oswald_draw_bitmap(81, 70, enterbutton_icon_width, enterbutton_icon_height, enterbutton_icon_bits);

	if (Msg[sdata->offset + sdata->pos].type != MSG_TYPE_END) {
		off=0;
		dstr[off++]='#';
		off+=num2str(sdata->pos + sdata->offset + 1,dstr+off,2,2);
		dstr[off++]='/';
		off+=num2str(Msgs,dstr+off,2,2);
		dstr[off++]='\0';
		oswald_write_string(30, 0, FONT_5x7, FALSE, dstr);
	}
	oswald_draw_line(0,7,95,7);

	for (i=0; i<6; i++) {
		if (Msg[i+sdata->offset].type != MSG_TYPE_END) {
			//oswald_write_string_length(4, 9+(i*12), 84, FONT_DROID8x12, (Msg[i+sdata->offset].type == MSG_TYPE_NEW), Msg[i+sdata->offset].msg);
			if (Msg[i+sdata->offset].type == MSG_TYPE_NEW)
				oswald_write_string_length(4, 9+(i*12), 84, FONT_DROID8x12b, (i+sdata->offset) == (sdata->offset + sdata->pos), Msg[i+sdata->offset].msg);
			else
				oswald_write_string_length(4, 9+(i*12), 84, FONT_DROID8x12, (i+sdata->offset) == (sdata->offset + sdata->pos), Msg[i+sdata->offset].msg);
		} else {
			oswald_draw_bitmap(0, 66, leftbutton_icon_width, leftbutton_icon_height, leftbutton_icon_bits);
		}
	}

	// marker selected msg
	oswald_draw_line(1,9,1,81);
	oswald_draw_line_ww(1,10+(sdata->pos*12),1,20+(sdata->pos*12),2);

	oswald_draw_line(0,87,95,87);

	if (Msg[sdata->offset + sdata->pos].type != MSG_TYPE_END) {
		msginfo2str(Msg+sdata->pos+sdata->offset,dstr,sizeof(dstr));
		oswald_write_string(2, 89, FONT_5x7, FALSE, dstr);
	}

	hal_lcd_update_display();
#endif
}

event_ret_t handle_messages_screen_buttons(watch_button button, messages_data_t *sdata)
{
#if 0
	switch (button) {
		case BUTTON_A:
			sdata->pos--;
			if (sdata->pos < 0) {
				if (sdata->offset > 0) {
					sdata->pos = 0;
					sdata->offset--;
					if (sdata->offset < 0)
						sdata->offset = 0;
				} else {
					sdata->pos = 5;
					sdata->offset = (Msgs - 5);
					if (sdata->offset < 0)
						sdata->offset = 0;
				}
			}
			draw_messages_screen(&messages_screen);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_B:
			sdata->pos++;
			if (sdata->pos > 5) {
				sdata->pos = 5;
				sdata->offset++;
				if ((sdata->offset + 5) > Msgs) {
					sdata->offset = 0;
					sdata->pos = 0;
				}
			}
			draw_messages_screen(&messages_screen);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_C:
			if (Msg[sdata->offset + sdata->pos].type == MSG_TYPE_END)
				return EVENT_RET_UNHANDLED;
			else
				// OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL);
				OswaldState.screen_id = MESSAGE_SCREEN;
				OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
				OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
				return EVENT_RET_HANDLED;
			break;
		default:
			break;
	}
#endif
	return EVENT_RET_UNHANDLED;
}

event_ret_t messages_screen_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
//			messages_screen.pos = 0;
//			messages_screen.offset = 0;
			draw_messages_screen(&messages_screen);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return handle_messages_screen_buttons(*(watch_button *)data, &messages_screen);
			break;
		default:
			break;
	};
	return EVENT_RET_UNHANDLED;
}

/* calendar overview */
typedef struct {
	int16_t off;
} calendar_overview_data_t;
static calendar_overview_data_t calendar_overview_screen = {
	0
};

void draw_calendar_overview_screen(int off)
{
	static const char wnames[]={"MTWTFSS"};
	static const char *mnames[]={"January","February","March","April","May","June","July","August","September","October","November","December"};
	int i,w,val,x,y;
	int yday,k;
	int *yeardays;
	int l;
	int month,year;
	unsigned char time[6];
	/* shift the month by the amount on "off" */
	month=OswaldClk.month+off;
	year=OswaldClk.year;
	if(month<=0) {
		while(month<=0) {
			year--;
			month+=12;
		}
	} else {
		year+=((month-1)/12);
		month=((month-1)%12)+1;
	}
	/* do the actual drawing */
	hal_lcd_clear_display();
	oswald_draw_rect(2,0,93,17,1);
	oswald_draw_grid(2,17,93,95,13,13);
	for(i=0;i<7;i++) 
		oswald_draw_font58(6+13*i, 30-9-13+2, wnames[i],&w,0,1);
	oswald_write_number(4,0, FONT_6x9, 1, year);
	w=4*5;
	oswald_write_string(4+w+5,0, FONT_6x9, 1, (char *) mnames[(month+11)%12]);
	val = OswaldClk.hour;
	if(!OswaldClk.clk24hr)
		val=(val>12)?val-12:val;
	time[0]=(val/10)+'0';
	time[1]=(val%10)+'0';
	time[2]=':';
	time[3]=(OswaldClk.minute/10)+'0';
	time[4]=(OswaldClk.minute%10)+'0';
	time[5]='\0';
	oswald_write_string(93-23,0, FONT_6x9, 1, (char *) time);
	mday2yday(year,month,1,&yday);
	yday2wday(year,yday,&k);
	k=(k+6)%7; /* wday for Monday is 1, we want it to be 0 */ 
	yeardays=GetYearDays(year);
	l=yeardays[month]-yeardays[month-1];
	for(i=1;i<=l;i++) {
		x=2+2+((i+k-1)%7)*13;
		y=17+2+((i+k-1)/7)*13;
		if(off==0 && i==OswaldClk.day) {
			oswald_draw_grid(x-3,y-3,x+13+1-3+1,y+13+1-3+1,15,15);
			oswald_draw_grid(x-3+1,y-3+1,x+13+1-3,y+13+1-3,15,15);
		}
		if(i<10) {
			oswald_draw_tinydigit(x,y,i+'0',&w,1,0);
		} else {
			oswald_draw_tinydigit(x,y,i/10+'0',&w,1,0);
			oswald_draw_tinydigit(x+w,y,i%10+'0',&w,1,0);
		}
	}
	hal_lcd_update_display();
}

event_ret_t handle_calendar_overview_buttons(watch_button button, calendar_overview_data_t *calendar_overview_screen)
{
	switch (button) {
		case BUTTON_A:
		case BUTTON_C:
			calendar_overview_screen->off+=((button==BUTTON_C)?1:-1);
			draw_calendar_overview_screen(calendar_overview_screen->off);
			return EVENT_RET_HANDLED;
			break;
		case BUTTON_B:
                        OswaldState.screen_id = IDLE_SCREEN;
                        OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
                        OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			return EVENT_RET_HANDLED;
			break;
		default:
			break;
	}

	return EVENT_RET_UNHANDLED;
}

event_ret_t calendar_overview_handle_events(uint16_t event, void *data)
{
	switch (event) {
		case EVENT_SCREEN_VISIBLE:
			calendar_overview_screen.off=0;
			draw_calendar_overview_screen(0);
			return EVENT_RET_HANDLED;
			break;
		case EVENT_USER_BUTTONS:
			dbg_out("button event %d\n", *(int *)data);
			return handle_calendar_overview_buttons(*(watch_button *)data, &calendar_overview_screen);
			break;
		case EVENT_ONE_SEC_TIMER:
			return EVENT_RET_HANDLED;
		default:
			break;
	};
	return EVENT_RET_UNHANDLED;
}