# -*- coding: iso-8859-1 -*-

from .Resultable import Resultable
from .Drawable import Drawable
import numpy
import threading
import tables

#############################################################################
#                                                                           #
# Name: Class Temp_Result                                                   #
#                                                                           #
# Purpose: Specialised class of Resultable and Drawable                     #
#          Contains recorded temperature data                               #
#                                                                           #
#############################################################################

class TemperatureResult(Resultable, Drawable):
    """
    Specialised class of Resultable and Drawable
    Contains recorded temperature data.
    
    Attributes:
        x: List of timestamps in seconds.
        y: List of temperature readings in Celsius.
        setpoint: Optional list of setpoint temperatures in Celsius.
        xlabel: Label for x-axis (default: "Time (s)").
        ylabel: Label for y-axis (default: "Temperature (C)").
    """
    def __init__(self, x = None, y = None, setpoint = None, desc = None, job_id = None, job_date = None):
        Resultable.__init__(self)
        Drawable.__init__(self)
        
        # Set default labels
        self.xlabel = "Time (s)"
        self.ylabel = "Temperature (C)"
        
        # Initialize data lists
        self.x = [] if x is None else x
        self.y = [] if y is None else y
        self.setpoint = [] if setpoint is None else setpoint
        
        # Set metadata if provided
        if desc is not None:
            self.description = desc
        if job_id is not None:
            self.job_id = job_id
        if job_date is not None:
            self.job_date = job_date
        
        # Listener management for GUI updates
        self.__listeners = []
        self.__listener_lock = threading.Lock()
    
    def get_number_of_channels(self):
        """Returns 1 for temperature data."""
        return 1
    
    def register_listener(self, listener):
        """Register a listener to be notified when data changes."""
        with self.__listener_lock:
            if listener not in self.__listeners:
                self.__listeners.append(listener)
    
    def unregister_listener(self, listener):
        """Unregister a listener."""
        with self.__listener_lock:
            if listener in self.__listeners:
                self.__listeners.remove(listener)
    
    def get_xdata(self):
        """Returns the x data (timestamps) as numpy array."""
        if isinstance(self.x, numpy.ndarray):
            return self.x
        return numpy.array(self.x, dtype=float) if self.x else numpy.array([], dtype=float)
    
    def get_ydata(self, channel=0):
        """Returns the y data (temperature readings) as numpy array."""
        if channel == 0:
            if isinstance(self.y, numpy.ndarray):
                return self.y
            return numpy.array(self.y, dtype=float) if self.y else numpy.array([], dtype=float)
        return numpy.array([], dtype=float)
    
    def __len__(self):
        """Returns the number of data points."""
        return len(self.y) if self.y else 0
    
    def uses_statistics(self):
        """Returns False as temperature data doesn't use statistics."""
        return False
    
    def ready_for_drawing_error(self):
        """Returns False as temperature data doesn't have error bars."""
        return False
    
    def get_yerr(self, channel=0):
        """Returns empty error data."""
        return numpy.array([], dtype=float)
    
    def get_errorplotdata(self):
        """Returns empty error plot data."""
        return [[], [], []]
    
    def get_lineplotdata(self):
        """Returns line plot data (not used for temperature)."""
        return [[], []]
    
    def get_xmin(self):
        """Returns minimum of x."""
        xdata = self.get_xdata()
        return xdata.min() if len(xdata) > 0 else 0
    
    def get_xmax(self):
        """Returns maximum of x."""
        xdata = self.get_xdata()
        return xdata.max() if len(xdata) > 0 else 0
    
    def get_ymin(self):
        """Returns minimum of y."""
        ydata = self.get_ydata(0)
        return ydata.min() if len(ydata) > 0 else 0
    
    def get_ymax(self):
        """Returns maximum of y."""
        ydata = self.get_ydata(0)
        return ydata.max() if len(ydata) > 0 else 0
    
    def get_xminpos(self):
        """Returns smallest positive value of x."""
        xdata = self.get_xdata()
        mask = xdata > 0
        if numpy.any(mask):
            return xdata[mask].min()
        return 0
    
    def get_yminpos(self):
        """Returns smallest positive value of y."""
        ydata = self.get_ydata(0)
        mask = ydata > 0
        if numpy.any(mask):
            return ydata[mask].min()
        return 0
    
    def write_to_hdf(self, hdffile, where, name, title, complib=None, complevel=None):
        """
        Write the temperature result to an HDF5 group.

        Stores x (timestamps), y (temperature readings), and setpoint arrays
        as an HDF5 table, with metadata saved as group attributes.
        """
        h5_table_format = {
            "x": tables.Float64Col(),
            "y": tables.Float64Col(),
            "setpoint": tables.Float64Col(),
        }
        filter = None
        if complib is not None:
            if complevel is None:
                complevel = 9
            filter = tables.Filters(complevel=complevel, complib=complib, shuffle=1)

        with self.__listener_lock:
            xdata = numpy.array(self.x, dtype=float) if self.x else numpy.array([], dtype=float)
            ydata = numpy.array(self.y, dtype=float) if self.y else numpy.array([], dtype=float)
            spdata = numpy.array(self.setpoint, dtype=float) if self.setpoint else numpy.array([], dtype=float)

        # Use the length of the longest array; shorter ones are padded with 0
        n = max(len(xdata), len(ydata), 1)
        if len(xdata) < n:
            xdata = numpy.pad(xdata, (0, n - len(xdata)), constant_values=0.0)
        if len(ydata) < n:
            ydata = numpy.pad(ydata, (0, n - len(ydata)), constant_values=0.0)
        if len(spdata) < n:
            spdata = numpy.pad(spdata, (0, n - len(spdata)), constant_values=0.0)

        temp_table = hdffile.create_table(
            where=where, name=name,
            description=h5_table_format,
            title=title,
            filters=filter,
            expectedrows=n,
        )
        temp_table.flavor = "numpy"
        temp_table.attrs.damaris_type = "TemperatureResult"
        temp_table.attrs.xlabel = self.xlabel
        temp_table.attrs.ylabel = self.ylabel

        if hasattr(self, "job_id") and self.job_id is not None:
            temp_table.attrs.job_id = self.job_id
        if hasattr(self, "job_date") and self.job_date is not None:
            temp_table.attrs.time = "%04d%02d%02d %02d:%02d:%02d.%03d" % (
                self.job_date.year,
                self.job_date.month,
                self.job_date.day,
                self.job_date.hour,
                self.job_date.minute,
                self.job_date.second,
                self.job_date.microsecond // 1000,
            )
        if hasattr(self, "description") and self.description is not None:
            for (key, value) in self.description.items():
                temp_table.attrs.__setattr__("description_" + key, str(value))

        row = temp_table.row
        for i in range(n):
            row["x"] = xdata[i]
            row["y"] = ydata[i]
            row["setpoint"] = spdata[i]
            row.append()

        temp_table.flush()

    def __repr__(self):
        """String representation of the temperature result."""
        return f"TemperatureResult(points={len(self)}, job_id={self.job_id})"


def read_from_hdf(hdf_node):
    """
    Read a TemperatureResult object from an HDF5 table node.
    Returns None if the node is not suitable.
    """
    if not isinstance(hdf_node, tables.Table):
        return None

    if hdf_node._v_attrs.damaris_type != "TemperatureResult":
        return None

    tr = TemperatureResult()
    tr.xlabel = getattr(hdf_node._v_attrs, "xlabel", "Time (s)")
    tr.ylabel = getattr(hdf_node._v_attrs, "ylabel", "Temperature (C)")

    for r in hdf_node.iterrows():
        tr.x.append(r["x"])
        tr.y.append(r["y"])
        sp = r["setpoint"]
        if sp != 0.0 or len(tr.setpoint) > 0:
            tr.setpoint.append(sp)

    return tr
