import random

####################################################################################################
# Simulated Devices
####################################################################################################

class AnalogOutput(RegisterBase):    
    def doRead(self):
        return self.val if hasattr(self, 'val') else 0.0 

    def doWrite(self, val):
        self.val = val

class AnalogInput(ReadonlyRegisterBase):
    def doRead(self):
        time.sleep(0.001)
        self.val = to_array(self.calc(), 'd')
        return self.val

class Waveform(ReadonlyRegisterBase, ReadonlyRegisterArray):
    def doRead(self):
        time.sleep(0.001)
        self.val = to_array(self.calc(), 'd')
        return self.val

    def getSize(self):
        return len(self.take(-1))    #only reads if cache is None

class Image(ReadonlyRegisterBase, ReadonlyRegisterMatrix):
    def doRead(self):
        time.sleep(0.001)
        self.val = to_array(self.calc(), 'd')
        return self.val

    def getWidth(self):
        return len(self.take(-1)[0])

    def getHeight(self):
        return len(self.take(-1))



class Random(AnalogInput):
    def calc(self):
        return random.random()


class SinusoidSample(AnalogInput):
    def calc(self):
        self.x = self.x + 0.1 if hasattr(self, 'x') else 0.0
        noise = (random.random() - 0.5) / 10.0
        return math.sin(self.x)  + noise

class SinusoidTime(AnalogInput):
    def calc(self):
        noise = (random.random() - 0.5) / 10.0
        return math.sin(time.time())  + noise


class SinusoidWaveform(Waveform):
    def calc(self):
        ret = []
        x = random.random()
        for i in range (20):
            ret.append(math.sin(x))
            x = x + 0.1
        return ret

class SinusoidImage(Image):
    def calc(self):     
        (width, height) = (200, 100)
        ret = []
        x = random.random();
        base = []        
        for i in range (width):
            base.append( math.sin(x))
            x = x + 0.05
        for i in range (height):
            noise = (random.random() - 0.5)/5.0 
            ret.append([x+noise for x in base])
        return ret
        
        
#Defintion
add_device(DummyMotor("m1"), True)
add_device(DummyMotor("m2"), True)
add_device(DummyRegister("reg1",3), True)
add_device(AnalogOutput("ao1"), True)
add_device(AnalogOutput("ao2"), True)
add_device(SinusoidSample("ai1"), True)
add_device(SinusoidTime("ai2"), True)
add_device(Random("ai3"), True)
add_device(SinusoidWaveform("wf1"), True)
add_device(SinusoidImage("im1"), True)
add_device(DummyPositioner("p1"),True)
add_device(MotorGroupBase("mg1", m1, m2), True)
add_device(MotorGroupDiscretePositioner("dp1", mg1), True)



#Initial Configuration
if p1.config.unit is None:
    p1.config.minValue = 0.0 #Not persisted
    p1.config.maxValue = 1000.0
    p1.config.unit = "mm"
    p1.config.save()
    p1.initialize()

if dp1.config.positions is None:
    dp1.config.positions = ["Park","Ready","Out","Clear"]
    dp1.config.motor1 = ["0.0","4.0","8.0" ,"0.0"]
    dp1.config.motor2 = ["0.0","5.0","3.0" ,"NaN"]
    dp1.config.save()
    dp1.initialize()



#Update
m1.setMonitored(True)
m2.setMonitored(True)




####################################################################################################
# Simple Readable / Writable objects can be created and used in scans
####################################################################################################
class WritableScalar(Writable):
    def write(self, value):
        pass

class ReadableScalar(Readable):
    def read(self):        
        return random.random()


class ReadableWaveform(ReadableArray):
    def getSize(self):
        return 20

    def read(self):
        ret = []
        for i in range (self.getSize()):
            ret.append(random.random())
        return ret

class ReadableImage(ReadableMatrix):
    def read(self):
        ret = []
        for i in range (self.getHeight()):
            ret.append([random.random()] * self.getWidth())
        return to_array(ret, 'd')

    def getWidth(self):
        return 80

    def getHeight(self):
        return 40



ws1 = WritableScalar()
rs1 = ReadableScalar()
rw1 = ReadableWaveform()
ri1 = ReadableImage()


####################################################################################################
# Imaging
####################################################################################################

configured = os.path.exists(Device.getConfigFileName("src1"))

add_device(RegisterMatrixSource("src1", im1), True)
add_device(RegisterMatrixSource("src2", ri1), True)

src1.polling = 100
src2.polling = 100

#Some configuration for so the imaging will work out of the box
if not configured:
    import ch.psi.pshell.imaging.Colormap
    src1.config.colormapAutomatic = True
    src1.config.colormap = ch.psi.pshell.imaging.Colormap.Temperature
    src1.config.save()
    src2.config.colormapAutomatic = True
    src2.config.save()


