from smbus2 import SMBus
import time
import json
import os

ADS1115_ADDR = 0x48
ADS1115_CONVERSION = 0x00
ADS1115_CONFIG = 0x01
CAL_FILE = "etape_calibration.txt"

# ADS1115 configuration:
# AIN0 single-ended, ±4.096V range, 128SPS, single-shot
CONFIG = (
    0x4000  # AIN0 single-ended
    | 0x0200  # ±4.096V range
    | 0x0100  # 128 samples per second
    | 0x8000  # Start single conversion
    | 0x0003  # Single-shot mode
)

# ------------------ READ VOLTAGE ------------------
def read_voltage(bus):
    """Read one voltage sample from ADS1115 channel A0"""
    time.sleep(0.1)
    config_bytes = [(CONFIG >> 8) & 0xFF, CONFIG & 0xFF]
    bus.write_i2c_block_data(ADS1115_ADDR, ADS1115_CONFIG, config_bytes)
    time.sleep(0.3)
    data = bus.read_i2c_block_data(ADS1115_ADDR, ADS1115_CONVERSION, 2)
    raw = (data[0] << 8) | data[1]
    if raw > 32767:
        raw -= 65536
    voltage = raw * 4.096 / 32768.0
    return voltage

# ------------------ CALIBRATION ------------------
def save_calibration(m, b):
    """Save calibration slope and intercept to file"""
    data = {"m": m, "b": b, "unit": "mm"}
    with open(CAL_FILE, "w") as f:
        json.dump(data, f)
    print(f"\n Calibration saved to {CAL_FILE}\n")

def load_calibration():
    """Load calibration if available"""
    if not os.path.exists(CAL_FILE):
        return None
    with open(CAL_FILE, "r") as f:
        return json.load(f)

def run_two_point_calibration(bus):
    """Perform two-point calibration using mm units"""
    print("\n--- Two-Point Calibration (units: mm) ---")

    print("\nCalibration Point 1:")
    target1 = float(input("Enter target level (mm): "))
    input("Position sensor at that level and press Enter to measure...")
    v1 = read_voltage(bus)

    print(f"Measured voltage: {v1:.3f} V")

    print("\nCalibration Point 2:")
    target2 = float(input("Enter target level (mm): "))
    input("Position sensor at that level and press Enter to measure...")
    v2 = read_voltage(bus)
  
    print(f"Measured voltage: {v2:.3f} V")

    # Compute linear relationship: level = m * V + b
    m = (target2 - target1) / (v2 - v1)
    b = target1 - m * v1

    print("\n--- Calibration Complete ---")
    print(f"Equation: level(mm) = {m:.4f} * voltage + {b:.4f}")
    save_calibration(m, b)

    return {"m": m, "b": b, "unit": "mm"}

# ------------------ LEVEL COMPUTATION ------------------
def voltage_to_level(v, cal):
    return cal["m"] * v + cal["b"]

# ------------------ MAIN ------------------
with SMBus(1) as bus:
    print("=== eTape Sensor Two-Point Calibration (ADS1115, mm) ===\n")

    cal = load_calibration()
    if cal: 
        if "m" not in cal or "b" not in cal:
            print("Old or incompatible calibration file found - redoing calibration.")
            cal = run_two_point_calibration(bus)
        
        else:      
            choice = input(
            f"Use saved calibration (level = {cal['m']:.4f} * V + {cal['b']:.4f})? [Y/n]: "
               ).lower()
            if choice == "n":
                cal = run_two_point_calibration(bus)
    else:
        print("No saved calibration found.")
        cal = run_two_point_calibration(bus)

    print("\nStarting continuous readout (Ctrl+C to stop)...\n")
    try:
        while True:
            v = read_voltage(bus)
            level = voltage_to_level(v, cal)
            print(f"Voltage: {v:.3f} V  |  Level: {level:.2f} mm")
            time.sleep(1)
    except KeyboardInterrupt:
        print("\nExiting. Goodbye!")