"""
Auto-Calibration System for Sensor Current Mismatch
"""

import numpy as np

class SensorAutoCalibrator:
    """Auto-calibration for current mismatch between sensors"""
    
    def __init__(self, reference_current=10.0):
        self.reference_current = reference_current  # mA
        self.calibration_factors = {1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}
        self.offset_corrections = {1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}
        
    def measure_currents(self, sensors_data):
        """Measure current of all sensors at 20°C"""
        print("🔧 Measuring sensor currents at 20°C...")
        
        # Simulated measurement (in reality, use multimeter)
        measured_currents = {}
        for sensor_id, data in sensors_data.items():
            # Simulate measurement with some noise
            actual_current = data['currents'][4]  # Index 4 = 20°C
            measured = actual_current + np.random.normal(0, 0.05)
            measured_currents[sensor_id] = measured
            
            print(f"  Sensor #{sensor_id}: {measured:.2f} mA")
        
        return measured_currents
    
    def calculate_calibration_factors(self, measured_currents):
        """Calculate calibration factors to normalize currents"""
        print("\n📊 Calculating calibration factors...")
        
        # Calculate average current
        avg_current = np.mean(list(measured_currents.values()))
        print(f"  Average current: {avg_current:.2f} mA")
        print(f"  Reference: {self.reference_current:.2f} mA")
        
        # Calculate factors to normalize to reference
        for sensor_id, current in measured_currents.items():
            # Factor to convert to reference current
            factor = self.reference_current / current
            self.calibration_factors[sensor_id] = factor
            
            # Offset correction (for linearity)
            offset = self.reference_current - current
            self.offset_corrections[sensor_id] = offset
            
            print(f"  Sensor #{sensor_id}:")
            print(f"    Current: {current:.2f} mA")
            print(f"    Factor: {factor:.4f}")
            print(f"    Offset: {offset:.3f} mA")
    
    def create_calibration_equation(self, sensor_id):
        """Create calibration equation for a sensor"""
        factor = self.calibration_factors[sensor_id]
        offset = self.offset_corrections[sensor_id]
        
        equation = f"I_calibrated = {factor:.4f} × I_measured"
        if abs(offset) > 0.01:
            if offset > 0:
                equation += f" + {abs(offset):.3f}"
            else:
                equation += f" - {abs(offset):.3f}"
        
        return equation
    
    def apply_calibration(self, raw_readings):
        """Apply calibration to raw readings"""
        calibrated = {}
        
        for sensor_id, raw_value in raw_readings.items():
            factor = self.calibration_factors[sensor_id]
            offset = self.offset_corrections[sensor_id]
            
            # Apply calibration: y = a*x + b
            calibrated_value = (raw_value * factor) + offset
            calibrated[sensor_id] = calibrated_value
        
        return calibrated
    
    def verify_calibration(self, sensors_data):
        """Verify calibration by simulating corrected readings"""
        print("\n✅ Verification of Calibration")
        print("-"*40)
        
        # Simulate field measurements at 20°C
        print("Simulating field measurements at 20°C:")
        
        # Create synthetic field data
        field_truth = 1000.0  # V/m (reference field)
        raw_readings = {}
        
        for sensor_id in range(1, 5):
            # Each sensor reads differently due to current mismatch
            current_factor = sensors_data[sensor_id]['currents'][4] / 10.0
            raw_reading = field_truth * (1.0 / current_factor)
            raw_readings[sensor_id] = raw_reading
        
        print("\nRaw readings (before calibration):")
        for sensor_id, reading in raw_readings.items():
            error = ((reading - field_truth) / field_truth) * 100
            print(f"  Sensor #{sensor_id}: {reading:.1f} V/m ({error:+.1f}%)")
        
        # Apply calibration
        calibrated = self.apply_calibration(raw_readings)
        
        print("\nCalibrated readings (after calibration):")
        max_error = 0
        for sensor_id, reading in calibrated.items():
            error = ((reading - field_truth) / field_truth) * 100
            max_error = max(max_error, abs(error))
            print(f"  Sensor #{sensor_id}: {reading:.1f} V/m ({error:+.1f}%)")
        
        print(f"\nMaximum error after calibration: {max_error:.2f}%")
        
        if max_error < 0.5:
            print("🎉 Calibration successful! All sensors within 0.5%")
            return True
        else:
            print("⚠️  Calibration needs improvement")
            return False
    
    def generate_calibration_code(self):
        """Generate Python code for calibration"""
        print("\n" + "="*60)
        print("📝 GENERATED CALIBRATION CODE")
        print("="*60)
        
        code = """
# AUTO-GENERATED CALIBRATION CODE
# Generated for Snow Dust Project - Electric Field Sensors
# Date: 2026-01-30

class FieldSensorCalibrator:
    \"\"\"Calibration for electric field sensors\"\"\"
    
    # Calibration factors (measured at 20°C)
    CALIBRATION_FACTORS = {"""
        
        for sensor_id in range(1, 5):
            factor = self.calibration_factors[sensor_id]
            code += f"\n        {sensor_id}: {factor:.4f},"
        
        code += "\n    }"
        
        code += """
    
    # Offset corrections (mA)
    OFFSET_CORRECTIONS = {"""
        
        for sensor_id in range(1, 5):
            offset = self.offset_corrections[sensor_id]
            code += f"\n        {sensor_id}: {offset:.3f},"
        
        code += """
    }
    
    @staticmethod
    def calibrate_reading(sensor_id, raw_reading):
        \"\"\"Apply calibration to a single reading\"\"\"
        factor = FieldSensorCalibrator.CALIBRATION_FACTORS.get(sensor_id, 1.0)
        offset = FieldSensorCalibrator.OFFSET_CORRECTIONS.get(sensor_id, 0.0)
        
        # Apply: y = a*x + b
        calibrated = (raw_reading * factor) + offset
        return calibrated
    
    @staticmethod
    def calibrate_all_readings(readings_dict):
        \"\"\"Calibrate all sensor readings at once\"\"\"
        calibrated = {}
        for sensor_id, raw_value in readings_dict.items():
            calibrated[sensor_id] = FieldSensorCalibrator.calibrate_reading(sensor_id, raw_value)
        return calibrated

# Example usage:
# raw_data = {1: 1500.5, 2: 1498.2, 3: 1502.7, 4: 1495.8}
# calibrated_data = FieldSensorCalibrator.calibrate_all_readings(raw_data)
"""
        
        print(code)
        
        # Save to file
        with open("sensor_calibration.py", "w") as f:
            f.write(code)
        
        print("\n✅ Calibration code saved to: sensor_calibration.py")
        print("📋 Usage: from sensor_calibration import FieldSensorCalibrator")

def main():
    """Main execution"""
    print("="*70)
    print("🔧 AUTO-CALIBRATION SYSTEM FOR SENSOR CURRENT MISMATCH")
    print("="*70)
    
    # Sensor data from thermal test
    sensors_data = {
        1: {'currents': [10.50, 10.59, 10.79, 10.84, 10.98, 11.14, 11.29]},
        2: {'currents': [10.06, 10.19, 10.38, 10.50, 10.65, 10.77, 10.88]},
        3: {'currents': [9.92, 10.14, 10.26, 10.37, 10.51, 10.61, 10.79]},
        4: {'currents': [8.76, 8.81, 9.06, 9.27, 9.28, 9.51, 9.56]}
    }
    
    # Create calibrator
    calibrator = SensorAutoCalibrator(reference_current=10.0)
    
    # Step 1: Measure currents
    measured_currents = calibrator.measure_currents(sensors_data)
    
    # Step 2: Calculate factors
    calibrator.calculate_calibration_factors(measured_currents)
    
    # Step 3: Show calibration equations
    print("\n📐 Calibration Equations:")
    for sensor_id in range(1, 5):
        equation = calibrator.create_calibration_equation(sensor_id)
        print(f"  Sensor #{sensor_id}: {equation}")
    
    # Step 4: Verify calibration
    success = calibrator.verify_calibration(sensors_data)
    
    # Step 5: Generate code
    if success:
        calibrator.generate_calibration_code()
        
        print("\n" + "="*70)
        print("🎯 NEXT STEPS:")
        print("="*70)
        print("1. Import sensor_calibration.py into your data processing")
        print("2. Apply calibration to all field measurements")
        print("3. Proceed to Noise Test (Test #2)")
        print("4. Field deployment after all tests pass")
    else:
        print("\n⚠️  Calibration needs hardware adjustment")
        print("   Consider adding trimmer resistors to each sensor board")

if __name__ == "__main__":
    main()
