# -*- coding: iso-8859-1 -*-
"""
Simulated temperature controller for testing.

Provides a temperature controller that simulates real temperature readings
with configurable parameters including sinusoidal variation, noise, and missing values.
"""

import time
import math
import random
import threading

from .temperature import TemperatureController, register_controller


class SimulatedTemperatureController(TemperatureController):
    """
    A simulated temperature controller that generates realistic temperature readings.
    
    This controller is useful for testing and development without requiring
    physical hardware. It simulates temperature readings with:
    - Base temperature around 290K (17C)
    - Sinusoidal variation with amplitude of 10K
    - 5% random noise
    - 10% probability of missing values (returns previous value)
    - 60 second period for the sine wave
    
    Attributes:
        base_temp: Base temperature in Kelvin (default: 290.0)
        amplitude: Amplitude of sinusoidal variation in Kelvin (default: 10.0)
        period: Period of sine wave in seconds (default: 60.0)
        noise_level: Fraction of random noise (default: 0.05 = 5%)
        missing_probability: Probability of missing value (default: 0.10 = 10%)
    """

    def __init__(self, base_temp: float = 290.0, amplitude: float = 10.0, 
                 period: float = 60.0, noise_level: float = 0.05,
                 missing_probability: float = 0.10):
        """
        Initialize the simulated temperature controller.
        
        Args:
            base_temp: Base temperature in Kelvin (default: 290.0)
            amplitude: Amplitude of sinusoidal variation in Kelvin (default: 10.0)
            period: Period of sine wave in seconds (default: 60.0)
            noise_level: Fraction of random noise (default: 0.05 = 5%)
            missing_probability: Probability of missing value (default: 0.10 = 10%)
        """
        self.base_temp = float(base_temp)
        self.amplitude = float(amplitude)
        self.period = float(period)
        self.noise_level = float(noise_level)
        self.missing_probability = float(missing_probability)
        
        self._start_time = time.time()
        self._lock = threading.RLock()
        self._setpoint = self.base_temp
        self._last_temp = self.base_temp
        self._last_reading_time = self._start_time

    def get_temperature(self) -> float:
        """
        Get simulated current temperature in Kelvin.
        
        Returns:
            float: Simulated temperature in Kelvin
        """
        with self._lock:
            elapsed = time.time() - self._start_time
            current_time = time.time()
            
            # Calculate ideal sinusoidal temperature
            temp = self.base_temp + self.amplitude * math.sin(2 * math.pi * elapsed / self.period)
            
            # Add 5% random noise
            noise = temp * self.noise_level * (random.random() * 2 - 1)  # -5% to +5%
            temp += noise
            
            # 10% chance of missing value (return last good value)
            if random.random() < self.missing_probability:
                temp = self._last_temp
            else:
                self._last_temp = temp
                self._last_reading_time = current_time
            
            return temp

    def get_setpoint(self) -> float:
        """
        Get the target setpoint temperature in Kelvin.
        
        Returns:
            float: Target temperature in Kelvin
        """
        with self._lock:
            return self._setpoint

    def set_setpoint(self, temperature: float) -> bool:
        """
        Set the target temperature setpoint.
        
        Args:
            temperature: Target temperature in Kelvin
            
        Returns:
            bool: True if successful, False otherwise
        """
        with self._lock:
            self._setpoint = float(temperature)
            return True

    def close(self):
        """Clean up resources."""
        pass
    
    def reset(self):
        """Reset the simulation to initial state."""
        with self._lock:
            self._start_time = time.time()
            self._last_temp = self.base_temp
            self._last_reading_time = self._start_time


# Register the simulated controller with the factory
register_controller('simulated', SimulatedTemperatureController)
