import pygame
import os
import subprocess
import pigpio
from threading import Timer,Thread,Event
from time import sleep
from signal import alarm, signal, SIGALRM, SIGKILL

# Overlays path
overlays_path = os.path.dirname(os.path.realpath(__file__)) + '/overlays/'
notifications_color = (210, 210, 210)

# Screen visible width, height anf offset
screen_width     = 126
screen_height    = 158
screen_start_pos = (1,1)
notifications_height = 30

# LCD dimensions
lcd_width     = 128
lcd_height    = 160


# set up the colors
BLACK = (  0,   0,   0)
WHITE = (255, 255, 255)
RED   = (255,   0,   0)
GREEN = (  0, 255,   0)
BLUE  = (  0,   0, 255)
POPUP = ( 59, 230, 240)
NAME  = (255, 255, 255)


############################################
# Interface Commands
############################################
WIFI_ENABLE = 1
WIFI_STATUS = 2
LORA_ENABLE = 3
LORA_STATUS = 4
LOGO        = 5
COUNTS      = 6
ID          = 7
ID_TIME     = 8
BEEP_ENABLE = 9
BEEP_FREQ   = 10
MAKE_BEEP   = 11
POPUP_TEXT  = 12
DISPLAY_POPUP= 13
COMPANY_NAME= 14

############################################
# Internal status interface
############################################
_status = {}
_status[WIFI_ENABLE] = False
_status[WIFI_STATUS] = -1
_status[LORA_ENABLE] = False
_status[LORA_STATUS] = -1
_status[LOGO       ] = overlays_path + 'logo.png'
_status[COUNTS     ] = 0
_status[ID         ] = "ID"
_status[ID_TIME    ] = 3
_status[BEEP_ENABLE] = True
_status[BEEP_FREQ  ] = 4200
_status[POPUP_TEXT ] = ""
_status[COMPANY_NAME]= ""

class Interface():

	class updateTimer():
		def __init__(self,t1, hFunction1,t2, hFunction2):
			self.t1=t1
			self.hFunction1 = hFunction1
			self.t2=t2
			self.hFunction2 = hFunction2
			self.thread= Timer(self.t1,self.handle_function1)

		def handle_function1(self):
			self.hFunction1()
			self.thread = Timer(self.t2,self.handle_function2)
			self.thread.daemon = True
			self.thread.start()

		def handle_function2(self):
			self.hFunction2()
			self.thread = Timer(self.t1,self.handle_function1)
			self.thread.daemon = True
			self.thread.start()

		def start(self):
			self.thread.daemon = True
			self.thread.start()

		def cancel(self):
			self.thread.cancel()

	class singleTimer():
		def __init__(self,t, hFunction):
			self.t=t
			self.hFunction = hFunction
			self.thread= Timer(self.t,self.handle_function)
			self.thread.daemon = True
			self.thread.start()

		def handle_function(self):
			self.hFunction()
			self.thread.cancel()

		def cancel(self):
			self.thread.cancel()

	class lcdInterface():
		def __init__(self):


			drivers = ['fbcon', 'directfb', 'svgalib']
			found = False
			for driver in drivers:
				# Make sure that SDL_VIDEODRIVER is set
				if not os.getenv('SDL_VIDEODRIVER'):
					os.putenv('SDL_VIDEODRIVER', driver)
					os.putenv('SDL_FBDEV', '/dev/fb1')
				try:
					pygame.display.init()
				except pygame.error:
					print 'Driver: {0} failed.'.format(driver)
					continue
				found = True
				break

			if not found:
				raise Exception('No suitable video driver found!')



			#pygame.init()
			#pygame.mouse.set_visible(False)

						# this section is an unbelievable nasty hack - for some reason Pygame
			# needs a keyboardinterrupt to initialise in some limited circs (second time running)
			class Alarm(Exception):
				pass
			def alarm_handler(signum, frame):
				raise Alarm
			signal(SIGALRM, alarm_handler)
			alarm(3)

			try:
				pygame.init()
				pygame.mouse.set_visible(False)
				self.lcd = pygame.display.set_mode((lcd_width, lcd_height), pygame.FULLSCREEN| pygame.DOUBLEBUF | pygame.HWSURFACE)
				alarm(0)
			except Alarm:
				raise KeyboardInterrupt

			#self.lcd = pygame.display.set_mode((lcd_width, lcd_height), pygame.FULLSCREEN| pygame.DOUBLEBUF | pygame.HWSURFACE)
			#bootImg=pygame.image.load('/etc/splash.png')
			#self.lcd.blit(bootImg,(0,0))
			#pygame.display.update()

			self.lcd.fill(WHITE)
			#pygame.display.update()

			# Start fonts
			pygame.font.init()

			#Create a visible windows (screen)
			self.screen = pygame.surface.Surface((screen_width, screen_height))
			self.screen.fill(WHITE)

			self.popup = pygame.surface.Surface((screen_width-20, screen_height-notifications_height-20))
			self.popupPos = (screen_start_pos[0]+10, screen_start_pos[1]+10)

			notifications_fill = pygame.surface.Surface((lcd_width, notifications_height + (lcd_height-screen_height)))
			notifications_fill.fill(notifications_color)
			self.lcd.blit(notifications_fill, (0, screen_height - notifications_height + screen_start_pos[1]))

			#Create 'Notification Bar' and 'Logo' areas
			self.notifications = pygame.surface.Surface((screen_width, notifications_height))
			self.logo = pygame.surface.Surface((screen_width, screen_height - notifications_height - 4))

			self.name = pygame.surface.Surface((screen_width-10, 40))
			self.namePos = (screen_start_pos[0]+5, screen_height-notifications_height-45)

			self.updateScreen('0')

		def stop(self):
			pygame.quit()

		######################################
		def updateLogo(self, surface, path ):
			#load image
			image = pygame.image.load(path).convert_alpha()

			#scale image
			scale_x = 1.0*surface.get_width()/image.get_width()
			scale_y = 1.0*surface.get_height()/image.get_height()
			if(scale_x < scale_y): scale = scale_x
			else                 : scale = scale_y
			image = pygame.transform.smoothscale(image, (int(image.get_width()*scale), int(image.get_height()*scale)))

			#center horizontally and vertically
			x=0
			y=0
			x = (surface.get_width()-image.get_width())/2
			y = (surface.get_height()-image.get_height())/2

			surface.blit(image, (x,y))
			return

		def displayName(self, surface, text):

			pos = (0,0)
			self.name.fill(NAME)
			font = pygame.font.SysFont('Arial', 19)
			color=pygame.Color('black')

			words = [word.split(' ') for word in text.splitlines()]  # 2D array where each row is a list of words.
			space = font.size(' ')[0]  # The width of a space.
			max_width, max_height = self.name.get_size()
			textSurface = pygame.surface.Surface((max_width, max_height))
			textSurface.fill(NAME)

			max_x_row = [0]*(len(words) + 3)

			x, y = pos
			row  = 0

			for line in words:
				for word in line:
					word_surface = font.render(word, 0, color)
					word_width, word_height = word_surface.get_size()
					if x + word_width >= max_width:
						x = pos[0]  # Reset the x.
						y += word_height  # Start on new row.
						if y >= max_height: break
						row = row + 1
					x += word_width + space
					if x > max_x_row[row] : max_x_row[row] = x

				x =pos[0]  # Reset the x.
				y += word_height  # Start on new row.
				if y >= max_height: break
				row = row + 1

			x,y = pos
			row = 0
			x = (max_width-max_x_row[row])/2

			for line in words:
				for word in line:
					word_surface = font.render(word, 0, color)
					word_width, word_height = word_surface.get_size()
					if x + word_width >= max_width:
						row = row + 1
						x = (max_width-max_x_row[row])/2
						y += word_height  # Start on new row.
						if y >= max_height: break
					textSurface.blit(word_surface, (x, y))
					x += word_width + space

				x =pos[0]  # Reset the x.
				y += word_height  # Start on new row.
				if y >= max_height: break
				row = row + 1
				x = (max_width-max_x_row[row])/2

			self.name.blit(textSurface, (0, (max_height-y)/2))
			surface.blit(self.name, self.namePos)




		def updateNotifications(self, surface, path, wifi_visibility, wifi, lora_visibility, lora, message = ''):

			x_offset = 2;
			#update text and align to right
			myfont = pygame.font.SysFont('Arial', 18)
			text = myfont.render(message, True, BLACK)
			text_rect = text.get_rect()
			text_rect.right = surface.get_width()
			surface.blit(text, (text_rect.x - 2, ((surface.get_height()-text_rect.height)/2 + 1) ) )

			#load wifi icons if enabled (Left aligned)
			if(wifi_visibility):
				if(wifi >= 0 and wifi <= 4):
					wifi_icon = pygame.image.load(path + 'wifiSignal' + str(wifi) + '.png').convert_alpha()
				else:
					wifi_icon = pygame.image.load(path + 'wifiNoInterface.png').convert_alpha()

				surface.blit(wifi_icon, (x_offset,1))
				x_offset = x_offset + 28 - 2

			#load Lora icons if enabled (Left aligned)
			if(lora_visibility):
				if(lora == 0):
					lora_icon = pygame.image.load(path + 'lora.png').convert_alpha()
				elif(lora == 1):
					lora_icon = pygame.image.load(path + 'loraConnect.png').convert_alpha()
				else:
					lora_icon = pygame.image.load(path + 'loraNoInterface.png').convert_alpha()

				surface.blit(lora_icon, (x_offset,1))
				x_offset = x_offset + 28 - 2

			return


		def updateScreen(self, s):
			global _status
			self.logo.fill(WHITE)

			try:
				self.updateLogo(self.logo, _status[LOGO])
			except Exception, e:
				self.updateLogo(self.logo, overlays_path + 'logo.png')

			if _status[COMPANY_NAME] != '':
				self.displayName(self.logo, _status[COMPANY_NAME])

			self.notifications.fill(notifications_color)
			self.updateNotifications(self.notifications, overlays_path, _status[WIFI_ENABLE], _status[WIFI_STATUS], _status[LORA_ENABLE], _status[LORA_STATUS], s)

			self.screen.blit(self.logo,(  0,  0))
			self.screen.blit(self.notifications,(  0, screen_height - notifications_height))

			self.lcd.blit(self.screen, screen_start_pos)
			pygame.display.update()

		def updateNotificationBar(self, s):
			global _status

			self.notifications.fill(notifications_color)
			self.updateNotifications(self.notifications, overlays_path, _status[WIFI_ENABLE], _status[WIFI_STATUS], _status[LORA_ENABLE], _status[LORA_STATUS], s)

			self.screen.blit(self.notifications,(  0, screen_height - notifications_height))

			self.lcd.blit(self.screen, screen_start_pos)
			pygame.display.update()

		def popUp(self, s):
			pos = (0,0)
			self.popup.fill(POPUP)
			font = pygame.font.SysFont('Arial', 19)
			color=pygame.Color('black')

			words = [word.split(' ') for word in s.splitlines()]  # 2D array where each row is a list of words.
			space = font.size(' ')[0]  # The width of a space.
			max_width, max_height = self.popup.get_size()
			textSurface = pygame.surface.Surface((max_width, max_height))
			textSurface.fill(POPUP)

			max_x_row = [0]*(len(words) + 3)

			x, y = pos
			row  = 0

			for line in words:
				for word in line:
					word_surface = font.render(word, 0, color)
					word_width, word_height = word_surface.get_size()
					if x + word_width >= max_width:
						x = pos[0]  # Reset the x.
						y += word_height  # Start on new row.
						if y >= max_height: break
						row = row + 1
					x += word_width + space
					if x > max_x_row[row] : max_x_row[row] = x

				x =pos[0]  # Reset the x.
				y += word_height  # Start on new row.
				if y >= max_height: break
				row = row + 1

			x,y = pos
			row = 0
			x = (max_width-max_x_row[row])/2

			for line in words:
				for word in line:
					word_surface = font.render(word, 0, color)
					word_width, word_height = word_surface.get_size()
					if x + word_width >= max_width:
						row = row + 1
						x = (max_width-max_x_row[row])/2
						y += word_height  # Start on new row.
						if y >= max_height: break
					textSurface.blit(word_surface, (x, y))
					x += word_width + space

				x =pos[0]  # Reset the x.
				y += word_height  # Start on new row.
				if y >= max_height: break
				row = row + 1
				x = (max_width-max_x_row[row])/2

			self.popup.blit(textSurface, (0, (max_height-y)/2))

			self.screen.blit(self.popup, self.popupPos)

			self.lcd.blit(self.screen, screen_start_pos)
			pygame.display.update()


	class beepInterface():
		def __init__(self):
			#subprocess.call('pigpiod', shell=True)
			#create crontab
			self.pg = pigpio.pi()
			self.setFreq()

		def setFreq(self):
			global _status
			self.pg.set_PWM_frequency(17, _status[BEEP_FREQ])
			self.pg.set_PWM_dutycycle(17, 0)

		def beep(self):
			global _status
			if _status[BEEP_ENABLE]:
				self.setFreq()
				self.pg.set_PWM_dutycycle(17, 127)
				sleep(0.1)
			self.pg.set_PWM_dutycycle(17, 0)

	###################################################################################
	#                INTERFACE
	###################################################################################
	def __init__(self):
		global _status
		self.lcd_interface = self.lcdInterface()
		self.beep_interface = self.beepInterface()

		self.threadInterfacePopup = None
		self.waitInteraction = False # to keep the popup message

		self.threadInterface = self.updateTimer(_status[ID_TIME], self.updateScreenID, 1, self.updateScreenCount)
		self.threadInterface.start()

	def stop(self):
		self.lcd_interface.stop()
		self.threadInterface.cancel()

	def updateScreen(self):
		global _status
		self.lcd_interface.updateScreen(str(_status[COUNTS]))
		self.waitInteraction = False

	def updateScreenCount(self):
		global _status
		self.lcd_interface.updateNotificationBar(str(_status[COUNTS]))

	def updateScreenID(self):
		global _status
		self.lcd_interface.updateNotificationBar(str(_status[ID]))

	def updateScreenPopUp(self, t=-1):
		global _status
		self.lcd_interface.popUp(str(_status[POPUP_TEXT]))

		if self.threadInterfacePopup != None:
			self.threadInterfacePopup.cancel()


		#just update the screen
		if t == -1:
			self.waitInteraction = False
			return

		#will wait for a card
		if t == 0:
			self.waitInteraction = True
			return

		self.waitInteraction = False
		#disable previous timer

		#starts a new timer
		self.threadInterfacePopup = self.singleTimer(t, self.updateScreen)

	############################################
	# WIFI_ENABLE, WIFI_STATUS
	############################################
	def setWifi(self, enable=False, status=-1):
		global _status
		_status[WIFI_ENABLE] = enable
		_status[WIFI_STATUS] = status
		self.updateScreenCount()

	def getWifiStatus(self):
		global _status
		return _status[WIFI_STATUS]

	def getWifiEnable(self):
		global _status
		return _status[WIFI_ENABLE]


	############################################
	# LORA_ENABLE
	############################################
	def setLora(self, enable=False, status=-1):
		global _status
		_status[LORA_ENABLE] = enable
		_status[LORA_STATUS] = status
		self.updateScreenCount()

	def getLoraStatus(self):
		global _status
		return _status[LORA_STATUS]

	def getLoraEnable(self):
		global _status
		return _status[LORA_ENABLE]

	############################################
	# LOGO
	############################################
	def setLogo(self, s=""):
		global _status
		_status[LOGO] = s
		self.updateScreenCount()

	def getLoraStatus(self):
		return _status[LOGO]

	############################################
	# COUNTS
	############################################
	def setCounts(self, c=0):
		global _status
		_status[COUNTS] = c
		self.updateScreenCount()

	def getCounts(self):
		global _status
		return _status[COUNTS]

	############################################
	# ID
	############################################
	def setId(self, s=""):
		global _status
		_status[ID] = s
		self.updateScreenCount()

	def getId(self):
		return _status[ID]

	############################################
	# ID_TIME
	############################################
	def setIdTime(self, c=0):
		global _status
		_status[ID_TIME] = c
		self.updateScreenCount()

	def getIdTime(self):
		global _status
		return _status[ID_TIME]

	############################################
	# BEEP_ENABLE
	############################################
	def setBeep(self, b=False):
		global _status
		_status[BEEP_ENABLE] = b

	def getBeep(self):
		global _status
		return _status[BEEP_ENABLE]

	############################################
	# BEEP_FREQ
	############################################
	def setBeepFreq(self, c=4200):
		global _status
		_status[BEEP_FREQ] = c

	def getBeepFreq(self):
		global _status
		return _status[BEEP_FREQ]

	############################################
	# MAKE_BEEP
	############################################
	def makeBeep(self):
		self.beep_interface.beep()

	############################################
	# POPUP_TEXT
	############################################
	def setPopup(self, s="", t=0):
		global _status
		_status[POPUP_TEXT] = s
		self.updateScreenPopUp(t)

	############################################
	# COMPANY_NAME
	############################################
	def setCompanyName(self, s=""):
		global _status
		_status[COMPANY_NAME] = s
		self.updateScreen()


	#functions

	#gui.setWifi(True, 1)
	#gui.getWifiStatus()
	#gui.getWifiEnable()
	#gui.setLora( True, 1)
	#gui.getLoraStatus()
	#gui.getLoraEnable()
	#gui.setLogo("a")
	#gui.getLoraStatus()
	#gui.setCounts(0)
	#gui.getCounts()
	#gui.setId("ID: 0000")
	#gui.getId()
	#gui.setIdTime(1)
	#gui.getIdTime()
	#gui.setCounts(False)
	#gui.getCounts()
	#gui.setBeepFreq(4200)
	#gui.getBeepFreq()
	#gui.makeBeep()
	#gui.setPopup("PopUp")
