import numpy as np
import re
import trace
[docs]
class TracePixel:
    """
    A class used to represent and manipulate a single trace pixel from a SLAP2 data file.
    Attributes
    ----------
    fileName : str
        The name of the file to be loaded.
    superPixelId : int
        The identifier for the superpixel.
    superPixelNumPixels : int
        The number of pixels in the superpixel.
    superPixelNumPixelsSelected : int
        The number of selected pixels in the superpixel.
    byteOffsets : list
        A list of byte offsets for data extraction.
    lineIdxs : list
        A list of line indices for data extraction.
    data : list
        The loaded data for the trace pixel.
    isLoaded : bool
        A flag indicating whether the data is loaded.
    firstCycleOffsetBytes : int
        The offset for the first cycle in bytes.
    numCycles : int
        The number of cycles.
    bytesPerCycle : int
        The number of bytes per cycle.
    linesPerCycle : int
        The number of lines per cycle.
    y : int
        An unspecified variable, initialized to -1.
    
    Descriptions for methods:
    ------------------------------
           
            
    Methods
    ---------
    __init__() :
        Initialize the fields with values.
    Return
    -------
        Self with populated fields.
    Methods
    ---------
    load(hMemmap=None) :
        Loads the data for the trace pixel from the specified memory-mapped file, which is given as an input (hMemmap).
    Return
    -------
        Self with edited data field and isLoaded changed to true.
    
    """
    def __init__(self):
        self.fileName = ""  # Name of the file to be loaded
        self.superPixelId = 0  # Identifier for the superpixel
        self.superPixelNumPixels = 0  # Number of pixels in the superpixel
        self.superPixelNumPixelsSelected = 0  # Number of selected pixels in the superpixel
        self.byteOffsets = []  # List of byte offsets for data extraction
        self.lineIdxs = []  # List of line indices for data extraction
        self.data = None  # Loaded data
        self.isLoaded = False  # Flag indicating whether data is loaded
        self.firstCycleOffsetBytes = 0  # Offset for the first cycle in bytes
        self.numCycles = 0  # Number of cycles
        self.bytesPerCycle = 0  # Number of bytes per cycle
        self.linesPerCycle = 0  # Number of lines per cycle
        self.y = -1  # Unspecified variable, initialized to -1
    def load(self, hMemmap=None):
        """
        Loads the data for the trace pixel from the specified memory-mapped file.
        Parameters
        ----------
        hMemmap : np.memmap, optional
            An optional memory-mapped file object. If provided, this object will be used to load the data.
            If not provided, a new memory-mapped file object will be created based on the fileName attribute.
        Returns
        -------
        None
        """
        # Check if data is already loaded
        allLoaded = self.isLoaded
        if allLoaded:
            return
        loadedFilename = ""  # Initialize loaded filename
        if hMemmap is not None:
            # If a memory map object is provided, get its filename and set the format
            loadedFilename = hMemmap.Filename
            hMemmap.Format = 'int16'
        # Update filename to change the extension from .meta to .dat
        fileName_ = self.fileName
        fileName_ = re.sub(r'\.meta$', '.dat', fileName_, flags=re.IGNORECASE)
        if loadedFilename != fileName_:
            # Create a new memory map if the filename does not match
            hMemmap = np.memmap(fileName_, dtype='int16', mode='r')
            loadedFilename = fileName_
        # Calculate byte offsets for each cycle
        cycleIdxs = np.arange(0, self.numCycles, dtype=np.uint64)
        cycleByteOffsets = self.firstCycleOffsetBytes + cycleIdxs * self.bytesPerCycle
        # Convert byte offsets to sample offsets (assuming 2 bytes per sample)
        cycleSampleOffsets = [int(x // 2) for x in cycleByteOffsets]
        sampleOffsets = [int(x // 2) for x in self.byteOffsets]
        # Create a 2D array of sample offsets
        _sampleOffsets = np.zeros((len(self.byteOffsets), len(cycleSampleOffsets)), dtype='uint64')
        for i in range(len(self.byteOffsets)):
            _sampleOffsets[i, :] = sampleOffsets[i] + np.array(cycleSampleOffsets)
        
        sampleOffsets = _sampleOffsets
        sampleOffsets = sampleOffsets.astype('int64')  # Ensure offsets are in int64 format
        # Ensure the offsets do not exceed the length of the memory-mapped file
        if (sampleOffsets[0, :][len(sampleOffsets[0, :]) - 1]) > len(hMemmap):
            sampleOffsets[0, :][len(sampleOffsets[0, :]) - 1] = len(hMemmap)
        data_ = []  # List to store the loaded data
        # Extract data from the memory-mapped file using the calculated offsets
        for i in range(sampleOffsets.shape[0]):
            data_.append(np.take(hMemmap, (sampleOffsets[i, :] - 1), 0))
        
        self.data = data_  # Assign the loaded data to the class attribute
        self.isLoaded = True  # Mark data as loaded