/*
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "drivers/ambient_light.h"
#include "services/common/hrm/hrm_manager.h"
#include "util/time/time.h"

//! @addtogroup Foundation
//! @{
//!   @addtogroup EventService
//!   @{
//!     @addtogroup HealthService
//!
//! \brief Get access to health information like step count, sleep totals, etc.
//!
//! The HealthService provides your app access to the step count and sleep activity of the user.
//!
//! @{

// convenient macros to distinguish between health and no health.
// TODO: PBL-21978 remove redundant comments as a workaround around for SDK generator
#if defined(PBL_HEALTH)

//! Convenience macro to switch between two expressions depending on health support.
//! On platforms with health support the first expression will be chosen, the second otherwise.
#define PBL_IF_HEALTH_ELSE(if_true, if_false) (if_true)

#else

//! Convenience macro to switch between two expressions depending on health support.
//! On platforms with health support the first expression will be chosen, the second otherwise.
#define PBL_IF_HEALTH_ELSE(if_true, if_false) (if_false)
#endif

//! Health metric values used to retrieve health data.
//! For example, using \ref health_service_sum().
typedef enum {
  //! The number of steps counted.
  HealthMetricStepCount,
  //! The number of seconds spent active (i.e. not resting).
  HealthMetricActiveSeconds,
  //! The distance walked, in meters.
  HealthMetricWalkedDistanceMeters,
  //! The number of seconds spent sleeping.
  HealthMetricSleepSeconds,
  //! The number of sleep seconds in the 'restful' or deep sleep state.
  HealthMetricSleepRestfulSeconds,
  //! The number of kcal (Calories) burned while resting due to resting metabolism.
  HealthMetricRestingKCalories,
  //! The number of kcal (Calories) burned while active.
  HealthMetricActiveKCalories,
  //! The heart rate, in beats per minute. This is a filtered value that is at most 15 minutes old.
  HealthMetricHeartRateBPM,
  //! The raw heart rate value of the most recent sample, in beats per minute.
  HealthMetricHeartRateRawBPM,
} HealthMetric;

//! Type used to represent HealthMetric values
typedef int32_t HealthValue;

//! Type used as a handle to a registered metric alert (returned by
//! \ref health_service_register_metric_alert)
struct HealthMetricAlert;
typedef struct HealthMetricAlert HealthMetricAlert;


//! Return the sum of a \ref HealthMetric's values over a time range.
//! The `time_start` and `time_end` parameters define the range of time you want the sum for.
//! @note The value returned will be based on daily totals, weighted for the length of the
//! specified time range. This may change in the future.
//! @param metric The metric to query for data.
//! @param time_start UTC time of the earliest data item to incorporate into the sum.
//! @param time_end UTC time of the most recent data item to incorporate into the sum.
//! @return The sum of that metric over the given time range, if available.
HealthValue health_service_sum(HealthMetric metric, time_t time_start, time_t time_end);

//! Convenience wrapper for \ref health_service_sum() that returns the sum for today.
//! @param metric The metric to query.
//! @return The sum of that metric's data for today, if available.
HealthValue health_service_sum_today(HealthMetric metric);

//! Convenience function for peeking at the current value of a metric. This is useful for metrics
//! like \ref HealthMetricHeartRateBPM that represent instantaneous values. It is NOT applicable for
//! metrics like \ref HealthMetricStepCount that must be accumulated over time (it will return 0 if
//! passed that type of metric). This call is equivalent to calling
//! `health_service_aggregate_averaged(metric, time(NULL), time(NULL), HealthAggregationAvg,
//! HealthServiceTimeScopeOnce)`
//! @param metric The metric to query.
//! @return The current value of that metric, if available.
HealthValue health_service_peek_current_value(HealthMetric metric);

//! Used by \ref health_service_sum_averaged() to specify how the average is computed.
typedef enum {
  //! No average computed. The result is the same as calling \ref health_service_sum().
  HealthServiceTimeScopeOnce,
  //! Compute average using the same day from each week. For example, every Monday if the passed
  //! in time range falls on a Monday.
  HealthServiceTimeScopeWeekly,
  //! Compute average using either weekdays (Monday to Friday) or weekends (Saturday and Sunday),
  //! depending on which day the passed in time range falls.
  HealthServiceTimeScopeDailyWeekdayOrWeekend,
  //! Compute average across all days of the week.
  HealthServiceTimeScopeDaily,
} HealthServiceTimeScope;

//! Return the value of a metric's sum over a given time range between `time_start`
//! and `time_end`. Using this call you can specify the time range that you are interested in
//! getting the average for, as well as a `scope` specifier on how to compute an average of the sum.
//! For example, if you want to get the average number of steps taken from 12 AM (midnight) to 9 AM
//! across all days you would specify:
//! \code{.c}
//! time_t time_start = time_start_of_today();
//! time_t time_end = time_start + (9 * SECONDS_PER_HOUR);
//! HealthValue value = health_service_sum_averaged(HealthMetricStepCount, time_start,
//!    time_end, HealthServiceTimeScopeDaily);
//! \endcode
//! If you want the average number of steps taken on a weekday (Monday to Friday) and today is a
//! Monday (in the local timezone) you would specify:
//! \code{.c}
//! time_start = time_start_of_today();
//! time_end = time_start + SECONDS_PER_DAY;
//! HealthValue value = health_service_sum_averaged(HealthMetricStepCount, time_start,
//!    time_end, HealthServiceTimeScopeDailyWeekdayOrWeekend);
//! \endcode
//!
//! Note that this call is the same as calling `health_service_aggregate_averaged(metric,
//! time_start, time_end, HealthAggregationSum, scope)`
//!
//! @param metric Which \ref HealthMetric to query.
//! @param time_start UTC time of the start of the query interval.
//! @param time_end UTC time of the end of the query interval.
//! @param scope \ref HealthServiceTimeScope value describing how the average should be computed.
//! @return The average of the sum of the given metric over the given time range, if available.
HealthValue health_service_sum_averaged(HealthMetric metric, time_t time_start, time_t time_end,
                                        HealthServiceTimeScope scope);


//! Used by \ref health_service_aggregate_averaged() to specify what type of aggregation to perform.
//! This aggregation is applied to the metric before the average is computed.
typedef enum {
  //! Sum the metric. The result is the same as calling \ref health_service_sum_averaged(). This
  //! operation is only applicable for metrics that accumulate, like HealthMetricStepCount,
  //! HealthMetricActiveSeconds, etc.
  HealthAggregationSum,
  //! Use the average of the metric. This is only applicable for metrics that measure instantaneous
  //! values, like HealthMetricHeartRateBPM
  HealthAggregationAvg,
  //! Use the minimum value of the metric. This is only applicable for metrics that measure
  //! instantaneous values, like HealthMetricHeartRateBPM
  HealthAggregationMin,
  //! Use the maximum value of the metric. This is only applicable for metrics that measure
  //! instantaneous values, like HealthMetricHeartRateBPM
  HealthAggregationMax
} HealthAggregation;


//! Return the value of an aggregated metric over a given time range. This call is more
//! flexible than health_service_sum_averaged because it lets you specify which aggregation function
//! to perform.
//!
//! The aggregation function `aggregation` is applied to the metric `metric` over the given time
//! range `time_start` to `time_end` first, and then an average is computed based on the passed in
//! `scope`.
//!
//! For example, if you want to get the average number of steps taken from 12 AM (midnight) to 9 AM
//! across all days you would specify:
//! \code{.c}
//! time_t time_start = time_start_of_today();
//! time_t time_end = time_start + (9 * SECONDS_PER_HOUR);
//! HealthValue value = health_service_aggregate_averaged(HealthMetricStepCount, time_start,
//!    time_end, HealthAggregationSum, HealthServiceTimeScopeDaily);
//! \endcode
//!
//! If you want to compute the average heart rate on Mondays and today is a Monday, you would
//! specify:
//! \code{.c}
//! time_t time_start = time_start_of_today(),
//! time_t time_end = time_start + SECONDS_PER_DAY,
//! HealthValue value = health_service_aggregate_averaged(HealthMetricHeartRateBPM, time_start,
//!    time_end, HealthAggregationAvg, HealthServiceTimeScopeWeekly);
//! \endcode
//!
//! To get the average of the minimum heart rate seen on Mondays for example, you would instead
//! pass in `HealthAggregationMin`
//!
//! Certain HealthAggregation operations are only applicable to certain types of metrics. See the
//! notes above on \ref HealthAggregation for details. Use
//! \ref health_service_metric_aggregate_averaged_accessible to check for applicability at run
//! time.
//!
//! @param metric Which \ref HealthMetric to query.
//! @param time_start UTC time of the start of the query interval.
//! @param time_end UTC time of the end of the query interval.
//! @param aggregation the aggregation function to perform on the metric. This operation is
//!   performed across the passed in time range `time_start` to `time_end`.
//! @param scope \ref HealthServiceTimeScope value describing how the average should be computed.
//!  Use `HealthServiceTimeScopeOnce` to not compute an average.
//! @return The average of the aggregation performed on the given metric over the given time range,
//!  if available.
HealthValue health_service_aggregate_averaged(HealthMetric metric, time_t time_start,
                                              time_t time_end, HealthAggregation aggregation,
                                              HealthServiceTimeScope scope);


//! Health-related activities that can be accessed using
// \ref health_service_peek_current_activities() and \ref health_service_activities_iterate().
typedef enum {
  //! No special activity.
  HealthActivityNone = 0,
  //! The 'sleeping' activity.
  HealthActivitySleep = 1 << 0,
  //! The 'restful sleeping' activity.
  HealthActivityRestfulSleep = 1 << 1,
  //! The 'walk' activity.
  HealthActivityWalk = 1 << 2,
  //! The 'run' activity.
  HealthActivityRun = 1 << 3,
  //! The 'generic' activity.
  HealthActivityOpenWorkout = 1 << 4,
} HealthActivity;

//! A mask value representing all available activities
#define HealthActivityMaskAll ((HealthActivityOpenWorkout << 1) - 1)

//! Expresses a set of \ref HealthActivity values as a bitmask.
typedef uint32_t HealthActivityMask;

//! Return a \ref HealthActivityMask containing a set of bits, one set for each
//! activity that is currently active.
//! @return A bitmask with zero or more \ref HealthActivityMask bits set as appropriate.
HealthActivityMask   health_service_peek_current_activities(void);

//! Callback used by \ref health_service_activities_iterate().
//! @param activity Which activity the caller is being informed about.
//! @param time_start Start UTC time of the activity.
//! @param time_end End UTC time of the activity.
//! @param context The `context` parameter initially passed
//!     to \ref health_service_activities_iterate().
//! @return `true` if you are interested in more activities, or `false` to stop iterating.
typedef bool (*HealthActivityIteratorCB)(HealthActivity activity, time_t time_start,
                                         time_t time_end, void *context);

//! Iteration direction, passed to \ref health_service_activities_iterate().
//! When iterating backwards (`HealthIterationDirectionPast`), activities that have a greater value
//! for `time_end` come first.
//! When iterating forward (`HealthIterationDirectionFuture`), activities that have a smaller value
//! for `time_start` come first.
typedef enum {
  //! Iterate into the past.
  HealthIterationDirectionPast,
  //! Iterate into the future.
  HealthIterationDirectionFuture,
} HealthIterationDirection;

//! Iterates backwards or forward within a given time span to list all recorded activities.
//! For example, this can be used to find the last recorded sleep phase or all deep sleep phases in
//! a given time range. Any activity that overlaps with `time_start` and `time_end` will be
//! included, even if the start time starts before `time_start` or end time ends after `time_end`.
//! @param activity_mask A bitmask containing set of activities you are interested in.
//! @param time_start UTC time of the earliest time you are interested in.
//! @param time_end UTC time of the latest time you are interested in.
//! @param direction The direction in which to iterate.
//! @param callback Developer-supplied callback that is called for each activity iterated over.
//! @param context Developer-supplied context pointer that is passed to the callback.
void health_service_activities_iterate(HealthActivityMask activity_mask, time_t time_start,
                                       time_t time_end, HealthIterationDirection direction,
                                       HealthActivityIteratorCB callback, void *context);

//! Possible values returned by \ref health_service_metric_accessible().
//! The values are used in combination as a bitmask.
//! For example, to check if any data is available for a given request use:
//! bool any_data_available = value & HealthServiceAccessibilityMaskAvailable;
typedef enum {
  //! Return values are available and represent the collected health information.
  HealthServiceAccessibilityMaskAvailable = 1 << 0,
  //! The user hasn't granted permission.
  HealthServiceAccessibilityMaskNoPermission = 1 << 1,
  //! The queried combination of time span and \ref HealthMetric or \ref HealthActivityMask
  //! is currently unsupported.
  HealthServiceAccessibilityMaskNotSupported = 1 << 2,
  //! No samples were recorded for the given time span.
  HealthServiceAccessibilityMaskNotAvailable = 1 << 3,
} HealthServiceAccessibilityMask;

//! Check if a certain combination of metric and time span is accessible using
//! \ref health_service_sum by returning a value of \ref HealthServiceAccessibilityMask. Developers
//! should check if the return value is \ref HealthServiceAccessibilityMaskAvailable before calling
//! \ref health_service_sum.
//!
//! Note that this call is the same as calling `health_service_metric_averaged_accessible(metric,
//! time_start, time_end, HealthServiceTimeScopeOnce)`
//!
//! @param metric The metric to query for data.
//! @param time_start Earliest UTC time you are interested in.
//! @param time_end Latest UTC time you are interested in.
//! @return A \ref HealthServiceAccessibilityMask representing the accessible metrics
//! in this time range.
HealthServiceAccessibilityMask health_service_metric_accessible(
    HealthMetric metric, time_t time_start, time_t time_end);

//! Check if a certain combination of metric, time span, and scope is accessible for calculating
//! summed, averaged data by returning a value of \ref HealthServiceAccessibilityMask. Developers
//! should check if the return value is \ref HealthServiceAccessibilityMaskAvailable before calling
//! \ref health_service_sum_averaged.
//!
//! Note that this call is the same as calling
//! `health_service_metric_aggregate_averaged_accessible(metric, time_start, time_end,
//!  HealthAggregationSum, HealthServiceTimeScopeOnce)`
//!
//! @param metric The metric to query for averaged data.
//! @param time_start Earliest UTC time you are interested in.
//! @param time_end Latest UTC time you are interested in.
//! @param scope \ref HealthServiceTimeScope value describing how the average should be computed.
//! @return A \HealthServiceAccessibilityMask value describing whether averaged data is available.
HealthServiceAccessibilityMask health_service_metric_averaged_accessible(
    HealthMetric metric, time_t time_start, time_t time_end, HealthServiceTimeScope scope);

//! Check if a certain combination of metric, time span, aggregation operation, and scope is
//! accessible for calculating aggregated, averaged data by returning a value of
//! \ref HealthServiceAccessibilityMask. Developers should check if the return value is
//! \ref HealthServiceAccessibilityMaskAvailable before calling
//! \ref health_service_aggregate_averaged.
//! @param metric The metric to query for averaged data.
//! @param time_start Earliest UTC time you are interested in.
//! @param time_end Latest UTC time you are interested in.
//! @param aggregation The aggregation to perform
//! @param scope \ref HealthServiceTimeScope value describing how the average should be computed.
//! @return A \HealthServiceAccessibilityMask value describing whether averaged data is available.
HealthServiceAccessibilityMask health_service_metric_aggregate_averaged_accessible(
    HealthMetric metric, time_t time_start, time_t time_end, HealthAggregation aggregation,
    HealthServiceTimeScope scope);

//! Check if a certain combination of metric, \ref HealthActivityMask and time span is
//! accessible. Developers should check if the return value is
//! \ref HealthServiceAccessibilityMaskAvailable before calling any other HealthService APIs that
//! involve the given activities.
//! @param activity_mask A bitmask of activities you are interested in.
//! @param time_start Earliest UTC time you are interested in.
//! @param time_end Latest UTC time you are interested in.
//! @return A \ref HealthServiceAccessibilityMask representing which of the
//! passed \ref HealthActivityMask values are available under the given constraints.
HealthServiceAccessibilityMask health_service_any_activity_accessible(
    HealthActivityMask activity_mask, time_t time_start, time_t time_end);

//! Health event enum. Passed into the \ref HealthEventHandler.
typedef enum {
  //! All data is considered as outdated and apps should re-read all health data.
  //! This happens after an app is subscribed via \ref health_service_events_subscribe(),
  //! on a change of the day, or in other cases that significantly change the underlying data.
  HealthEventSignificantUpdate = 0,
  //! Recent values around \ref HealthMetricStepCount, \ref HealthMetricActiveSeconds,
  //! or \ref HealthMetricWalkedDistanceMeters have changed.
  HealthEventMovementUpdate = 1,
  //! Recent values around \ref HealthMetricSleepSeconds, \ref HealthMetricSleepRestfulSeconds,
  //! \ref HealthActivitySleep, and \ref HealthActivityRestfulSleep changed.
  HealthEventSleepUpdate = 2,
  //! A metric has crossed the threshold set by \ref health_service_register_metric_alert.
  HealthEventMetricAlert = 3,
  //! Value of \ref HealthMetricHeartRateBPM or \ref HealthMetricHeartRateRawBPM has changed.
  HealthEventHeartRateUpdate = 4,
} HealthEventType;

//! Developer-supplied event handler, called when a health-related event occurs after subscribing
//! via \ref health_service_events_subscribe();
//! @param event The type of health-related event that occurred.
//! @param context The developer-supplied context pointer.
typedef void (*HealthEventHandler)(HealthEventType event, void *context);

//! Subscribe to HealthService events. This allocates a cache on the application's heap of up
//! to 2048 bytes that will be de-allocated if you call \ref health_service_events_unsubscribe().
//! If there's not enough heap available, this function will return `false` and will not
//! subscribe to any events.
//! @param handler Developer-supplied event handler function.
//! @param context Developer-supplied context pointer.
//! @return `true` on success, `false` on failure.
bool health_service_events_subscribe(HealthEventHandler handler, void *context);

//! Unsubscribe from HealthService events.
//! @return `true` on success, `false` on failure.
bool health_service_events_unsubscribe(void);

//! Set the desired sampling period for heart rate readings. Normally, the system will sample the
//! heart rate using a sampling period that is automatically chosen to provide useful information
//! without undue battery drain (it automatically samples more often during periods of intense
//! activity, and less often when the user is idle). If desired though, an application can request a
//! specific sampling period using this call. The system will use this as a suggestion, but does not
//! guarantee that the requested period will be used. The actual sampling period may be greater or
//! less due to system needs or heart rate sensor reading quality issues.
//!
//! Each time a new heart rate reading becomes available, a `HealthEventHeartRateUpdate` event will
//! be sent to the application's `HealthEventHandler`. The sample period request will remain in
//! effect the entire time the app is running unless it is explicitly cancelled (by calling this
//! method again with 0 as the desired interval). If the app exits without first cancelling the
//! request, it will remain in effect even for a limited time afterwards. To determine how long it
//! will remain active after the app exits, use
//! `health_service_get_heart_rate_sample_period_expiration_sec`.
//!
//! Unless the app explicitly needs to access to historical high-resolution heart rate data, it is
//! best practice to always cancel the sample period request before exiting in order to maximize
//! battery life. Historical heart rate data can be accessed using the
//! `health_service_get_minute_history` call.
//! @param interval_sec desired interval between heart rate reading updates. Pass 0 to
//!   go back to automatically chosen intervals.
//! @return `true` on success, `false` on failure
bool health_service_set_heart_rate_sample_period(uint16_t interval_sec);

//! Return how long a heart rate sample period request (sent via
//! `health_service_set_heart_rate_sample_period`) will remain active after the app exits. If
//! there is no current request by this app, this call will return 0.
//! @return The number of seconds the heart rate sample period request will remain active after
//! the app exits, or 0 if there is no active request by this app.
uint16_t health_service_get_heart_rate_sample_period_expiration_sec(void);

//! Register for an alert when a metric crosses the given threshold. When the metric crosses this
//! threshold (either goes above or below it), a \ref HealthEventMetricAlert event will be
//! generated. To cancel this registration, pass the returned \ref HealthMetricAlert value to
//! \ref health_service_cancel_metric_alert. The only metric currently supported by this call is
//! \ref HealthMetricHeartRateBPM, but future versions may support additional metrics. To see if a
//! specific metric is supported by this call, use:
//! \code{.c}
//! time_t now = time(NULL);
//! HealthServiceAccessibilityMask accessible =
//!     health_service_metric_aggregate_averaged_accessible(metric, now, now, HealthAggregationAvg,
//!     HealthServiceTimeScopeOnce);
//! bool alert_supported = (accessible & HealthServiceAccessibilityMaskAvailable);
//! \endcode
//!
//! In the current implementation, only one alert per metric can be registered at a time. Future
//! implementations may support two or more simulataneous alert registrations per metric. To change
//! the alert threshold in the current implementation, cancel the original registration
//! using `health_service_cancel_metric_alert` before registering the new threshold.
//! @param metric Which \ref HealthMetric to query.
//! @param threshold The threshold value.
//! @return handle to the alert registration on success, NULL on failure
HealthMetricAlert *health_service_register_metric_alert(HealthMetric metric, HealthValue threshold);

//! Cancel an metric alert previously created with \ref health_service_register_metric_alert.
//! @param alert the \ref HealthMetricAlert previously returned by
//!  \ref health_service_register_metric_alert
//! @return `true` on success, `false` on failure
bool health_service_cancel_metric_alert(HealthMetricAlert *alert);

//! Structure representing a single minute data record returned
//! by \ref health_service_get_minute_history().
//! The `orientation` field encodes the angle of the watch in the x-y plane (the "yaw") in the
//! lower 4 bits (360 degrees linearly mapped to 1 of 16 different values) and the angle to the
//! z axis (the "pitch") in the upper 4 bits.
//! The `vmc` value is a measure of the total amount of movement seen by the watch. More vigorous
//! movement yields higher VMC values.
typedef struct {
  uint8_t steps;              //!< Number of steps taken in this minute.
  uint8_t orientation;        //!< Quantized average orientation.
  uint16_t vmc;               //!< Vector Magnitude Counts (vmc).
  bool is_invalid: 1;         //!< `true` if the item doesn't represents actual data
                              //!< and should be ignored.
  AmbientLightLevel light: 3; //!< Instantaneous light level during this minute.
  uint8_t padding: 4;
  uint8_t heart_rate_bpm;     //!< heart rate in beats per minute
  uint8_t reserved[6];        //!< Reserved for future use.
} HealthMinuteData;

//! Return historical minute data records. This fills in the `minute_data` array parameter with
//! minute by minute statistics of the user's steps, average watch orientation, etc. The data is
//! returned in time order, with the oldest minute data returned at `minute_data[0]`.
//! @param minute_data Pointer to an array of \ref HealthMinuteData records that will be filled
//!      in with the historical minute data.
//! @param max_records The maximum number of records the `minute_data` array can hold.
//! @param[in,out] time_start On entry, the UTC time of the first requested record. On exit,
//!      the UTC time of the first second of the first record actually returned.
//!      If `time_start` on entry is somewhere in the middle of a minute interval, this function
//!      behaves as if the caller passed in the start of that minute.
//! @param[in,out] time_end On entry, the UTC time of the end of the requested range of records. On
//!      exit, the UTC time of the end of the last record actually returned (i.e. start time of last
//!      record + 60). If `time_end` on entry is somewhere in the middle of a minute interval, this
//!      function behaves as if the caller passed in the end of that minute.
//! @return Actual number of records returned. May be less then the maximum requested.
//! @note If the return value is zero, `time_start` and `time_end` are meaningless.
//!      It's not guaranteed that all records contain valid data, even if the return value is
//!      greater than zero. Check `HealthMinuteData.is_invalid` to see if a given record contains
//!      valid data.
uint32_t health_service_get_minute_history(HealthMinuteData *minute_data, uint32_t max_records,
                                           time_t *time_start, time_t *time_end);

//! Types of measurement system a \ref HealthMetric may be measured in.
typedef enum {
  //! The measurement system is unknown, or does not apply to the chosen metric.
  MeasurementSystemUnknown,
  //! The metric measurement system.
  MeasurementSystemMetric,
  //! The imperial measurement system.
  MeasurementSystemImperial,
} MeasurementSystem;

//! Get the preferred measurement system for a given \ref HealthMetric, if the user has chosen
//! a preferred system and it is applicable to that metric.
//! @param metric A metric value chosen from \ref HealthMetric.
//! @return A value from \ref MeasurementSystem if applicable, else \ref MeasurementSystemUnknown.
MeasurementSystem health_service_get_measurement_system_for_display(HealthMetric metric);

// -------------------------------------------------------------------------------------------------
// The following types are internally used to implement the cache for the health service.
// They are declared here and not in health_service_private.h to avoid a cyclic dependency between
// events.h and their declaration.

//! @internal
// Auxiliary data passed into HealthEventHandler along with the health event type.
typedef struct {
  uint32_t steps;                           //!< Total number of steps for today
} HealthEventMovementUpdateData;

//! @internal
typedef struct {
  uint32_t total_seconds;                   //!< Total number of seconds of sleep for today
  uint32_t total_restful_seconds;           //!< Total number of restful seconds
} HealthEventSleepUpdateData;

//! @internal
typedef struct {
  uint16_t day_id;                          //!< The new day_id for today
} HealthEventSignificantUpdateData;

//! @internal
typedef struct {
  uint8_t current_bpm;
  uint8_t resting_bpm;
  HRMQuality quality:8;
  bool is_filtered;
} HealthEventHeartRateUpdateData;

//! @internal
typedef struct  {
  union {
    HealthEventMovementUpdateData movement_update;
    HealthEventSleepUpdateData sleep_update;
    HealthEventSignificantUpdateData significant_update;
    HealthEventHeartRateUpdateData heart_rate_update;
  };
} HealthEventData;

//!     @} // end addtogroup HealthService
//!   @} // end addtogroup EventService
//! @} // end addtogroup Foundation
