#include "app_message.h"

#include <pebble.h>

/* AppMessage's Dictionary has no float tuple type, so latitude/
 * longitude/utc_offset_hours (all doubles in BirthData) cross the wire
 * as scaled int32s - microdegrees for lat/lon (~0.11m precision, far
 * finer than this app's astrology needs), minutes for the UTC offset
 * (covers every real-world offset, including the half/quarter-hour
 * ones) - see config.c's own persisted representation, which uses the
 * same scaling so no conversion happens twice. */

static ConfigUpdatedCallback s_callback;

static bool read_int(DictionaryIterator *iter, uint32_t key, int32_t *out) {
  Tuple *t = dict_find(iter, key);
  if (!t) return false;
  *out = t->value->int32;
  return true;
}

static void inbox_received_handler(DictionaryIterator *iter, void *context) {
  WatchConfig cfg;
  config_load(&cfg); /* start from existing settings - a field the page
                       * doesn't send (e.g. it only changed the language)
                       * keeps its previous value rather than resetting. */

  int32_t v;
  bool got_birth = false;
  if (read_int(iter, MESSAGE_KEY_BIRTH_YEAR, &v)) { cfg.birth.year = v; got_birth = true; }
  if (read_int(iter, MESSAGE_KEY_BIRTH_MONTH, &v)) { cfg.birth.month = v; got_birth = true; }
  if (read_int(iter, MESSAGE_KEY_BIRTH_DAY, &v)) { cfg.birth.day = v; got_birth = true; }
  if (read_int(iter, MESSAGE_KEY_BIRTH_HOUR, &v)) { cfg.birth.hour = v; got_birth = true; }
  if (read_int(iter, MESSAGE_KEY_BIRTH_MINUTE, &v)) { cfg.birth.minute = v; got_birth = true; }
  if (read_int(iter, MESSAGE_KEY_BIRTH_UTC_OFFSET_MINUTES, &v)) {
    cfg.birth.utc_offset_hours = v / 60.0;
    got_birth = true;
  }
  if (read_int(iter, MESSAGE_KEY_BIRTH_LAT_MICRODEG, &v)) {
    cfg.birth.latitude = v / 1000000.0;
    got_birth = true;
  }
  if (read_int(iter, MESSAGE_KEY_BIRTH_LON_MICRODEG, &v)) {
    cfg.birth.longitude = v / 1000000.0;
    got_birth = true;
  }
  if (read_int(iter, MESSAGE_KEY_GENDER, &v)) cfg.gender = (Gender)v;

  Tuple *lang_tuple = dict_find(iter, MESSAGE_KEY_LANG);
  if (lang_tuple) {
    strncpy(cfg.lang, lang_tuple->value->cstring, sizeof(cfg.lang) - 1);
    cfg.lang[sizeof(cfg.lang) - 1] = '\0';
  }

  if (read_int(iter, MESSAGE_KEY_NOTIFY_ENABLED, &v)) cfg.notify_enabled = (v != 0);
  if (read_int(iter, MESSAGE_KEY_NOTIFY_HOUR, &v)) cfg.notify_hour = v;
  if (read_int(iter, MESSAGE_KEY_NOTIFY_MINUTE, &v)) cfg.notify_minute = v;

  /* Birth data is the one thing that makes a config submission "complete" -
   * language/notification-only re-submissions (e.g. from a later re-open
   * of the settings page) must not un-configure an already-set-up watch. */
  if (got_birth) cfg.configured = true;

  config_log(&cfg); /* confirms what was actually received/parsed */
  config_save(&cfg);
  if (s_callback) s_callback(&cfg);
}

static void inbox_dropped_handler(AppMessageResult reason, void *context) {
  APP_LOG(APP_LOG_LEVEL_ERROR, "AppMessage dropped: reason %d", (int)reason);
}

/* app_message_*_size_maximum() return the largest buffer the platform
 * *could* hand out (8200 bytes each) - not a size to actually request.
 * The real payload is ~150-200 bytes; 256 bytes each leaves headroom
 * without eating into this app's ~12KB heap budget (see watch/README.md). */
#define INBOX_SIZE 256
#define OUTBOX_SIZE 256

void app_message_init(ConfigUpdatedCallback on_config_updated) {
  s_callback = on_config_updated;
  app_message_register_inbox_received(inbox_received_handler);
  app_message_register_inbox_dropped(inbox_dropped_handler);
  app_message_open(INBOX_SIZE, OUTBOX_SIZE);
}
