#!/usr/bin/python3
"""
  ____ _____ ____ 
 |  _ \_   _/ ___|
 | |_) || || |    
 |  _ < | || |___ 
 |_| \_\|_| \____|

Script to set the RTC using an NTP server (script used by web UI)
RPI needs to be connected to the internet (WIFI).
Requires ntplib and pytz:
sudo pip3 install ntplib pytz --break-system-packages
"""
import smbus2
import time
from datetime import datetime
import ntplib
import pytz  # For timezone handling

# DS3231 I2C address
DS3231_ADDR = 0x68

# Registers for DS3231
REG_TIME = 0x00

def bcd_to_dec(bcd):
    """Convert BCD to decimal."""
    return (bcd // 16 * 10) + (bcd % 16)

def dec_to_bcd(dec):
    """Convert decimal to BCD."""
    return (dec // 10 * 16) + (dec % 10)

def set_time(bus, year, month, day, hour, minute, second):
    """Set the RTC time."""
    # Convert the time to BCD format
    second_bcd = dec_to_bcd(second)
    minute_bcd = dec_to_bcd(minute)
    hour_bcd = dec_to_bcd(hour)
    day_bcd = dec_to_bcd(day)
    month_bcd = dec_to_bcd(month)
    year_bcd = dec_to_bcd(year - 2000)  # DS3231 uses year from 2000

    # Write time to DS3231
    bus.write_i2c_block_data(DS3231_ADDR, REG_TIME, [
        second_bcd,
        minute_bcd,
        hour_bcd,
        0x01,  # Day of the week (1=Monday, etc.)
        day_bcd,
        month_bcd,
        year_bcd
    ])

def read_time(bus):
    """Read the RTC time and validate the values."""
    try:
        data = bus.read_i2c_block_data(DS3231_ADDR, REG_TIME, 7)
        
        # Convert from BCD
        second = bcd_to_dec(data[0] & 0x7F)
        minute = bcd_to_dec(data[1])
        hour = bcd_to_dec(data[2] & 0x3F)
        day = bcd_to_dec(data[4])
        month = bcd_to_dec(data[5])
        year = bcd_to_dec(data[6]) + 2000
        
        # Print raw values for debugging
        print(f"Raw RTC values: {data}")
        print(f"Decoded values: Y:{year} M:{month} D:{day} H:{hour} M:{minute} S:{second}")
        
        # Validate date values
        if not (1 <= month <= 12):
            print(f"Invalid month value: {month}, using default")
            month = 1
            
        # Check days in month (simplified)
        days_in_month = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        if not (1 <= day <= days_in_month[month]):
            print(f"Invalid day value: {day} for month {month}, using default")
            day = 1
            
        # Validate time values
        if not (0 <= hour <= 23):
            print(f"Invalid hour value: {hour}, using default")
            hour = 0
            
        if not (0 <= minute <= 59):
            print(f"Invalid minute value: {minute}, using default")
            minute = 0
            
        if not (0 <= second <= 59):
            print(f"Invalid second value: {second}, using default")
            second = 0
            
        return (year, month, day, hour, minute, second)
        
    except Exception as e:
        print(f"Error reading RTC: {e}")
        # Return a safe default date (2023-01-01 00:00:00)
        return (2023, 1, 1, 0, 0, 0)

def get_internet_time():
    """Get the current time from an NTP server."""
    ntp_client = ntplib.NTPClient()
    # Try multiple NTP servers in case one fails
    servers = ['pool.ntp.org', 'time.google.com', 'time.windows.com', 'time.apple.com']
    
    for server in servers:
        try:
            print(f"Trying NTP server: {server}")
            response = ntp_client.request(server, timeout=2)
            utc_time = datetime.utcfromtimestamp(response.tx_time)
            print(f"Successfully got time from {server}")
            return utc_time
        except Exception as e:
            print(f"Failed to get time from {server}: {e}")
    
    # If all servers fail, raise exception
    raise Exception("All NTP servers failed")

def main():
    try:
        bus = smbus2.SMBus(1)
        
        # Test if RTC is accessible
        try:
            bus.read_byte(DS3231_ADDR)
            print("RTC module is accessible")
        except Exception as e:
            print(f"Error accessing RTC module: {e}")
            print("Please check connections and I2C configuration")
            return

        # Get the current time from the RTC
        try:
            year, month, day, hours, minutes, seconds = read_time(bus)
            # Create datetime object with validation to handle invalid dates
            rtc_time = datetime(year, month, day, hours, minutes, seconds)
            print(f"Actual RTC Time              : {rtc_time.strftime('%Y-%m-%d %H:%M:%S')}")
        except ValueError as e:
            print(f"Invalid date/time read from RTC: {e}")
            print("Will proceed with setting RTC from internet time")
            rtc_time = None

        # Get current UTC time from an NTP server
        try:
            internet_utc_time = get_internet_time()
            print(f"Time from Internet (UTC)     : {internet_utc_time.strftime('%Y-%m-%d %H:%M:%S')}")
        except Exception as e:
            print(f"Error retrieving time from the internet: {e}")
            if rtc_time is None:
                print("Cannot proceed without either valid RTC time or internet time")
                return
            print("Will keep current RTC time")
            return

        # Set the RTC to UTC time
        print("Setting RTC to internet time...")
        set_time(bus, internet_utc_time.year, internet_utc_time.month, internet_utc_time.day, 
                internet_utc_time.hour, internet_utc_time.minute, internet_utc_time.second)

        # Read and print the new time from RTC
        print("Reading back new RTC time...")
        year, month, day, hour, minute, second = read_time(bus)
        rtc_time_new = datetime(year, month, day, hour, minute, second)
        print(f"New RTC Time (UTC)           : {rtc_time_new.strftime('%Y-%m-%d %H:%M:%S')}")
        
        # Calculate difference to verify accuracy
        time_diff = abs((rtc_time_new - internet_utc_time).total_seconds())
        print(f"Time difference             : {time_diff:.2f} seconds")
        
        if time_diff > 5:
            print("Warning: RTC time differs significantly from internet time")
            print("You may need to retry or check RTC module")
        else:
            print("RTC successfully synchronized with internet time")
            
    except Exception as e:
        print(f"Unexpected error: {e}")

if __name__ == "__main__":
    main()