//! @file

#include "kernel/kernel_heap.h"
#include "memfault/components.h"
#include "drivers/imu/lsm6dso/lsm6dso.h"
#include "services/common/battery/battery_state.h"
#include "util/heap.h"
#include "services/common/analytics/analytics_metric_table.h"
#include "services/common/analytics/analytics_external.h"
#include "applib/app_message/app_message_internal.h"
#include "shell/normal/watchface.h"
#include "process_management/app_install_manager.h"

int memfault_platform_get_stateofcharge(sMfltPlatformBatterySoc *soc) {
  BatteryChargeState chargestate = battery_get_charge_state();

  *soc = (sMfltPlatformBatterySoc){
      .soc = chargestate.charge_percent,
      .discharging = !chargestate.is_charging,
  };

  return 0;
}

// Forward curated Pebble analytics metrics to Memfault (~70 total).
// Only forwards the subset of metrics we've defined in memfault_metrics_heartbeat_config.def
void memfault_metric_set_device_from_pebble_analytics(AnalyticsMetric metric, int64_t val) {
  switch (metric) {
    // System Health & Stability
    case ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_CODE:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_CODE, val);
      break;
    case ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_LR:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_LR, val);
      break;
    case ANALYTICS_DEVICE_METRIC_DEVICE_UP_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_DEVICE_UP_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_KERNEL_HEAP_MIN_HEADROOM_BYTES:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_KERNEL_HEAP_MIN_HEADROOM_BYTES, val);
      break;
    case ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_MAIN:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_MAIN, val);
      break;
    case ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_BACKGROUND:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_STACK_FREE_KERNEL_BACKGROUND, val);
      break;
    case ANALYTICS_DEVICE_METRIC_STACK_FREE_NEWTIMERS:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_STACK_FREE_NEWTIMERS, val);
      break;
    case ANALYTICS_DEVICE_METRIC_PFS_SPACE_FREE_KB:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_PFS_SPACE_FREE_KB, val);
      break;
    case ANALYTICS_DEVICE_METRIC_APP_CRASHED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_APP_CRASHED_COUNT, val);
      break;

    // Battery & Power
    case ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE_DELTA:
      MEMFAULT_METRIC_SET_SIGNED(ANALYTICS_DEVICE_METRIC_BATTERY_VOLTAGE_DELTA, val);
      break;
    case ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT_DELTA:
      MEMFAULT_METRIC_SET_SIGNED(ANALYTICS_DEVICE_METRIC_BATTERY_PERCENT_DELTA, val);
      break;
    case ANALYTICS_DEVICE_METRIC_BATTERY_CHARGE_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_BATTERY_CHARGE_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_BATTERY_PLUGGED_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_BATTERY_PLUGGED_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_CPU_RUNNING_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_CPU_RUNNING_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_CPU_STOP_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_CPU_STOP_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_CPU_SLEEP_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_CPU_SLEEP_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_BACKLIGHT_ON_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_VIBRATOR_ON_COUNT, val);
      break;

    // User Interaction (2 metrics)
    case ANALYTICS_DEVICE_METRIC_BUTTON_PRESSED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_BUTTON_PRESSED_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_STATIONARY_TIME_MINUTES:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_STATIONARY_TIME_MINUTES, val);
      break;

    // Accelerometer Health & Activity
    case ANALYTICS_DEVICE_METRIC_ACCEL_SAMPLE_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ACCEL_SAMPLE_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_ACCEL_RESET_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ACCEL_RESET_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_ACCEL_FIFO_OVERRUN_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ACCEL_FIFO_OVERRUN_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_ACCEL_SHAKE_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ACCEL_SHAKE_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_ACCEL_DOUBLE_TAP_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ACCEL_DOUBLE_TAP_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_ACCEL_PEEK_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ACCEL_PEEK_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_ACCEL_XYZ_DELTA:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ACCEL_XYZ_DELTA, val);
      break;

    // Hardware I/O Health
    case ANALYTICS_DEVICE_METRIC_I2C_ERROR_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_I2C_ERROR_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_I2C_MAX_TRANSFER_DURATION_TICKS:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_I2C_MAX_TRANSFER_DURATION_TICKS, val);
      break;
    case ANALYTICS_DEVICE_METRIC_FLASH_READ_BYTES_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_FLASH_READ_BYTES_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_FLASH_WRITE_BYTES_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_FLASH_WRITE_BYTES_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_FLASH_ERASE_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_FLASH_ERASE_COUNT, val);
      break;

    // Phone Call Handling
    case ANALYTICS_DEVICE_METRIC_PHONE_CALL_INCOMING_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_PHONE_CALL_INCOMING_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_PHONE_CALL_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_PHONE_CALL_TIME, val);
      break;

    // Notifications
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_DND_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_RECEIVED_DND_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_DISMISSED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_DISMISSED_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_CLOSED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_CLOSED_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_BYTE_IN_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_BYTE_IN_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_PARSE_ERROR_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_PARSE_ERROR_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_SMS_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_NOTIFICATION_ANCS_SMS_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_REMINDER_RECEIVED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_REMINDER_RECEIVED_COUNT, val);
      break;

    // App Usage
    case ANALYTICS_DEVICE_METRIC_APP_ROCKY_LAUNCH_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_APP_ROCKY_LAUNCH_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_APP_ROCKY_CRASHED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_APP_ROCKY_CRASHED_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_APP_USER_LAUNCH_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_APP_USER_LAUNCH_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_APP_QUICK_LAUNCH_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_APP_QUICK_LAUNCH_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_APP_THROTTLED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_APP_THROTTLED_COUNT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_APP_NOTIFIED_DISCONNECTED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_APP_NOTIFIED_DISCONNECTED_COUNT, val);
      break;

    // User Preferences
    case ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_SETTING_SHAKE_TO_LIGHT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_SETTING_SHAKE_TO_LIGHT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_INTENSITY_PCT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_INTENSITY_PCT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_TIMEOUT_SEC:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_SETTING_BACKLIGHT_TIMEOUT_SEC, val);
      break;
    case ANALYTICS_DEVICE_METRIC_SETTING_VIBRATION_STRENGTH:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_SETTING_VIBRATION_STRENGTH, val);
      break;

    // Display & UI
    case ANALYTICS_DEVICE_METRIC_WATCH_ONLY_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_WATCH_ONLY_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_DISPLAY_UPDATES_PER_HOUR:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_DISPLAY_UPDATES_PER_HOUR, val);
      break;
    case ANALYTICS_DEVICE_METRIC_FPGA_REPROGRAM_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_FPGA_REPROGRAM_COUNT, val);
      break;

    // Health/HRM
    case ANALYTICS_DEVICE_METRIC_HRM_ACCEL_DATA_MISSING:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_HRM_ACCEL_DATA_MISSING, val);
      break;
    case ANALYTICS_DEVICE_METRIC_HRM_ON_TIME:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_HRM_ON_TIME, val);
      break;
    case ANALYTICS_DEVICE_METRIC_HRM_WATCHDOG_TIMEOUT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_HRM_WATCHDOG_TIMEOUT, val);
      break;
    case ANALYTICS_DEVICE_METRIC_ALARM_SOUNDED_COUNT:
      MEMFAULT_METRIC_SET_UNSIGNED(ANALYTICS_DEVICE_METRIC_ALARM_SOUNDED_COUNT, val);
      break;

    default:
      // This metric is not in our curated list, don't forward to Memfault
      break;
  }
}

// Record some metrics.
void memfault_metrics_heartbeat_collect_data(void) {
  // battery_state_get_voltage() actually returns the voltage in millivolts,
  // which is the unit for the battery_v metric as recorded on device.
  MEMFAULT_METRIC_SET_UNSIGNED(battery_v, battery_state_get_voltage());

  // Kernel heap usage
  Heap *kernel_heap = kernel_heap_get();
  const uint32_t kernel_heap_size = heap_size(kernel_heap);
  const uint32_t kernel_heap_max_used = kernel_heap->high_water_mark;
  // kernel_heap_pct is a percentage with 2 decimal places of precision
  // (i.e. 10000 = 100.00%)
  const uint32_t kernel_heap_pct = (kernel_heap_max_used * 10000) / kernel_heap_size;

  MEMFAULT_LOG_INFO("Heap Usage: %lu/%lu (%lu.%02lu%%)\n", kernel_heap_max_used, kernel_heap_size,
                    kernel_heap_pct / 100, kernel_heap_pct % 100);

  MEMFAULT_METRIC_SET_UNSIGNED(memory_pct_max, kernel_heap_pct);

#if PLATFORM_ASTERIX
  // Capture accelerometer diagnostics to catch LSM6DSO stuck states.
  Lsm6dsoDiagnostics accel_diag;
  lsm6dso_get_diagnostics(&accel_diag);
  MEMFAULT_METRIC_SET_SIGNED(accel_lsm6dso_last_x_mg, accel_diag.last_sample_mg[0]);
  MEMFAULT_METRIC_SET_SIGNED(accel_lsm6dso_last_y_mg, accel_diag.last_sample_mg[1]);
  MEMFAULT_METRIC_SET_SIGNED(accel_lsm6dso_last_z_mg, accel_diag.last_sample_mg[2]);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_sample_age_ms, accel_diag.last_sample_age_ms);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_read_age_ms, accel_diag.last_successful_read_age_ms);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_interrupt_age_ms, accel_diag.last_interrupt_age_ms);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_wake_age_ms, accel_diag.last_wake_event_age_ms);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_double_tap_age_ms, accel_diag.last_double_tap_age_ms);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_state_flags, accel_diag.state_flags);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_i2c_error_count, accel_diag.i2c_error_count);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_consecutive_errors, accel_diag.consecutive_error_count);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_interrupt_count, accel_diag.interrupt_count);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_wake_events, accel_diag.wake_event_count);
  MEMFAULT_METRIC_SET_UNSIGNED(accel_lsm6dso_double_tap_events,
                               accel_diag.double_tap_event_count);
#endif

  extern uint32_t metric_firm_425_back_button_long_presses_cancelled;
  MEMFAULT_METRIC_SET_UNSIGNED(firm_425_back_button_long_presses_cancelled, metric_firm_425_back_button_long_presses_cancelled);

  // AppMessage metrics
  MEMFAULT_METRIC_SET_UNSIGNED(app_message_sent_count, app_message_outbox_get_sent_count());
  MEMFAULT_METRIC_SET_UNSIGNED(app_message_received_count, app_message_inbox_get_received_count());
  
#if !RECOVERY_FW
  // Active watchface name
  AppInstallId watchface_id = watchface_get_default_install_id();
  AppInstallEntry watchface_entry;
  if (app_install_get_entry_for_install_id(watchface_id, &watchface_entry)) {
    MEMFAULT_METRIC_SET_STRING(active_watchface_name, watchface_entry.name);
  } else {
    MEMFAULT_METRIC_SET_STRING(active_watchface_name, "Unknown");
  }

  // Update Pebble analytics and forward curated metrics to Memfault
  analytics_external_update();
#endif
}
