// DOS time/date conversion utilities for ZIP files
// DOS time format: 16-bit time and 16-bit date fields

// DOS time structure (packed into 16 bits)
// Bits 15-11: Hours (0-23)
// Bits 10-5:  Minutes (0-59) 
// Bits 4-0:   Seconds/2 (0-29, representing 0-58 seconds in 2-second intervals)

// DOS date structure (packed into 16 bits)  
// Bits 15-9: Year offset from 1980 (0-127, representing 1980-2107)
// Bits 8-5:  Month (1-12)
// Bits 4-0:  Day (1-31)

// Unix timestamp to DOS date/time conversion

///|
pub fn unix_to_dos_datetime(unix_timestamp : Int) -> (Int, Int) {
  // Convert Unix timestamp to components
  // This is a simplified conversion - a full implementation would handle
  // leap years, time zones, etc. more accurately

  if unix_timestamp <= dos_epoch {
    // Before DOS epoch, use minimum DOS date/time (1980-01-01 00:00:00)
    return (0, 0x0021) // Time: 00:00:00, Date: 1980-01-01
  }
  let seconds_since_dos_epoch = unix_timestamp - dos_epoch
  let days_since_dos_epoch = seconds_since_dos_epoch / 86400
  let seconds_in_day = seconds_since_dos_epoch % 86400

  // Calculate time components
  let hours = seconds_in_day / 3600
  let minutes = seconds_in_day % 3600 / 60
  let seconds = seconds_in_day % 60

  // Pack DOS time (seconds are divided by 2)
  let dos_time = (hours << 11) | (minutes << 5) | (seconds / 2)

  // Calculate date components (simplified - doesn't handle leap years perfectly)
  let mut year = 1980
  let mut remaining_days = days_since_dos_epoch

  // Approximate year calculation
  while remaining_days >= 365 {
    let days_in_year = if is_leap_year(year) { 366 } else { 365 }
    if remaining_days >= days_in_year {
      remaining_days = remaining_days - days_in_year
      year = year + 1
    } else {
      break
    }
  }

  // Calculate month and day
  let (month, day) = day_of_year_to_month_day(
    remaining_days + 1,
    is_leap_year(year),
  )

  // Pack DOS date
  let year_offset = year - 1980
  let dos_date = (year_offset << 9) | (month << 5) | day
  (dos_time, dos_date)
}

// DOS date/time to Unix timestamp conversion

///|
pub fn dos_datetime_to_unix(dos_time : Int, dos_date : Int) -> Int {
  // Unpack DOS time
  let hours = (dos_time >> 11) & 0x1f
  let minutes = (dos_time >> 5) & 0x3f
  let seconds = (dos_time & 0x1f) * 2

  // Unpack DOS date
  let year_offset = (dos_date >> 9) & 0x7f
  let month = (dos_date >> 5) & 0x0f
  let day = dos_date & 0x1f
  let year = 1980 + year_offset

  // Validate components
  if month < 1 ||
    month > 12 ||
    day < 1 ||
    day > 31 ||
    hours > 23 ||
    minutes > 59 ||
    seconds > 59 {
    return dos_epoch // Return DOS epoch for invalid dates
  }

  // Calculate days since DOS epoch
  let mut days = 0

  // Add days for complete years
  for y = 1980; y < year; y = y + 1 {
    days = days + (if is_leap_year(y) { 366 } else { 365 })
  }

  // Add days for complete months in the current year
  days = days + month_day_to_day_of_year(month, day, is_leap_year(year)) - 1

  // Calculate total seconds
  dos_epoch + days * 86400 + hours * 3600 + minutes * 60 + seconds
}

// Check if a year is a leap year

///|
fn is_leap_year(year : Int) -> Bool {
  (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}

// Convert day of year to month and day

///|
fn day_of_year_to_month_day(day_of_year : Int, is_leap : Bool) -> (Int, Int) {
  let days_in_months = if is_leap {
    [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  } else {
    [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  }
  let mut remaining_days = day_of_year
  let mut month = 1
  for i = 0; i < days_in_months.length(); i = i + 1 {
    if remaining_days <= days_in_months[i] {
      return (month, remaining_days)
    }
    remaining_days = remaining_days - days_in_months[i]
    month = month + 1
  }

  // Fallback for invalid day of year
  (12, 31)
}

// Convert month and day to day of year

///|
fn month_day_to_day_of_year(month : Int, day : Int, is_leap : Bool) -> Int {
  let days_in_months = if is_leap {
    [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  } else {
    [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  }
  let mut day_of_year = day
  for i = 0; i < month - 1 && i < days_in_months.length(); i = i + 1 {
    day_of_year = day_of_year + days_in_months[i]
  }
  day_of_year
}

// Get current Unix timestamp (placeholder - would need system integration)

///|
pub fn current_unix_timestamp() -> Int {
  // This is a placeholder. In a real implementation, this would
  // get the current system time. For now, return a reasonable default.
  1640995200 // 2022-01-01 00:00:00 UTC
}

// Create DOS time/date from individual components

///|
pub fn make_dos_datetime(
  year : Int,
  month : Int,
  day : Int,
  hour : Int,
  minute : Int,
  second : Int,
) -> (Int, Int) {
  // Validate and clamp inputs
  let year_clamped = if year < 1980 {
    1980
  } else if year > 2107 {
    2107
  } else {
    year
  }
  let month_clamped = if month < 1 {
    1
  } else if month > 12 {
    12
  } else {
    month
  }
  let day_clamped = if day < 1 { 1 } else if day > 31 { 31 } else { day }
  let hour_clamped = if hour < 0 { 0 } else if hour > 23 { 23 } else { hour }
  let minute_clamped = if minute < 0 {
    0
  } else if minute > 59 {
    59
  } else {
    minute
  }
  let second_clamped = if second < 0 {
    0
  } else if second > 59 {
    59
  } else {
    second
  }

  // Pack DOS time
  let dos_time = (hour_clamped << 11) |
    (minute_clamped << 5) |
    (second_clamped / 2)

  // Pack DOS date
  let year_offset = year_clamped - 1980
  let dos_date = (year_offset << 9) | (month_clamped << 5) | day_clamped
  (dos_time, dos_date)
}

// Extract individual components from DOS time/date

///|
pub fn dos_datetime_components(
  dos_time : Int,
  dos_date : Int,
) -> (Int, Int, Int, Int, Int, Int) {
  // Unpack DOS time
  let hours = (dos_time >> 11) & 0x1f
  let minutes = (dos_time >> 5) & 0x3f
  let seconds = (dos_time & 0x1f) * 2

  // Unpack DOS date
  let year_offset = (dos_date >> 9) & 0x7f
  let month = (dos_date >> 5) & 0x0f
  let day = dos_date & 0x1f
  let year = 1980 + year_offset
  (year, month, day, hours, minutes, seconds)
}

// Format DOS date/time as human-readable string

///|
pub fn format_dos_datetime(dos_time : Int, dos_date : Int) -> String {
  let (year, month, day, hours, minutes, seconds) = dos_datetime_components(
    dos_time, dos_date,
  )

  // Format as YYYY-MM-DD HH:MM:SS
  let year_str = year.to_string()
  let month_str = if month < 10 {
    "0" + month.to_string()
  } else {
    month.to_string()
  }
  let day_str = if day < 10 { "0" + day.to_string() } else { day.to_string() }
  let hour_str = if hours < 10 {
    "0" + hours.to_string()
  } else {
    hours.to_string()
  }
  let minute_str = if minutes < 10 {
    "0" + minutes.to_string()
  } else {
    minutes.to_string()
  }
  let second_str = if seconds < 10 {
    "0" + seconds.to_string()
  } else {
    seconds.to_string()
  }
  year_str +
  "-" +
  month_str +
  "-" +
  day_str +
  " " +
  hour_str +
  ":" +
  minute_str +
  ":" +
  second_str
}