#include "oswald.h"
#include "oswald_watch_faces.h"
#include "oswald_screens.h"
#include "oswald_hal.h"

#include "embedvm.h"
#include "mymem.h"
#include "strutils.h"

#include "oswald_main.h"

/*
 * some variable defining our curent state
 * these are globals in order to avoid having to pass pointers
 * through function calls thus saving stack space
 */
clock_state OswaldClk;
alarm_clk OswaldAlarm[MAX_ALARM];
watch_state OswaldState;
watch_screen OswaldScreens[SCREENS_END];
power_state OswaldPowerState;
uint8_t backlight_safety_off = 0;
char MainMessage[148];


void oswald_change_to_screen(screen_number screen_id)
{
	// we spare the update if no change happened
	if (OswaldState.screen_id != screen_id) {
		OswaldState.screen_id = screen_id;
		if ((OswaldState.screen->event_func != NULL) &&
		    (OswaldState.screen->event_mask & EVENT_SCREEN_VISIBLE))
				OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
	}
}

void oswald_set_time(uint8_t hour, uint8_t minute, uint8_t second, boolean clk24hr)
{
	OswaldClk.hour = hour;
	OswaldClk.minute = minute;
	OswaldClk.second = second;
	OswaldClk.clk24hr = clk24hr;
}

void oswald_set_date(uint8_t day, uint8_t month, uint16_t year, boolean day_first)
{
	OswaldClk.day = day;
	OswaldClk.month = month;
	OswaldClk.year = year;
	OswaldClk.day_first = day_first;
}

static void update_clock_state (void)
{
	hal_get_rtc(&OswaldClk);

	/* check for pending alarm once per minute */
	if (OswaldClk.second == 0) {
		int i;
		for(i=0;i<MAX_ALARM;i++) {
			if (OswaldClk.hour == OswaldAlarm[i].hour &&
				OswaldClk.minute == OswaldAlarm[i].minute &&
				((1 << OswaldClk.wday) & OswaldAlarm[i].wday)) {
				OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL);
				OswaldState.screen_id = ALARM_SCREEN;
				OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
				OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
				break;
			}
		}
	}
}

void oswald_one_second_tick(void)
{
	/* update clock - should use RTC if available */
	update_clock_state();

	hal_get_power_state(&OswaldPowerState);
	if (backlight_safety_off) {
		backlight_safety_off--;
		if (!backlight_safety_off)
			hal_lcd_set_backlight(FALSE);
	}

	/* wake-up screen if interested in the one-second-event */
	if (OswaldState.screen->event_func != NULL &&
	    (OswaldState.screen->event_mask & EVENT_ONE_SEC_TIMER))
		OswaldState.screen->event_func(EVENT_ONE_SEC_TIMER, NULL);
}

void oswald_centisecond_tick(void)
{
	if (OswaldState.screen->event_func != NULL &&
	    (OswaldState.screen->event_mask & EVENT_CS_TIMER))
		OswaldState.screen->event_func(EVENT_CS_TIMER, NULL);
}

void oswald_halfsecond_tick(void)
{
	if (OswaldState.screen->event_func != NULL &&
	    (OswaldState.screen->event_mask & EVENT_HALF_SEC_TIMER))
		OswaldState.screen->event_func(EVENT_HALF_SEC_TIMER, NULL);
}

void oswald_handle_button_press(watch_button button)
{
	switch (button) {
		case BUTTON_A:
		case BUTTON_B:
		case BUTTON_C:
		case BUTTON_F:
			if (OswaldState.screen->event_func != NULL &&
			    (OswaldState.screen->event_mask & EVENT_USER_BUTTONS)) {
			    	OswaldState.screen->event_func(EVENT_USER_BUTTONS, &button);
			}
			break;
		case BUTTON_E:
			if (OswaldState.screen->event_func != NULL &&
			    (OswaldState.screen->event_mask & EVENT_USER_BUTTONS)) {
			    	if (OswaldState.screen->event_func(EVENT_USER_BUTTONS, &button) == EVENT_RET_HANDLED)
			    		break;
			}
			OswaldState.screen->event_func(EVENT_SCREEN_DESTROY, NULL);
#if 0
			// next screen
			OswaldState.screen_id++;
			if (OswaldState.screen_id >= LAST_SCREEN) {
				OswaldState.screen_id = IDLE_SCREEN;
			};
#else
			OswaldState.screen_id = IDLE_SCREEN;
#endif
			OswaldState.screen = &OswaldScreens[OswaldState.screen_id];
			OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
			break;
		case BUTTON_D:
			// backlight on/off
			if (hal_lcd_get_backlight()) {
				hal_lcd_set_backlight(FALSE);
				backlight_safety_off = 0;
			} else {
				hal_lcd_set_backlight(TRUE);
				backlight_safety_off = 2;
			}
			break;
		default:
			// should never get here
			break;
	};
}

void oswald_handle_accel_event(uint8_t x, uint8_t y, uint8_t z)
{
	accel_data_t accel_data;

	accel_data.x = x;
	accel_data.y = y;
	accel_data.z = z;

	if (OswaldState.screen->event_func != NULL &&
	    (OswaldState.screen->event_mask & EVENT_ACCEL_UPDATE))
		OswaldState.screen->event_func(EVENT_ACCEL_UPDATE, &accel_data);
}

void oswald_handle_ambientlight_event(uint8_t light_level)
{
	if (OswaldState.screen->event_func != NULL &&
	    (OswaldState.screen->event_mask & EVENT_AMBIENTLIGHT_UPDATE))
		OswaldState.screen->event_func(EVENT_AMBIENTLIGHT_UPDATE, &light_level);
}

void oswald_handle_comm_input(uint16_t mlen, const void *mdat)
{
	char *icmd = (char *) mdat;

	if (icmd[0] == '$') {
		if (strncmp(icmd, "$GRT", 4) == 0) { // get current RTC
			char rtime[12];
			mymemcpy(rtime,"#RTC",4);
			num2str(OswaldClk.hour,rtime+4,2,2);
			num2str(OswaldClk.minute,rtime+6,2,2);
			num2str(OswaldClk.second,rtime+8,2,2);
			rtime[10]='\n';
			rtime[11]='\0';
			hal_bluetooth_send_data(rtime, strlen(rtime));
		} else if (strncmp(icmd, "$SRT", 4) == 0) { // set current RTC
			char rtime[16];
			mymemcpy(rtime,"#USP\n",6);
			hal_bluetooth_send_data(rtime, strlen(rtime));
		} else if (strncmp(icmd, "$MSG", 4) == 0) { // message on main screen
			char *msg = (icmd+4);
			mlen -= 4;
			mymemset(MainMessage, 0, 148);
			if(mlen>147) 
				mlen=147;
			mymemcpy(MainMessage,msg,mlen);
			MainMessage[mlen]='\0';
		} else if (strncmp(icmd, "$MCL", 4) == 0) { // clear message
			mymemset(MainMessage, 0, 148);
		} else if (strncmp(icmd, "$BAT", 4) == 0) { // clear message
			char rtime[16];
			int off;
			mymemcpy(rtime,"#BAT",4);
			off=4;
			off+=num2str(OswaldPowerState.charge_state,rtime+off,1,sizeof(rtime)-3-off);
			rtime[off++]=',';
			off+=num2str(OswaldPowerState.percent,rtime+off,1,sizeof(rtime)-2-off);
			rtime[off++]='\n';
			rtime[off++]='\0';
			hal_bluetooth_send_data(rtime, strlen(rtime));
		} else if (strncmp(icmd, "$GAL", 4) == 0) { // get ambient light value
			char rtime[16],off;
			mymemcpy(rtime,"#GAL",4);
			off=4;
			off+=num2str(hal_amblight_get_val(),rtime+off,1,sizeof(rtime)-2-off);
			rtime[off++]='\n';
			rtime[off++]='\0';
			hal_bluetooth_send_data(rtime, strlen(rtime));
		}
	}
}

void oswald_init(void)
{
	int i;
	OswaldScreens[IDLE_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_ONE_SEC_TIMER;
	OswaldScreens[IDLE_SCREEN].event_func = idle_handle_events;

	OswaldScreens[MAIN_MENU_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_ONE_SEC_TIMER;
	OswaldScreens[MAIN_MENU_SCREEN].event_func = main_menu_handle_events;

	OswaldScreens[ALARM_SETUP_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_HALF_SEC_TIMER;
	OswaldScreens[ALARM_SETUP_SCREEN].event_func = alarm_setup_events;

	OswaldScreens[STOP_WATCH_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_CS_TIMER;
	OswaldScreens[STOP_WATCH_SCREEN].event_func = stop_watch_handle_events;

	OswaldScreens[BLUETOOTH_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_HALF_SEC_TIMER;
	OswaldScreens[BLUETOOTH_SCREEN].event_func = bluetooth_screen_events;

	OswaldScreens[ACCEL_DISPLAY_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_ACCEL_UPDATE;
	OswaldScreens[ACCEL_DISPLAY_SCREEN].event_func = accel_handle_events;

	OswaldScreens[MESSAGES_SCREEN].event_mask = EVENT_USER_BUTTONS;
	OswaldScreens[MESSAGES_SCREEN].event_func = messages_screen_handle_events;

	OswaldScreens[MESSAGE_SCREEN].event_mask = EVENT_USER_BUTTONS;
	OswaldScreens[MESSAGE_SCREEN].event_func = message_screen_handle_events;

	OswaldScreens[INFO_SCREEN].event_mask = 0x00; // this one does not consume any events
	OswaldScreens[INFO_SCREEN].event_func = info_screen_handle_events;

	OswaldScreens[DATETIME_SETTING_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_HALF_SEC_TIMER;
	OswaldScreens[DATETIME_SETTING_SCREEN].event_func = datetime_setup_events;
	OswaldScreens[CALENDAR_OVERVIEW_SCREEN].event_mask = EVENT_USER_BUTTONS|EVENT_ONE_SEC_TIMER;
	OswaldScreens[CALENDAR_OVERVIEW_SCREEN].event_func = calendar_overview_handle_events;

	OswaldScreens[MENU_TEST_SCREEN].event_mask = EVENT_USER_BUTTONS;
	OswaldScreens[MENU_TEST_SCREEN].event_func = test_menu_handle_events;

	OswaldScreens[ALARM_SCREEN].event_mask = EVENT_USER_BUTTONS | EVENT_HALF_SEC_TIMER;
	OswaldScreens[ALARM_SCREEN].event_func = alarm_handle_events;

	OswaldState.screen_id = IDLE_SCREEN;
	OswaldState.screen = &OswaldScreens[OswaldState.screen_id];

	if (OswaldState.screen->event_func != NULL)
		OswaldState.screen->event_func(EVENT_SCREEN_VISIBLE, NULL);
	for(i=0;i<MAX_ALARM;i++) {
		OswaldAlarm[i].hour = 12;
		OswaldAlarm[i].minute = 0;
		OswaldAlarm[i].wday = 0x00;
	}
}