/*
 * 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.
 */

#include "date_time_selection_window_private.h"

#include "services/common/clock.h"
#include "services/common/i18n/i18n.h"
#include "util/date.h"
#include "util/math.h"

#include <stdio.h>

static const int MIN_SELECTABLE_YEAR = 2010;
static const int MAX_SELECTABLE_YEAR = 2037;  // Work around Y2038 problem

static int prv_wrap(int x, int max, int delta) {
  x = (x + delta) % max;
  return x < 0 ? x + max : x;
}

int date_time_selection_step_hour(int hour, int delta) {
  return prv_wrap(hour, 24, delta);
}

int date_time_selection_step_minute(int minute, int delta) {
  return prv_wrap(minute, 60, delta);
}

int date_time_selection_step_day(int year, int month, int day, int delta) {
  bool is_leap_year = date_util_is_leap_year(year);
  // This function expects Jan == 0, but date_util_get_max_days_in_month expects Jan == 1
  int max_days = date_util_get_max_days_in_month(month + 1, is_leap_year);
  // This functions expects the first day of the month is 1, but wrap expects the first day of the
  // month is 0 (based off the mday element of the "tm" struct)
  return prv_wrap(day - 1, max_days, delta) + 1;
}

int date_time_selection_step_month(int month, int delta) {
  return prv_wrap(month, 12, delta);
}

int date_time_selection_truncate_date(int year, int month, int day) {
  bool is_leap_year = date_util_is_leap_year(year);

  // date_util_get_max_days_in_month expects Jan == 1, but this function expects Jan == 0
  int max_days = date_util_get_max_days_in_month(month + 1, is_leap_year);
  return MIN(day, max_days);
}

int date_time_selection_step_year(int year, int delta) {
  year += delta;
  return CLIP(year, MIN_SELECTABLE_YEAR - STDTIME_YEAR_OFFSET,
      MAX_SELECTABLE_YEAR - STDTIME_YEAR_OFFSET);
}

char *date_time_selection_get_text(TimeData *data, TimeInputIndex index, char *buf) {
  switch (index) {
    case TimeInputIndexHour: {
      unsigned hour = data->hour;
      if (!clock_is_24h_style()) {
        hour = hour % 12;
        if (hour == 0) {
          hour = 12;
        }
      }
      snprintf(buf, 3, "%02u", hour);
      return buf;
    }
    case TimeInputIndexMinute:
      snprintf(buf, 3, "%02u", data->minute);
      return buf;
    case TimeInputIndexAMPM: // We should only get this in 12h style
      if (data->hour < 12) {
        i18n_get_with_buffer("AM", buf, 3);
      } else {
        i18n_get_with_buffer("PM", buf, 3);
      }
      return buf;
    default:
      return "";
  }
}

void date_time_handle_time_change(TimeData *data, TimeInputIndex index, int delta) {
  switch (index) {
    case TimeInputIndexHour:
      data->hour = date_time_selection_step_hour(data->hour, delta);
      break;
    case TimeInputIndexMinute:
      data->minute = date_time_selection_step_minute(data->minute, delta);
      break;
    case TimeInputIndexAMPM: // We should only get this in 12h style
      data->hour = date_time_selection_step_hour(data->hour, 12 * delta);
      break;
  }
}
