# THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
# APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
# HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
# OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
# IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
# ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
# WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
# THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
# GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
# USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
# DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
# PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
# EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGES.

# Copyright (c) 2018 Dr. Oliver Barth <info@drb-electronic.de>
# i2c driver for MCP23017

from threading import Lock

regsMCP23017 = {
	'IODIRA':0x00,   #i/o direction register
	'IODIRB':0x01,
	'IPOLA':0x02,    #input polarity register
	'IPOLB':0x03,
	'GPINTENA':0x04, #interrupt-on-change register
	'GPINTENB':0x05,
	'DEFVALA':0x06,  #default value register
	'DEFVALB':0x07,
	'INTCONA':0x08,  #interrupt control register
	'INTCONB':0x09,
	'ICONA':0x0A,    #io expander configuration register
	'ICONB':0x0B,
	'GPPUA':0x0C,    #gpio pull-up resisitor register
	'GPPUB':0x0D,
	'INTFA':0x0E,    #interupt flag register
	'INTFB':0x0F,
	'INTCAPA':0x10,  #interupt capture register
	'INTCAPB':0x11,
	'GPIOA':0x12,    #gpio port register
	'GPIOB':0x13,
	'OLATA':0x14,    #output latch register
	'OLATB':0x15
	}
	
IOexp_MCP23017_keywords = ['self.ioexp.setReg(reg,val)', 'self.ioexp.getReg(reg)', 'self.ioexp.setDir(dira,dirb)',
	'self.ioexp.setOutput(outa,outb)', 'self.ioexp.getInputAsBoolArray()', 'self.ioexp.getInputAsInt()']

#
class IOexp_MCP23017:
	#
	def __init__(self, i2c_address, active, bus):
		self.i2c_address = i2c_address
		self.active = active
		self.i2c_bus = bus
		self.blow = 0x00
		self.bhigh = 0x00
		self.din = [False, False, False, False, False, False, False, False,
				False, False, False, False, False, False, False, False]
		if (self.active == 1):
			# set direction to input
			self.setDir(0xFF, 0xFF)
			# enable pullups
			self.i2c_bus.write_byte_data(self.i2c_address, regsMCP23017['GPPUA'], 0xFF)
			self.i2c_bus.write_byte_data(self.i2c_address, regsMCP23017['GPPUB'], 0xFF)
		self.mutex = Lock()

	#
	def setReg(self, reg, val):
		if (self.active == 1):
			self.i2c_bus.write_byte_data(self.i2c_address, reg, val)

	#
	def getReg(self, reg):
		if (self.active == 1):
			b = self.i2c_bus.read_byte_data(self.i2c_address, reg)
		else:
			b = 0
		return b

	#
	def setDir(self, dira, dirb):
		if (self.active == 1):
			self.setReg(regsMCP23017['IODIRA'], dira)
			self.setReg(regsMCP23017['IODIRB'], dirb)
	
	#
	def setOutput(self, outa, outb):
		if (self.active == 1):
			self.setReg(regsMCP23017['GPIOA'], outa)
			self.setReg(regsMCP23017['GPIOB'], outb)
		return

	#
	def update(self):
		if (self.active == 1):
			self.mutex.acquire()
			self.blow = self.i2c_bus.read_byte_data(self.i2c_address, 0x13)
			self.bhigh = self.i2c_bus.read_byte_data(self.i2c_address, 0x12)
			self.din[0] = ((self.blow & 0x01) != 0x00)
			self.din[1] = ((self.blow & 0x02) != 0x00)
			self.din[2] = ((self.blow & 0x04) != 0x00)
			self.din[3] = ((self.blow & 0x08) != 0x00)
			self.din[4] = ((self.blow & 0x10) != 0x00)
			self.din[5] = ((self.blow & 0x20) != 0x00)
			self.din[6] = ((self.blow & 0x40) != 0x00)
			self.din[7] = ((self.blow & 0x80) != 0x00)
			self.din[8] = ((self.bhigh & 0x01) != 0x00)
			self.din[9] = ((self.bhigh & 0x02) != 0x00)
			self.din[10] = ((self.bhigh & 0x04) != 0x00)
			self.din[11] = ((self.bhigh & 0x08) != 0x00)
			self.din[12] = ((self.bhigh & 0x10) != 0x00)
			self.din[13] = ((self.bhigh & 0x20) != 0x00)
			self.din[14] = ((self.bhigh & 0x40) != 0x00)
			self.din[15] = ((self.bhigh & 0x80) != 0x00)
			self.mutex.release()

	# return port A and port B bytes as array 16 of bool 
	def getInputAsBoolArray(self):
		self.mutex.acquire()
		ret = self.din
		self.mutex.release()
		return ret
		
	# return port A and port B as int16
	def getInputAsInt(self):
		self.mutex.acquire()
		ret = self.blow | (self.bhigh << 8)
		self.mutex.release()
		return ret

