import numpy as np
import time
from .slm import SLM


class bbbSLM(SLM):
    """BeagleBone Black connected TI SLM.
    This SLM looks like a screen attached to a computer,
    in fact it is connected via display parallel interface. TI provide RGB projector
    which we stripped off to get access to Digital Mirror Array aka SLM.
    It still requires a couple settings after reboot, so make sure that you did it:
    - increase frame rate to 60 FPS (done in kernel overlay setting)
    - sync SLM to display video sync signal (so they run and update synchronously
    """

    def __init__(self, *args, shape=(360, 640), **kwds):
        super().__init__(*args, shape=(360, 640), **kwds)  # forcing SLM size
        self.config["Device model"] = "BeagleBone Black SLM"
        self.config["SLM type"] = "DMD"
        self.deviceProperties.update(
            {
                "SettleTime",
                "FPS",
            }
        )
        # some hardware inner working details
        self.slmFPS = 60  # frame rate in frames per second (FPS)
        # it takes one display frame time to send data to SLM from Frame Buffer
        # in case if we update FB when it is already sending we should wait at least one extra frame time
        self.settleTime = 2 / self.slmFPS  # in seconds
        self.settleTime += 0.001  # a bit extra just to be sure

        # setup ADC system which exposed in /sys
        self.fileADC = open("/sys/bus/iio/devices/iio:device0/in_voltage0_raw")

    def getSettleTime(self):
        return self.settleTime

    def getFPS(self):
        return self.slmFPS

    # This SLM looks like displays so we need to convert SLM pixel values
    # to physical display (Frame Buffer) representation
    def physical2fb(self):
        """Convert physical pixels values to their frame buffer representation.

        The SLM can convert different bit color planes from RGB representation
        to mirrors on off plane. But we cannot figure out which bit plane is when
        during a frame display. So we just set every color bit to on including
        unused alpha channel.
        """
        np.copyto(self.frameBuffer, self.pxlsPhysical)
        # now scale up
        self.frameBuffer *= 256**4 - 1  # convert on/off to display representation

    # Hardware dependent
    def display(self):
        """Send prepared data (in frame buffer) to SLM hardware"""
        self.frameBuffer.tofile("/dev/fb0")

    # Hardware dependent
    def _measure(self):
        # We need to acquire longer then one frame
        # to guarantee at least one SLM flick (when it toggles pixels in a weird way)
        # this time is usually short compare to a frame time and happens right around video sync signal
        # Ideally would be to avoid acquisition during the flick,
        # but that requires precision timing within a frame.
        aquisition_time = 1.4 / self.slmFPS
        aquisition_deadline = time.perf_counter() + aquisition_time
        meas = []
        while time.perf_counter() <= aquisition_deadline:
            # Read ADC for currently displayed mask
            self.fileADC.seek(0)
            b = self.fileADC.read()  # about 230 uS per seek and read 
            meas.append(float(b))
        brightness = np.mean(meas)
        return brightness
