"""
  _____ _   ___     _______    _    
 | ____| \ | \ \   / / ____|  / \   
 |  _| |  \| |\ \ / /|  _|   / _ \  
 | |___| |\  | \ V / | |___ / ___ \ 
 |_____|_| \_|  \_/  |_____/_/   \_\
                                    
Gather data from envea Sensors and store them to the SQlite table
Use the RTC time for the timestamp

This script is run by a service nebuleair-envea-data.service

/usr/bin/python3 /var/www/nebuleair_pro_4g/envea/read_value_v2.py -d

"""

import json
import serial
import time
import traceback
import sqlite3
from datetime import datetime
import sys

# Set DEBUG to True to enable debug prints, False to disable
DEBUG = False  # Change this to False to disable debug output

# You can also control debug via command line argument
if len(sys.argv) > 1 and sys.argv[1] in ['--debug', '-d']:
    DEBUG = True
elif len(sys.argv) > 1 and sys.argv[1] in ['--quiet', '-q']:
    DEBUG = False

def debug_print(message):
    """Print debug messages only if DEBUG is True"""
    if DEBUG:
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"[{timestamp}] {message}")

debug_print("=== ENVEA Sensor Reader Started ===")

# Connect to the SQLite database
try:
    conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
    cursor = conn.cursor()
except Exception as e:
    debug_print(f"✗ Failed to connect to database: {e}")
    sys.exit(1)

# GET RTC TIME from SQlite
try:
    cursor.execute("SELECT * FROM timestamp_table LIMIT 1")
    row = cursor.fetchone()  # Get the first (and only) row
    rtc_time_str = row[1]  # '2025-02-07 12:30:45'
except Exception as e:
    debug_print(f"✗ Failed to get RTC time: {e}")
    rtc_time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    debug_print(f"  Using system time instead: {rtc_time_str}")

# Fetch connected ENVEA sondes from SQLite config table
try:
    cursor.execute("SELECT port, name, coefficient FROM envea_sondes_table WHERE connected = 1")
    connected_envea_sondes = cursor.fetchall()  # List of tuples (port, name, coefficient)
    debug_print(f"✓ Found {len(connected_envea_sondes)} connected ENVEA sensors")
    for port, name, coefficient in connected_envea_sondes:
        debug_print(f"  - {name}: port={port}, coefficient={coefficient}")
except Exception as e:
    debug_print(f"✗ Failed to fetch connected sensors: {e}")
    connected_envea_sondes = []

serial_connections = {}

if connected_envea_sondes:
    debug_print("\n--- Opening Serial Connections ---")
    for port, name, coefficient in connected_envea_sondes:
        try:
            serial_connections[name] = serial.Serial(
                port=f'/dev/{port}',
                baudrate=9600,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE,
                bytesize=serial.EIGHTBITS,
                timeout=1
            )
            debug_print(f"✓ Opened serial port for {name} on /dev/{port}")
        except serial.SerialException as e:
            debug_print(f"✗ Error opening serial port for {name}: {e}")
else:
    debug_print("! No connected ENVEA sensors found in configuration")

# Initialize sensor data variables
global data_h2s, data_no2, data_o3, data_co, data_nh3, data_so2
data_h2s = 0
data_no2 = 0
data_o3 = 0
data_co = 0
data_nh3 = 0
data_so2 = 0

try:
    if connected_envea_sondes:
        debug_print("\n--- Reading Sensor Data ---")
        for port, name, coefficient in connected_envea_sondes:
            if name in serial_connections:
                serial_connection = serial_connections[name]
                try:
                    debug_print(f"Reading from {name}...")

                    calculated_value = None
                    max_retries = 3

                    for attempt in range(max_retries):
                        # Flush input buffer to clear any stale data
                        serial_connection.reset_input_buffer()

                        # Send command to sensor
                        command = b"\xFF\x02\x13\x30\x01\x02\x03\x04\x05\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x12\xAF\x88\x03"
                        serial_connection.write(command)
                        if attempt == 0:
                            debug_print(f"  → Sent command: {command.hex()}")

                        # Wait for sensor response
                        time.sleep(0.8)

                        # Read all available data from buffer
                        bytes_available = serial_connection.in_waiting
                        debug_print(f"  ← Attempt {attempt + 1}: {bytes_available} bytes available")

                        if bytes_available > 0:
                            data_envea = serial_connection.read(bytes_available)
                        else:
                            data_envea = serial_connection.read(32)

                        if len(data_envea) > 0:
                            debug_print(f"  ← Received {len(data_envea)} bytes: {data_envea.hex()}")

                        # Find frame start (0xFF 0x02) in received data
                        frame_start = -1
                        for i in range(len(data_envea) - 1):
                            if data_envea[i] == 0xFF and data_envea[i + 1] == 0x02:
                                frame_start = i
                                break

                        if frame_start >= 0:
                            frame_data = data_envea[frame_start:]
                            if len(frame_data) >= 20:
                                byte_20 = frame_data[19]
                                calculated_value = byte_20 * coefficient
                                debug_print(f"  → Found valid frame at position {frame_start}")
                                debug_print(f"  → Byte 20 = {byte_20} × {coefficient} = {calculated_value}")
                                break  # Success, exit retry loop

                        debug_print(f"  ✗ Attempt {attempt + 1} failed, {'retrying...' if attempt < max_retries - 1 else 'giving up'}")
                        time.sleep(0.2)

                    if calculated_value is not None:
                        if name == "h2s":
                            data_h2s = calculated_value
                        elif name == "no2":
                            data_no2 = calculated_value
                        elif name == "o3":
                            data_o3 = calculated_value
                        elif name == "co":
                            data_co = calculated_value
                        elif name == "nh3":
                            data_nh3 = calculated_value
                        elif name == "so2":
                            data_so2 = calculated_value
                        debug_print(f"  ✓ {name.upper()} = {calculated_value}")
                    else:
                        debug_print(f"  ✗ Failed to read {name} after {max_retries} attempts")
                        
                except serial.SerialException as e:
                    debug_print(f"✗ Error communicating with {name}: {e}")
            else:
                debug_print(f"! No serial connection available for {name}")
                
except Exception as e:
    debug_print(f"\n✗ An error occurred while gathering data: {e}")
    traceback.print_exc()

# Display all collected data
debug_print(f"\n--- Collected Sensor Data ---")
debug_print(f"H2S: {data_h2s} ppb")
debug_print(f"NO2: {data_no2} ppb")
debug_print(f"O3:  {data_o3} ppb")
debug_print(f"CO:  {data_co} ppb")
debug_print(f"NH3: {data_nh3} ppb")
debug_print(f"SO2: {data_so2} ppb")

# Save to sqlite database
try:
    cursor.execute('''
    INSERT INTO data_envea (timestamp, h2s, no2, o3, co, nh3, so2) VALUES (?,?,?,?,?,?,?)'''
    , (rtc_time_str, data_h2s, data_no2, data_o3, data_co, data_nh3, data_so2))
    
    # Commit and close the connection
    conn.commit()
    
except Exception as e:
    debug_print(f"✗ Database error: {e}")
    traceback.print_exc()

# Close serial connections
if serial_connections:
    for name, connection in serial_connections.items():
        try:
            connection.close()
        except:
            pass

conn.close()
debug_print("\n=== ENVEA Sensor Reader Finished ===\n")