import time
import datetime
from mylogger import * 
import ctypes

class DecodeState(object):
	UNKNOWN=0
	T0=1
	T1=2
	T2=3
	T3=4
	OK=5
	DONE=6

class MessageType(object):
	UNKNOWN="unknown"
	BATTERY_STATUS="battery"
	TEMPERATURE="temperature"
	HUMIDITY="humidity"
	TPOWER="total power"
	IPOWER="instant power"
	RAWDATA="raw"
	CRC="crc"

class BatteryStatus(object):
	GOOD="good"
	BAD="bad"

class CrcStatus(object):
	GOOD="ok"
	BAD="error"

class DecodedInfo:
	def __init__(self,type=MessageType.UNKNOWN, val=0):
		self.type = type
		self.val = val

class DecodedMessage:
	def __init__(self,vendorName='unknown',productId=None,deviceId=None):
		self.data = []
		self.vendorName = vendorName
		self.productId = productId
		self.deviceId = deviceId
		
	def append(self,info):
		self.data.append(info)

	def display(self):
		RfLogger.debug('Vendor Name:\t' + str(self.vendorName))
		RfLogger.debug('Product Id:\t' + hex(self.productId))
		RfLogger.debug('Device Id:\t' + hex(self.deviceId))
		for i in range(len(self.data)):
			info = self.data[i]
			if info.type == MessageType.RAWDATA:
				RfLogger.debug(info.type + ':\t' + hex(info.val))
			else:
				RfLogger.debug(info.type + ':\t' + str(info.val))

class DecodeOOK:
	def __init__(self):
		self.showPlot = False
		self.Reset()

	def Reset(self):
		self.total_bits = 0
		self.bits = 0
		self.flip = 0
		self.state = DecodeState.UNKNOWN
		self.pos = 0
		self.data = []
		self.data.append(0)
		self.message = None
		#self.message = DecodedMessage()

	def Restart(self):
		self.Reset()
		
	def nextPulse (self,width,level):
		if (self.state != DecodeState.DONE):
			result = self.Decode(width)
			if result == -1:
				self.Reset()
			elif result == 1:
				self.Done()
		return (self.state == DecodeState.DONE)
	
	def GotBit(self,value):
		self.total_bits += 1
		olddata = self.data[self.pos]
		self.data[self.pos] = (olddata >> 1) | (value << 7)
		self.bits += 1
		if (self.bits >= 8):
			self.bits = 0
			self.data.append(0)
			self.pos += 1
			RfLogger.debug("new byte from OOK @ %d: 0x%02x" % ((self.pos - 1),self.data[self.pos - 1]))
		self.state = DecodeState.OK

	def GotManchesterBit(self,value):
		self.flip ^= value # manchester code, long pulse flips the bit
		self.GotBit(self.flip)
	
	def Done(self):
		while (self.bits):
			self.GotBit(0) # padding
		self.state = DecodeState.DONE 

class OregonDecoderV2(DecodeOOK):
	def Interpret_1A2D(self):
		#sendor id: nible 0:3

		#channel: nibble 4
		channel_info = self.data[2] >> 4
		channelNb = int((channel_info*2)/3 + 1)

		#address: nible6-7
		address = self.data[3]
		
		deviceId = channelNb<<8 | address
		self.message = DecodedMessage(vendorName='OregonV2',productId=0x1A2D,deviceId=deviceId)
		
		
		#battery status: nible 9, bit 2
		bat_info = self.data[4] & 0xf
		if (bat_info & 0x4):
			self.message.append(DecodedInfo(type=MessageType.BATTERY_STATUS,val=BatteryStatus.BAD))
		else:
			self.message.append(DecodedInfo(type=MessageType.BATTERY_STATUS,val=BatteryStatus.GOOD))
			
		#temperature: nible 8, 10, 11
		temp_l=float((self.data[4]>>4) & 0xf)
		temp_m=float((self.data[5]) & 0xf)
		temp_h=float((self.data[5]>>4) & 0xf)
		temperature = ((temp_h * 100.0) + (temp_m * 10.0) + temp_l)/10.0
		
		#negative info: nible 13, bit 3
		neg=self.data[6] & 0x8
		#humidity: nible 12 and 15
		hum=(self.data[7]&0xf)*10 +((self.data[6]>>4)&0xf) 
		
		if neg:
			temperature *= -1
		
		self.message.append(DecodedInfo(type=MessageType.TEMPERATURE,val=temperature))
		self.message.append(DecodedInfo(type=MessageType.HUMIDITY,val=hum))
		
		#compute CRC
		crc=0
		for val in range(8):
			crc += (self.data[val]>>4)&0xf
			crc += (self.data[val]&0xf)
		crc -= 0xa
		crc &= 0xff
		if crc==self.data[8]:
			self.message.append(DecodedInfo(type=MessageType.CRC,val=CrcStatus.GOOD))
		else:
			self.message.append(DecodedInfo(type=MessageType.CRC,val=CrcStatus.BAD))
		#message.display()
		
		return 1
	
	def Interpret_EA4C(self):
		self.message = DecodedMessage()
		#sendor id: nible 0:3, byte 0-1

		#channel: nibble 4, byte 2 MSB
		channel_info = self.data[2] >> 4
		channelNb = int((channel_info*2)/3 + 1)

		#address: nible6-7, byte 3
		address = self.data[3]

		deviceId = channelNb<<8 | address
		self.message = DecodedMessage(vendorName='OregonV2',productId=0xEA4C,deviceId=deviceId)

		#battery status: nible 9, bit 2 = byte 4 LSB
		bat_info = self.data[4] & 0xf
		if (bat_info & 0x4):
			self.message.append(DecodedInfo(type=MessageType.BATTERY_STATUS,val=BatteryStatus.BAD))
		else:
			self.message.append(DecodedInfo(type=MessageType.BATTERY_STATUS,val=BatteryStatus.GOOD))
			
		#temperature: nible 8, 10, 11 = byte 4 LSB, byte 5
		temp_l=float((self.data[4]>>4) & 0xf)
		temp_m=float((self.data[5]) & 0xf)
		temp_h=float((self.data[5]>>4) & 0xf)
		temperature = ((temp_h * 100.0) + (temp_m * 10.0) + temp_l)/10.0
		
		#negative info: nible 13, bit 3 = byte 6
		neg=self.data[6] & 0x8
		
		if neg:
			temperature *= -1
		
		self.message.append(DecodedInfo(type=MessageType.TEMPERATURE,val=temperature))
		#message.display()
		
		return 1
		
	def TryInterpret(self):
		ret = None
		if (self.pos>2):
			#we have sensor ID
			sensorId=self.data[0]<<8 | self.data[1]
			#print 'Interpret ' + hex(sensorId) + ' '  + str(self.pos)
			if (sensorId == 0x1A2D):
				if self.pos == 9:
					#officialy need 10 bytes to interpret, but 9 is enough (don't knwow what to do with last byte !!)
					RfLogger.debug('Interpret 1A2D')
					ret = self.Interpret_1A2D()
			elif (sensorId == 0xEA4C):
				if self.pos == 8:
					# need 8 bytes to interpret
					RfLogger.debug('Interpret EA4C')
					ret = self.Interpret_EA4C()
			else:
				if self.pos<<4 == self.total_bits:
					RfLogger.debug("new byte from unknown %02x %02x @ %d: 0x%x" % (self.data[0],self.data[1],self.pos-1,self.data[self.pos-1]))

		return ret

	#add one bit to the packet data buffer
	def GotBit (self,value):
		if not (self.total_bits & 0x01):
			if value != 0:
				self.data[self.pos] = (self.data[self.pos] >> 1) | 0x80
			else:
				self.data[self.pos] = (self.data[self.pos] >> 1) | 0
		self.total_bits += 1
		#print 'total_bits ' + str(self.total_bits)
		self.pos = self.total_bits >> 4
		if self.pos >= len(self.data):
			#print "new byte @ %d: 0x%x" % ((self.pos - 1),self.data[self.pos - 1])
			self.data.append(0)
		self.state = DecodeState.OK

    
	def Decode (self,width):
		#if self.state == DecodeState.T0 or self.state == DecodeState.OK:
			#print "at %d have 0x%x" % (self.pos,self.data[self.pos])
		interpret_res = 0;
		if (200 <= width and width < 1200):
			w = (width >= 700)
			if self.state == DecodeState.UNKNOWN:
				if (w != 0):
					# Long pulse
					self.flip += 1
					#print 'have unknown long pulse ' + str(self.flip)
				elif (31 <= self.flip):
					# Short pulse, start bit
					RfLogger.debug('Have Start bit')
					self.flip = 0
					self.state = DecodeState.T0
				else:
					#Reset decoder
					#print 'reset unknown decoder'
					return -1
				
			elif self.state == DecodeState.OK:
				if (w == 0):
					# Short pulse
					self.state = DecodeState.T0
				else:
					# Long pulse
					self.GotManchesterBit(1)
					interpret_res=self.TryInterpret()
			elif self.state == DecodeState.T0:
				if (w == 0):
					# Second short pulse
					self.GotManchesterBit(0)
					interpret_res=self.TryInterpret()
				else:
					RfLogger.debug('Reset T0 ' + str(width))
					# Reset decoder
					return -1

		else:
			#if self.state == DecodeState.T0 or self.state == DecodeState.OK:
			return -1

		return interpret_res
	
class OregonDecoderV3(OregonDecoderV2):
	def hiB(nibble):
		return (nibble>>4)&0xf
	def loB(nibble):
		return (nibble&0xf)

	def cm119ChkSumOk(self):
		sum=0
		for i in range(1,11,1):
			sum += hiB(self.data[i])+loB(self.data[i])
		sum += loB(self.data[11])
		sumRef = (loB(self.data[12]))<<4 + hiB(self.data[11])
		return sum==sumRef

	def Interpret_CM119(self):
		deviceId = hiB(self.data[0]) + self.data[2]
		productId = self.data[2]
		instantPower=(loB(self.data[5]))<<12 + (self.data[4])<<4 + hiB(self.data[3])
		#Low10 9 8 7 6 hi5
		totalPower=loB(self.data[10])
		for i in range(9,5,-1):
			totalPower *= 256
			totalPower += self.data[i]
		totalPower *= 16
		totalPower += hiB(self.data[5])
		totalPower /= 223

		self.message = DecodedMessage(vendorName='OWL CM119',productId=productId,deviceId=deviceId)
		self.message.append(DecodedInfo(type=MessageType.IPOWER,val=instantPower))
		self.message.append(DecodedInfo(type=MessageType.TPOWER,val=totalPower))

		return 1

	def TryInterpret(self):
		ret = 0
		if (self.pos==12):
			cm119ID=self.data[1]&0xf
			if (cm119ID==0xa and cm119ChkSumOk()):
				ret=Interpret_CM119()
		return ret

	def Decode (self,width):
		#if self.state == DecodeState.T0 or self.state == DecodeState.OK:
			#print "at %d have 0x%x" % (self.pos,self.data[self.pos])
		interpret_res = 0;
		if (200 <= width and width < 1200):
			w = (width >= 700)
			if self.state == DecodeState.UNKNOWN:
				if (w == 0):
					# Short pulse
					self.flip += 1
					#print 'have unknown long pulse ' + str(self.flip)
				elif (20 <= self.flip):
					# Long pulse, start bit
					RfLogger.debug('Have V3 Start bit ' + str(width))
					self.flip = 0
					self.GotManchesterBit(0)
					self.state = DecodeState.OK
				else:
					#Reset decoder
					#print 'reset unknown decoder'
					return -1

			elif self.state == DecodeState.OK:
				if (w == 0):
					#print 'V3 to T0 ' + str(width)
					# Short pulse
					self.state = DecodeState.T0
				else:
					# Long pulse
					#print 'V3 add 1 ' + str(width)
					self.GotManchesterBit(1)
					interpret_res=self.TryInterpret()
			elif self.state == DecodeState.T0:
				if (w == 0):
					#print 'V3 add 0 ' + str(width)
					# Second short pulse
					self.GotManchesterBit(0)
					interpret_res=self.TryInterpret()
				else:
					RfLogger.debug('V3 reset ' + str(width))
					# Reset decoder
					return -1

		else:
			#if self.state == DecodeState.T0 or self.state == DecodeState.OK:
			return -1

		return interpret_res

	def GotBit(self,value):
		#print 'Got bit ' + str(value)
		self.total_bits += 1
		olddata = self.data[self.pos]
		self.data[self.pos] = (olddata >> 1) | (value << 7)
		self.bits += 1
		if (self.bits >= 8):
			self.data.append(0)
			self.bits = 0
			self.pos += 1
			RfLogger.debug("new byte from V3 @ %d: 0x%x" % ((self.pos - 1),self.data[self.pos - 1]))
		self.state = DecodeState.OK	

class X2DDecoder(DecodeOOK):
#Deltadore use biphase mark coding
#see patent EP 1160752 B1
#http://www.google.com/patents/EP1160752B1?cl=fr

    ENDFRAME=0x1FE
    FRAME_STATE_UNKNOWN="unknown"
    FRAME_STATE_FRAME_END_FOUND="ended"
    
    def __init__(self):
        self.rawdata = []
        self.rawcnt = 0
        DecodeOOK.__init__(self)
        self.syncword_detector = 0x00
        self.oneCounter=0
        self.lastcrc=0
        self.Reset()

    def resetRawData(self):
        self.rawdata = []
        self.rawcnt = 0;
        
    def Restart(self):
        self.pos = 0
        self.data = []
        self.data.append(0)
        self.state = DecodeState.T1
        self.syncword_detector = 0x00
        self.doaverage = False
        self.flip = 1
        self.resetRawData()
        self.oneCounter=0
        self.syncState = X2DDecoder.FRAME_STATE_UNKNOWN
        self.message = None

    def Reset(self):
        DecodeOOK.Reset(self)
        self.resetRawData()
        self.syncword_detector=0x00
        self.longpulse_average = 0
        self.shortpulse_average = 0
        self.longmax = 460
        self.longmin = 307
        self.shortmax = 250
        self.shortmin = 134
        self.nbFrames = 0
        self.doaverage = True
        self.oneCounter=0
        self.syncState = X2DDecoder.FRAME_STATE_UNKNOWN
        self.message = None

# Champ Adresse Maison       2 octets    0 a FFFFh
# Champ Adresse Source       1 octet     0 a FFh
# Champ Adresse Destinataire 1 octet     0 a FFh
# Champ Adresse Transmetteur 1 octet     0 a FFh
# Champ de controle          1 octet
# Champ d information        N octet(s)  N compris entre 0 et 8
# Champ de verification      2 octets

    def hasNewCRC(self,nbytes):
        if (nbytes<2):
            return
        acc_crc=int(0)
        transmitted_crc=int(self.data[nbytes-2]<<8 | self.data[nbytes-1])
        for i in range(nbytes-2):
            acc_crc += int(self.data[i])
        computed_crc = int(~(acc_crc)+1) & 0xFFFF
        if transmitted_crc == computed_crc and int(self.lastcrc) != int(transmitted_crc):
            self.lastcrc = transmitted_crc
            RfLogger.debug("================================")
            RfLogger.debug("Have new crc 0x%x"%computed_crc)
            
            return True
        else:
            return False
    
    def getSenderId(self):
        return (self.data[2]>>6)<<16 | self.data[1]<<8 | self.data[0]
    
    def getType(self):
        return self.data[2] & 0x3F
    
    def dumpBytes(self,nbytes):
        for i in range(nbytes):
            RfLogger.debug("byte from X2D @ %d: 0x%02x" % (i,self.data[i]))
            
    def decodeInfo(self,type):
        
        battery_low = (self.data[4] & 0x10 == 0x10)
        box_opened = (self.data[4] & 0x20 == 0x20)
        device_core_info = (self.data[4] & 0x40 == 0x40)
        paring_requested = (self.data[4] & 0x80 == 0x80)
        answer_request = (self.data[5] & 0x4 == 0x4)
        
        if (paring_requested):
            RfLogger.debug("Pairing Requested")
        if (battery_low):
            RfLogger.debug("Battery Low")
        else:
            RfLogger.debug("Battery OK")
        if (box_opened):
            RfLogger.debug("Box open")
        else:
            RfLogger.debug("Box closed")
        if (answer_request):
            RfLogger.debug("Answer request")
        else:
            RfLogger.debug("No Answer request")        
        
        if (type == 33):
            RfLogger.debug("Volumetric Infrared")
            if (device_core_info):
                RfLogger.debug("Mvt detected")
            else:
                RfLogger.debug("No mvt detected")
        elif (type == 41):
            RfLogger.debug("Perimetric Contact")
            if (device_core_info):
                RfLogger.debug("Open")
            else:
                RfLogger.debug("Close")
        elif (type == 52):
            RfLogger.debug("Smoke Detector")
            if (device_core_info):
                RfLogger.debug("Smoke")
            else:
                RfLogger.debug("No Smoke")
        elif (type == 21 ):
            rolling = int(self.data[6]<<8 | self.data[7])
            state = self.data[3]
            state_info="unkwown"
            if (state == 0):
                state_info = "OFF"
            elif (state == 1):
                state_info = "Zone All"
            else:
                state_info = str(self.data[3])                
            RfLogger.debug("2 Buttons Alarm Remote")
            RfLogger.debug("State " + state_info)
            RfLogger.debug("Rolling 0x%x"%rolling)
        elif (type == 22):
            rolling = int(self.data[6]<<8 | self.data[7])
            RfLogger.debug("4 Buttons Alarm Remote")
            state = self.data[3]
            state_info="unkwown"
            if (state == 0):
                state_info = "OFF"
            elif (state == 1):
                state_info = "Zone All"
            elif (state == 2):
                state_info = "Zone 1"
            elif (state == 3):
                state_info = "Zone 2"
            else:
                state_info = str(self.data[3])
            RfLogger.debug("State " + state_info)
            RfLogger.debug("Rolling 0x%x"%rolling)
        elif (type == 13):
            RfLogger.debug("Tydom Panel Controler")
        else:
            RfLogger.debug("Unknwown type " + str(type))

    def TryInterpret(self):
        if (self.message==None and self.hasNewCRC(self.pos)):
            id = self.getSenderId()
            type = self.getType()
            self.dumpBytes(self.pos)
            self.message = DecodedMessage(vendorName='X2D',productId=id,deviceId=type)
            self.decodeInfo(type)
            data = "%01x"%((self.data[3]&0xf8))
            data = data + "%02x%02x%02x"%(self.data[4],self.data[5],self.data[6])                    
            self.message.append(DecodedInfo(type=MessageType.UNKNOWN,val=data))
        return
    
    def Decode (self,width):
        #if self.state == DecodeState.T0 or self.state == DecodeState.OK:
            #print "at %d have 0x%x" % (self.pos,self.data[self.pos])
        interpret_res = 0;
        if (self.shortmin <= width and width < self.shortmax) or (self.longmin <= width and width < self.longmax):
            longpulse = (self.longmin <= width and width < self.longmax)
            if self.state == DecodeState.UNKNOWN:
                #first detect a minimum of 7 long pulse
                #    __    __    __
                # __|  |__|  |__|  |__
                if (longpulse == 1):
                    if (self.flip == 0):
                        self.longpulse_average = width
                    else:
                        self.longpulse_average = ((self.longpulse_average*3)+width)/4
                    # long pulse
                    self.flip += 1
                    self.GotRawBit(0)
                elif (7 <= self.flip):
                    #if we have at leat 7 long pulse and then a short pulse, start receiving short pulse
                    #   __    __    __    _         
                    #__|  |__|  |__|  |__| |
                    #print 'have unknown short pulse '
                    # Long pulse, start 12 short
                    self.flip = 1
                    self.state = DecodeState.T1
                    self.shortpulse_average = width
                else:
                    #Reset decoder
                    #print 'reset unknown decoder'
                    return -1
            elif self.state == DecodeState.T1:
                #now count short pulse
                if (longpulse == 0):
                    if (self.rawcnt == 0):
                        self.GotRawBit(1)
                        self.rawcnt = 1
                    else:
                        self.rawcnt = 0
                        
                    # short pulse
                    self.flip += 1
                    if self.doaverage == True:
                        self.shortpulse_average = ((self.shortpulse_average*3)+width)/4
                    #print 'have T1 short pulse ' + str(self.flip)
                elif (12 <= self.flip):
                    #once we had 12 short pulses, a X2D sync pattern is here
                    #RfLogger.debug('Have X2D start bit at ' + str(datetime.datetime.now()))
                    #RfLogger.debug('flip is ' + str(self.flip) + " longpulse is " + str(longpulse))
                    #RfLogger.debug("long=%d short=%d" % ( self.longpulse_average, self.shortpulse_average ))
                    self.GotRawBit(0)
                    if self.doaverage == True:
                        self.longmax = self.longpulse_average * 1.25
                        self.longmin = self.longpulse_average * 0.75
                        self.shortmax = self.shortpulse_average * 1.28
                        self.shortmin = self.shortpulse_average * 0.72
                    self.flip = 1
                    self.state = DecodeState.OK
                else:
                    #Reset decoder
                    #print 'reset unknown d
                    self.GotRawBit(0)
                    return -1               
            elif self.state == DecodeState.OK: 
                #decode thanks to Biphase mark coding
                #2 successive short pulse = 1
                #1 long pulse = 0
                #now decode bits
                if (longpulse == 0):
                    # Short pulse
                    self.state = DecodeState.T0
                else:
                    # Long pulse
                    #print 'X2D add 1 ' + str(width)
                    self.GotRawBit(0)
                    self.GotBit(0)
                    self.TryInterpret()
            elif self.state == DecodeState.T0:
                if (longpulse == 0):
                    #print 'X2D add 0 ' + str(width)
                    # Second short pulse
                    self.GotRawBit(1)
                    self.GotBit(1)
                    self.TryInterpret()
                else:
                    RfLogger.debug('X2D reset ' + str(width))
                    # Reset decoder
                    return -1
        else:
            if self.state == DecodeState.T0 or self.state == DecodeState.OK or self.state == DecodeState.OK:
                RfLogger.debug('reset ' + str(self.state) + ' / ' + str(width))
            return -1

        return (self.syncState == X2DDecoder.FRAME_STATE_FRAME_END_FOUND)

    def syncWordDetector(self,value):
        
        self.syncword_detector = value | (self.syncword_detector << 1)
        self.syncword_detector &= 0x1FF
        #RfLogger.debug("sync: %d %02x"%(value,self.syncword_detector))
        
        if self.syncword_detector == X2DDecoder.ENDFRAME:
            self.syncState = X2DDecoder.FRAME_STATE_FRAME_END_FOUND
        else:
            self.syncState = X2DDecoder.FRAME_STATE_UNKNOWN
        return 
    
    def GotRawBit(self,value):
        self.rawdata.append(value)
    
    def GotBit(self,value):

        self.syncWordDetector(value)
        if self.syncState == X2DDecoder.FRAME_STATE_FRAME_END_FOUND:
            #RfLogger.debug('End Frame %d'%(self.nbFrames))
            #if len(self.rawdata)>8:
                #RfLogger.debug("GotRawBit " + str(self.rawdata))
                nothing_todo=0
        else:
            if self.oneCounter==5 and value==0:
                #RfLogger.debug("remove a 0 bit after 5 successive one: " + str(self.pos) + " # " + str(value))
                self.state = DecodeState.OK
                self.oneCounter=0
                return

            self.total_bits += 1
            olddata = self.data[self.pos]
            #self.data[self.pos] = value | (olddata << 1)
            self.data[self.pos] = value<<7 | (olddata >> 1)
            #RfLogger.debug("data: %d %02x"%(value,self.data[self.pos]))
            self.bits += 1
            if (self.bits >= 8):
                self.bits = 0
                self.data.append(0)
                self.pos += 1
                #RfLogger.debug("new byte from X2D @ %d: 0x%02x" % ((self.pos - 1),self.data[self.pos - 1]))
        self.state = DecodeState.OK

        if value==1:
            self.oneCounter+=1
        else:
            self.oneCounter=0

    def Done(self):
        self.state = DecodeState.DONE 
