diff options
Diffstat (limited to 'src/c')
| -rw-r--r-- | src/c/fastforge.c | 143 | ||||
| -rw-r--r-- | src/c/fastforge.h | 10 |
2 files changed, 150 insertions, 3 deletions
diff --git a/src/c/fastforge.c b/src/c/fastforge.c index d248ab8..79d9217 100644 --- a/src/c/fastforge.c +++ b/src/c/fastforge.c @@ -5,20 +5,153 @@ FastEntry history[MAX_FASTS]; int history_count = 0; FastEntry current_fast = {0}; uint16_t global_target_minutes = DEFAULT_TARGET_MINUTES; +StreakData streak_data = {0}; AppTimer *alarm_timer = NULL; time_t target_time = 0; static Window *s_main_window; static TextLayer *s_text_layer; +static char s_status_text[96]; + +static int clamp_history_count(int value) { + if (value < 0) { + return 0; + } + if (value > MAX_FASTS) { + return MAX_FASTS; + } + return value; +} + +static void normalize_loaded_data(void) { + if (global_target_minutes == 0) { + global_target_minutes = DEFAULT_TARGET_MINUTES; + } + + if (current_fast.start_time != 0 && current_fast.target_minutes == 0) { + current_fast.target_minutes = global_target_minutes; + } + + if (current_fast.end_time != 0 && current_fast.end_time < current_fast.start_time) { + current_fast.end_time = 0; + } +} + +void save_all_data(void) { + persist_write_int(KEY_HISTORY_COUNT, history_count); + persist_write_data(KEY_HISTORY_DATA, history, sizeof(FastEntry) * history_count); + persist_write_data(KEY_CURRENT_FAST, ¤t_fast, sizeof(FastEntry)); + persist_write_int(KEY_TARGET_MIN, global_target_minutes); + persist_write_data(KEY_STREAK_DATA, &streak_data, sizeof(StreakData)); +} + +void load_all_data(void) { + history_count = 0; + memset(history, 0, sizeof(history)); + memset(¤t_fast, 0, sizeof(current_fast)); + global_target_minutes = DEFAULT_TARGET_MINUTES; + memset(&streak_data, 0, sizeof(streak_data)); + + if (persist_exists(KEY_HISTORY_COUNT)) { + history_count = clamp_history_count(persist_read_int(KEY_HISTORY_COUNT)); + } + if (history_count > 0 && persist_exists(KEY_HISTORY_DATA)) { + const int expected_size = sizeof(FastEntry) * history_count; + const int read_size = persist_read_data(KEY_HISTORY_DATA, history, expected_size); + if (read_size != expected_size) { + history_count = 0; + memset(history, 0, sizeof(history)); + } + } + if (persist_exists(KEY_CURRENT_FAST)) { + const int read_size = persist_read_data(KEY_CURRENT_FAST, ¤t_fast, sizeof(FastEntry)); + if (read_size != (int)sizeof(FastEntry)) { + memset(¤t_fast, 0, sizeof(current_fast)); + } + } + if (persist_exists(KEY_TARGET_MIN)) { + const int read_target = persist_read_int(KEY_TARGET_MIN); + if (read_target > 0 && read_target <= UINT16_MAX) { + global_target_minutes = (uint16_t)read_target; + } + } + if (persist_exists(KEY_STREAK_DATA)) { + const int read_size = persist_read_data(KEY_STREAK_DATA, &streak_data, sizeof(StreakData)); + if (read_size != (int)sizeof(StreakData)) { + memset(&streak_data, 0, sizeof(streak_data)); + } + } + + normalize_loaded_data(); +} + +static void refresh_status_text(void) { + if (current_fast.start_time == 0) { + snprintf(s_status_text, sizeof(s_status_text), "No active fast\nTarget: %u min\nSELECT: Start\nDOWN: Quit", + global_target_minutes); + } else { + const time_t elapsed = time(NULL) - current_fast.start_time; + const int elapsed_hours = (int)(elapsed / 3600); + const int elapsed_minutes = (int)((elapsed % 3600) / 60); + snprintf(s_status_text, sizeof(s_status_text), + "Fast running\n%02dh %02dm elapsed\nSELECT: Stop\nDOWN: Quit", + elapsed_hours, elapsed_minutes); + } + text_layer_set_text(s_text_layer, s_status_text); +} + +static void append_history_entry(const FastEntry *entry) { + if (!entry) { + return; + } + + if (history_count < MAX_FASTS) { + history[history_count++] = *entry; + return; + } + + memmove(&history[0], &history[1], sizeof(FastEntry) * (MAX_FASTS - 1)); + history[MAX_FASTS - 1] = *entry; +} + +static void mark_fast_completed(time_t end_time) { + streak_data.current_streak++; + if (streak_data.current_streak > streak_data.longest_streak) { + streak_data.longest_streak = streak_data.current_streak; + } + streak_data.last_completed_fast_end = end_time; +} // ==================== BUTTON HANDLERS ==================== static void select_click_handler(ClickRecognizerRef recognizer, void *context) { - text_layer_set_text(s_text_layer, "Hello!"); - APP_LOG(APP_LOG_LEVEL_INFO, "SELECT pressed - Hello!"); + (void)recognizer; + (void)context; + + if (current_fast.start_time == 0) { + current_fast.start_time = time(NULL); + current_fast.end_time = 0; + current_fast.target_minutes = global_target_minutes; + memset(current_fast.note, 0, sizeof(current_fast.note)); + current_fast.max_stage_reached = 0; + APP_LOG(APP_LOG_LEVEL_INFO, "Started fast at %ld", (long)current_fast.start_time); + } else { + FastEntry completed = current_fast; + completed.end_time = time(NULL); + append_history_entry(&completed); + mark_fast_completed(completed.end_time); + memset(¤t_fast, 0, sizeof(current_fast)); + APP_LOG(APP_LOG_LEVEL_INFO, "Stopped fast and saved to history (count=%d)", history_count); + } + + save_all_data(); + refresh_status_text(); } static void down_click_handler(ClickRecognizerRef recognizer, void *context) { + (void)recognizer; + (void)context; + APP_LOG(APP_LOG_LEVEL_INFO, "DOWN pressed - Quitting app..."); window_stack_pop_all(false); // Cleanly quit the app } @@ -41,18 +174,21 @@ static void window_load(Window *window) { text_layer_set_text_color(s_text_layer, GColorBlack); text_layer_set_text_alignment(s_text_layer, GTextAlignmentCenter); text_layer_set_font(s_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - text_layer_set_text(s_text_layer, "Press SELECT\nfor Hello\n\nDOWN to quit"); + refresh_status_text(); layer_add_child(window_layer, text_layer_get_layer(s_text_layer)); } static void window_unload(Window *window) { + (void)window; + save_all_data(); text_layer_destroy(s_text_layer); } // ==================== APP INIT / DEINIT ==================== static void init(void) { + load_all_data(); s_main_window = window_create(); window_set_click_config_provider(s_main_window, click_config_provider); window_set_window_handlers(s_main_window, (WindowHandlers) { @@ -64,6 +200,7 @@ static void init(void) { } static void deinit(void) { + save_all_data(); window_destroy(s_main_window); } diff --git a/src/c/fastforge.h b/src/c/fastforge.h index 1ab5339..201ce11 100644 --- a/src/c/fastforge.h +++ b/src/c/fastforge.h @@ -11,6 +11,12 @@ typedef struct { uint8_t max_stage_reached; // 0=none, 1=12h, 2=18h, 3=24h+ } FastEntry; +typedef struct { + uint16_t current_streak; + uint16_t longest_streak; + time_t last_completed_fast_end; +} StreakData; + #define MAX_FASTS 64 #define DEFAULT_TARGET_MINUTES (16 * 60) @@ -24,7 +30,11 @@ extern FastEntry history[MAX_FASTS]; extern int history_count; extern FastEntry current_fast; extern uint16_t global_target_minutes; +extern StreakData streak_data; extern AppTimer *alarm_timer; extern time_t target_time; +void save_all_data(void); +void load_all_data(void); + #endif |
