import network
import ntptime
import time
import socket
import machine
from dht20 import DHT20
from bme280 import BME280

# ========== USER CONFIGURATION ==========
WIFI_SSID = "SSID"
WIFI_PASSWORD = "PASS"

# I2C pins for both sensors
I2C_SDA_PIN = 4   # GP4 = physical pin 6
I2C_SCL_PIN = 5   # GP5 = physical pin 7

# CSV file name and maximum number of data rows (excluding header)
CSV_FILE = "log.csv"
MAX_ENTRIES = 10000

# Web server port
HTTP_PORT = 80

# NTP settings
NTP_RETRY_INTERVAL = 3600        # 1 hour – retry if sync fails
NTP_SYNC_INTERVAL = 86400        # 24 hours – re-sync after successful sync
# ========================================

# ---------- I2C Setup ----------
i2c = machine.I2C(0, sda=machine.Pin(I2C_SDA_PIN), scl=machine.Pin(I2C_SCL_PIN), freq=400000)

# ---------- Sensor Initialisation ----------
dht20 = DHT20(0x38, i2c)
bme280 = BME280(i2c, addr=0x76)   # use 0x77 if your sensor has that address

# ---------- Time helpers (UTC only) ----------
def utc_time_tuple():
    return time.gmtime()

# ---------- Wi‑Fi connection ----------
def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print("Connecting to Wi-Fi...")
        wlan.connect(WIFI_SSID, WIFI_PASSWORD)
        while not wlan.isconnected():
            time.sleep(1)
    print("Wi-Fi connected:", wlan.ifconfig())

# ---------- Time synchronisation ----------
last_ntp_attempt = 0
last_ntp_success = 0

def sync_time(force=False):
    global last_ntp_attempt, last_ntp_success
    if not force:
        if time.time() - last_ntp_attempt < NTP_RETRY_INTERVAL:
            return False
    last_ntp_attempt = time.time()
    try:
        ntptime.settime()
        print("NTP sync successful. RTC updated to UTC:", time.gmtime())
        last_ntp_success = time.time()
        return True
    except Exception as e:
        print("NTP sync failed:", e)
        return False

def maintain_time():
    global last_ntp_success
    if last_ntp_success == 0:
        sync_time()
    else:
        if time.time() - last_ntp_success >= NTP_SYNC_INTERVAL:
            sync_time(force=True)

# ---------- CSV file management ----------
def ensure_csv_header():
    try:
        with open(CSV_FILE, 'r') as f:
            pass
    except OSError:
        with open(CSV_FILE, 'w') as f:
            f.write("datetime_utc,temperature_c,humidity_pct,pressure_hPa\n")

def append_csv_row(dt_str, temp_c, hum_pct, pressure_hpa):
    ensure_csv_header()
    with open(CSV_FILE, 'r') as f:
        lines = f.readlines()
    data_rows = lines[1:]
    if len(data_rows) >= MAX_ENTRIES:
        data_rows.pop(0)
    new_row = f"{dt_str},{temp_c:.2f},{hum_pct:.2f},{pressure_hpa:.2f}\n"
    data_rows.append(new_row)
    with open(CSV_FILE, 'w') as f:
        f.write(lines[0])          # header
        for row in data_rows:
            f.write(row)

# ---------- Sensor reading ----------
def read_sensors():
    """
    Returns (temperature, humidity, pressure)
    If a sensor fails, its value is None.
    """
    temp_dht = None
    hum = None
    pressure = None

    # Read DHT20
    try:
        meas = dht20.measurements
        if meas['crc_ok']:
            temp_dht = meas['t']
            hum = meas['rh']
        else:
            print("DHT20 CRC error")
    except Exception as e:
        print("DHT20 read error:", e)

    # Read BME280
    try:
        bme_temp, bme_press = bme280.read()
        # Note: we ignore BME280 temperature, only use pressure
        pressure = bme_press
    except Exception as e:
        print("BME280 read error:", e)

    return temp_dht, hum, pressure

# ---------- Web server ----------
def start_web_server():
    addr = socket.getaddrinfo('0.0.0.0', HTTP_PORT)[0][-1]
    server = socket.socket()
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(addr)
    server.listen(1)
    server.settimeout(1)
    return server

def handle_client(server):
    try:
        cl, addr = server.accept()
        print("Client from", addr)
        request = cl.recv(1024)
        try:
            with open(CSV_FILE, 'rb') as f:
                csv_data = f.read()
            response = (
                "HTTP/1.1 200 OK\r\n"
                "Content-Type: text/csv\r\n"
                f"Content-Disposition: attachment; filename={CSV_FILE}\r\n"
                "Connection: close\r\n\r\n"
            ).encode() + csv_data
            cl.send(response)
        except Exception:
            cl.send(b"HTTP/1.1 404 Not Found\r\n\r\n")
        cl.close()
    except OSError:
        pass   # timeout

# ---------- Logging helper ----------
def log_now():
    """Take readings and log them with current UTC time."""
    temp, hum, pressure = read_sensors()
    if temp is not None and hum is not None and pressure is not None:
        t = utc_time_tuple()
        year, month, day, hour, minute, second = t[0:6]
        dt_str = f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}"
        append_csv_row(dt_str, temp, hum, pressure)
        print(f"Logged (UTC): {dt_str} T={temp:.2f}°C H={hum:.2f}% P={pressure:.2f}hPa")
        return (year, month, day, hour)   # return hour identifier
    else:
        print("Sensor read failed, no data logged.")
        return None

# ---------- Main loop ----------
def main():
    connect_wifi()
    maintain_time()   # initial sync
    server = start_web_server()

    last_logged_hour = None

    while True:
        handle_client(server)
        maintain_time()

        now = utc_time_tuple()
        current_hour = (now[0], now[1], now[2], now[3])   # year, month, day, hour

        if current_hour != last_logged_hour:
            # Optionally, check if we are still in the same hour after sensor reading
            temp, hum, pressure = read_sensors()
            if temp is not None and hum is not None and pressure is not None:
                dt_str = f"{now[0]:04d}-{now[1]:02d}-{now[2]:02d} {now[3]:02d}:{now[4]:02d}:{now[5]:02d}"
                append_csv_row(dt_str, temp, hum, pressure)
                print(f"Logged (UTC): {dt_str} T={temp:.2f}°C H={hum:.2f}% P={pressure:.2f}hPa")
            else:
                print(f"Sensor failed at {now[3]}:00 – data not logged")
            last_logged_hour = current_hour

        time.sleep(0.5)

if __name__ == "__main__":
    main()
