"""
محاكاة المعايرة الميدانية للكشف الكهربائي في العواصف الثلجية
بدون الحاجة للنشر الفعلي في الميدان
"""

import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import json

class SnowstormFieldSimulator:
    """محاكاة واقعية للمجال الكهربائي خلال العواصف الثلجية"""
    
    def __init__(self, duration_days=7, sampling_rate=100):
        self.duration_days = duration_days
        self.sampling_rate = sampling_rate  # Hz
        self.total_samples = duration_days * 24 * 3600 * sampling_rate
        
        # أنماط العواصف الثلجية المعروفة
        self.storm_patterns = {
            'quiet': {'mean': 0, 'std': 0.1, 'events': 0},
            'onset': {'mean': 0.5, 'std': 0.3, 'events': 2},
            'peak': {'mean': 2.0, 'std': 1.5, 'events': 5},
            'recovery': {'mean': 0.8, 'std': 0.5, 'events': 3}
        }
    
    def generate_realistic_field(self):
        """توليد مجال كهربائي واقعي للعواصف الثلجية"""
        samples_per_day = 24 * 3600 * self.sampling_rate
        time = np.linspace(0, self.duration_days * 24, self.total_samples)
        
        # المجال الأساسي
        base_field = np.zeros(self.total_samples)
        
        # إضافة أحداث العواصف
        storm_events = []
        for day in range(self.duration_days):
            # كل يوم له نمط مختلف
            pattern_key = list(self.storm_patterns.keys())[day % len(self.storm_patterns)]
            pattern = self.storm_patterns[pattern_key]
            
            # توليد أحداث العاصفة
            for event in range(pattern['events']):
                start = day * 24 + np.random.uniform(2, 20)  # بداية عشوائية
                duration = np.random.uniform(0.5, 3)  # مدة الحدث
                amplitude = np.random.normal(pattern['mean'], pattern['std'])
                
                # شكل الجرس للحدث
                event_samples = int(duration * 3600 * self.sampling_rate)
                event_start = int((start - duration/2) * 3600 * self.sampling_rate)
                
                if event_start + event_samples < len(base_field):
                    x = np.linspace(-3, 3, event_samples)
                    bell = amplitude * np.exp(-x**2)
                    base_field[event_start:event_start+event_samples] += bell
                    
                    storm_events.append({
                        'day': day + 1,
                        'start_hour': start,
                        'duration_hours': duration,
                        'amplitude_kV': amplitude
                    })
        
        # إضافة ضوضاء واقعية
        noise = np.random.normal(0, 0.05, self.total_samples)
        realistic_field = base_field + noise
        
        return time, realistic_field, storm_events
    
    def plot_field_pattern(self, time, field):
        """رسم أنماط المجال الكهربائي"""
        plt.figure(figsize=(12, 6))
        plt.plot(time, field, 'b-', alpha=0.7, linewidth=0.5)
        plt.xlabel('الوقت (ساعة)')
        plt.ylabel('شدة المجال الكهربائي (kV/m)')
        plt.title('محاكاة المجال الكهربائي خلال عاصفة ثلجية (7 أيام)')
        plt.grid(True, alpha=0.3)
        plt.savefig('snowstorm_field_simulation.png', dpi=300, bbox_inches='tight')
        plt.show()

class SensorArraySimulator:
    """محاكاة مصفوفة المستشعرات الرباعية"""
    
    def __init__(self, num_sensors=4):
        self.num_sensors = num_sensors
        
        # أخطاء واقعية للمستشعرات (بناء على بيانات مخبرية)
        self.sensor_errors = []
        for i in range(num_sensors):
            # أخطاء كسب وانزياح واقعية
            gain_error = np.random.normal(1.0, 0.08)  # خطأ 8% في الكسب
            offset_error = np.random.normal(0.0, 0.02)  # إزاحة 2%
            self.sensor_errors.append({
                'id': f'Sensor_{i+1}',
                'gain': gain_error,
                'offset': offset_error,
                'noise_std': 0.01 + i * 0.002  # ضوضاء متفاوتة
            })
    
    def apply_sensor_errors(self, true_field):
        """تطبيق أخطاء المستشعرات على المجال الحقيقي"""
        sensor_readings = []
        
        for sensor in self.sensor_errors:
            # محاكاة قراءة المستشعر مع الأخطاء
            gain = sensor['gain']
            offset = sensor['offset']
            noise = np.random.normal(0, sensor['noise_std'], len(true_field))
            
            reading = gain * true_field + offset + noise
            sensor_readings.append(reading)
        
        return sensor_readings
    
    def print_sensor_errors(self):
        """عرض أخطاء المستشعرات"""
        print("\n" + "="*60)
        print("أخطاء المستشعرات قبل المعايرة الميدانية:")
        print("="*60)
        for sensor in self.sensor_errors:
            print(f"{sensor['id']}:")
            print(f"  • خطأ الكسب: {sensor['gain']:.3f} ({abs(sensor['gain']-1)*100:.1f}%)")
            print(f"  • خطأ الإزاحة: {sensor['offset']:.3f} kV/m")
            print(f"  • مستوى الضوضاء: {sensor['noise_std']:.3f} kV/m")
            print()

class FieldCalibrationProcessor:
    """معالجة المعايرة الميدانية"""
    
    def __init__(self):
        self.calibration_factors = None
    
    def calculate_relative_calibration(self, sensor_readings, storm_events):
        """حساب عوامل المعايرة النسبية من أحداث العاصفة"""
        num_sensors = len(sensor_readings)
        calibration_factors = np.ones(num_sensors)
        
        # استخدام أحداث العاصفة كنقاط مرجعية
        event_indices = self._extract_event_indices(sensor_readings[0], storm_events)
        
        if len(event_indices) > 0:
            # متوسط القراءات خلال الأحداث
            event_readings = []
            for readings in sensor_readings:
                event_vals = readings[event_indices]
                event_readings.append(np.median(event_vals))
            
            # المستشعر المرجعي (الأكثر استقراراً)
            ref_index = np.argmin(np.std([r[event_indices] for r in sensor_readings], axis=1))
            reference = event_readings[ref_index]
            
            # حساب عوامل التصحيح
            for i in range(num_sensors):
                if i != ref_index:
                    calibration_factors[i] = reference / event_readings[i]
        
        return calibration_factors
    
    def _extract_event_indices(self, signal, storm_events, threshold=0.3):
        """استخراج عينات أحداث العاصفة"""
        event_indices = []
        for event in storm_events:
            start_sample = int(event['start_hour'] * 3600 * 100)  # 100 هرتز
            duration_samples = int(event['duration_hours'] * 3600 * 100)
            
            start_idx = max(0, start_sample - duration_samples//2)
            end_idx = min(len(signal), start_sample + duration_samples//2)
            
            # إضافة العينات التي تتجاوز العتبة
            event_signal = signal[start_idx:end_idx]
            above_threshold = np.where(np.abs(event_signal) > threshold)[0]
            event_indices.extend(start_idx + above_threshold)
        
        return np.array(event_indices)
    
    def apply_calibration(self, sensor_readings, calibration_factors):
        """تطبيق عوامل المعايرة"""
        calibrated_readings = []
        for i, readings in enumerate(sensor_readings):
            calibrated = readings * calibration_factors[i]
            calibrated_readings.append(calibrated)
        return calibrated_readings
    
    def evaluate_calibration(self, original_readings, calibrated_readings):
        """تقييم دقة المعايرة"""
        print("\n" + "="*60)
        print("نتائج المعايرة الميدانية:")
        print("="*60)
        
        # حساب التباين بين المستشعرات قبل وبعد المعايرة
        original_variances = []
        calibrated_variances = []
        
        for time_point in range(0, len(original_readings[0]), 1000):  # كل 10 ثواني
            orig_vals = [r[time_point] for r in original_readings]
            cal_vals = [r[time_point] for r in calibrated_readings]
            
            original_variances.append(np.var(orig_vals))
            calibrated_variances.append(np.var(cal_vals))
        
        print(f"التباين بين المستشعرات (قبل المعايرة): {np.mean(original_variances):.4f}")
        print(f"التباين بين المستشعرات (بعد المعايرة):  {np.mean(calibrated_variances):.4f}")
        print(f"نسبة التحسن: {100*(1 - np.mean(calibrated_variances)/np.mean(original_variances)):.1f}%")
        
        return np.mean(calibrated_variances)

def main():
    """التنفيذ الرئيسي لمحاكاة المعايرة الميدانية"""
    print("🚀 بدء محاكاة المعايرة الميدانية")
    print("❄️  محاكاة 7 أيام من جمع البيانات في ظروف عاصفة ثلجية")
    print("="*60)
    
    # 1. محاكاة المجال الكهربائي الطبيعي
    print("\n1. توليد المجال الكهربائي للعاصفة الثلجية...")
    field_sim = SnowstormFieldSimulator(duration_days=7, sampling_rate=100)
    time, true_field, storm_events = field_sim.generate_realistic_field()
    
    # 2. محاكاة مصفوفة المستشعرات
    print("2. محاكاة مصفوفة المستشعرات الرباعية...")
    sensor_array = SensorArraySimulator(num_sensors=4)
    sensor_array.print_sensor_errors()
    
    # 3. تطبيق أخطاء المستشعرات على المجال الحقيقي
    print("3. تطبيق أخطاء المستشعرات...")
    sensor_readings = sensor_array.apply_sensor_errors(true_field)
    
    # 4. معايرة ميدانية
    print("4. تطبيق خوارزمية المعايرة الميدانية...")
    cal_processor = FieldCalibrationProcessor()
    
    # حساب عوامل المعايرة من أحداث العاصفة
    calibration_factors = cal_processor.calculate_relative_calibration(
        sensor_readings, storm_events
    )
    
    print("\nعوامل المعايرة المحسوبة:")
    for i, factor in enumerate(calibration_factors):
        print(f"  {sensor_array.sensor_errors[i]['id']}: {factor:.4f}")
    
    # 5. تطبيق المعايرة
    calibrated_readings = cal_processor.apply_calibration(
        sensor_readings, calibration_factors
    )
    
    # 6. تقييم النتائج
    final_variance = cal_processor.evaluate_calibration(
        sensor_readings, calibrated_readings
    )
    
    # 7. حفظ النتائج
    print("\n5. حفظ النتائج...")
    results = {
        'simulation_date': datetime.now().isoformat(),
        'duration_days': 7,
        'sampling_rate': 100,
        'storm_events_detected': len(storm_events),
        'calibration_factors': calibration_factors.tolist(),
        'final_variance': float(final_variance),
        'sensor_errors': sensor_array.sensor_errors
    }
    
    with open('field_calibration_results.json', 'w', encoding='utf-8') as f:
        json.dump(results, f, ensure_ascii=False, indent=2)
    
    # 8. عرض رسومي
    print("\n6. إنشاء الرسوم البيانية...")
    fig, axes = plt.subplots(3, 1, figsize=(14, 10))
    
    # المجال الحقيقي
    axes[0].plot(time[::100], true_field[::100], 'k-', alpha=0.7, linewidth=0.5)
    axes[0].set_ylabel('المجال الحقيقي (kV/m)')
    axes[0].set_title('المجال الكهربائي الحقيقي خلال العاصفة')
    axes[0].grid(True, alpha=0.3)
    
    # قراءات المستشعرات قبل المعايرة
    for i, readings in enumerate(sensor_readings):
        axes[1].plot(time[::100], readings[::100], alpha=0.5, 
                    label=f'مستشعر {i+1} (قبل)')
    axes[1].set_ylabel('قراءات المستشعرات (kV/m)')
    axes[1].set_title('قراءات المستشعرات قبل المعايرة')
    axes[1].legend(loc='upper right', fontsize=8)
    axes[1].grid(True, alpha=0.3)
    
    # قراءات المستشعرات بعد المعايرة
    for i, readings in enumerate(calibrated_readings):
        axes[2].plot(time[::100], readings[::100], alpha=0.5,
                    label=f'مستشعر {i+1} (بعد)')
    axes[2].set_xlabel('الوقت (ساعة)')
    axes[2].set_ylabel('قراءات المعايرة (kV/m)')
    axes[2].set_title('قراءات المستشعرات بعد المعايرة الميدانية')
    axes[2].legend(loc='upper right', fontsize=8)
    axes[2].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.savefig('field_calibration_comparison.png', dpi=300, bbox_inches='tight')
    plt.show()
    
    print("\n" + "="*60)
    print("✅ محاكاة المعايرة الميدانية اكتملت بنجاح!")
    print("="*60)
    print("\n📁 الملفات المحفوظة:")
    print("  1. field_calibration_simulator.py - كود المحاكاة")
    print("  2. field_calibration_results.json - نتائج المعايرة")
    print("  3. field_calibration_comparison.png - الرسوم البيانية")
    print("\n🎯 الاستنتاج:")
    print("  • المعايرة الميدانية ممكنة بدون نشر فعلي")
    print("  • يمكن اختبار خوارزميات المعايرة بالمحاكاة")
    print("  • البيانات المحاكاة تساعد في تطوير النظام")

if __name__ == "__main__":
    main()
