import serial
from thread import start_new_thread, allocate_lock
import time
import threading

class Display(threading.Thread):
	def __init__(self,portname="/dev/ttyUSB0"):
	        threading.Thread.__init__(self)
		self.serialport=None
		self.runme=True
		self.idlemessage=None
		self.portlock = allocate_lock()
		self.portname=portname
		self.brightness=5
	        self.scroll_line1 = None
		self.scroll_line2 = None
		self.offset_line1 = 0
		self.offset_line2 = 0
		self.screensaver = Screensaver(self)
		self.open_port()

	def __del__(self):
		if (not self.screensaver == None):
			self.screensaver = None

		if (not self.serialport == None):
			self.serialport.close()
			self.serialport=None

	def open_port(self):
		try:
			self.serialport = serial.Serial(self.portname,9600,timeout=2,rtscts=False, dsrdtr=False)
			#print ("Initializing display on port:\n  %s\n" % self.serialport )
			self.mutex_get()
			self.cmd_reset()
			self.mutex_release()
		except Exception as e:
			#print ("Exception opening serial port '%s' for display\n" % self.portname)
			#print ("Ignoring and trying to open it again later\n")
			#print (e)
			self.serialport = None
		

	#Helper-Function, scrolls text in a specific line.
	def display_scroll_text(self,line,text):
		if (line==1): #clear the line on invocation:
			self.cmd("\x1f\x24\x01%c\x18" % chr(line) )
			self.scroll_line1 = text
			self.offset_line1=0
		if (line==2): #clear the line on invocation:
			self.cmd("\x1f\x24\x01%c\x18" % chr(line) )
			self.scroll_line2 = text
			self.offset_line2=0


	def display_handle_scroll(self,line,text,offset):
		if (text):
			l = len(text)
			if (l<21):
				if (offset == 0):
					self.cmd("\x1f\x24\x01%c%s" % (chr(line),text[offset:(20+offset)]))
					offset=1
			else:   
				self.cmd("\x1f\x24\x01%c%s" % (chr(line),text[offset:(20+offset)]))
				missing_chars=20+offset-l
				if (missing_chars>0):
					self.cmd(text[:missing_chars])
				offset=((offset+1)%l)
		else:   
			offset=0        #reset offset
		return offset


	def run(self):
		#print "Starting Display thread" 
		self.screensaver.start()
		while(self.runme):
			#print("display thread loop\n")
			self.mutex_get()
			#print("display got mutex and handles scroll\n")
			self.offset_line1 = self.display_handle_scroll(1,self.scroll_line1,self.offset_line1)
	                self.offset_line2 = self.display_handle_scroll(2,self.scroll_line2,self.offset_line2)
			self.mutex_release()
			time.sleep(.5)
		print "Exiting Display thread"




	#Front-End Funtion to display a Screen
	# with heading
	def display_screen(self,title,message):
		self.mutex_get()

	        self.offset_line1=0
	        self.offset_line2=0
	        self.screensaver.idle_reset();
	        self.cmd_brightness(5)

	        if (len(title)<21):
	                self.scroll_line1=None
	                self.cmd("\x1f\x24\x01%c\x18%s" % (chr(1),'\xdb'*20) )
	                if (len(title)<20):
	                        pos=1+(20-len(title))/2
	                else:
	                        pos=1
	                self.cmd("\x1f\x24%c%c%s" % (chr(pos),chr(1),title) )
	        else:
	                self.display_scroll_text(1,title)
	
	        self.display_scroll_text(2,message)
		self.mutex_release()




	def cmd_reset(self):
		#reset the display
		self.cmd("\x1b\x40")

	def cmd_cursor_show(self,on=True):
		#show or hide the cursor
		if (on): 
			self.cmd("\x1f\x43\x01")
		else:
			self.cmd("\x1f\x43\x00")

	def cmd_blink(self,rate=127):
		self.cmd("\x1f\x45%c" % chr(rate))


	def cmd_brightness(self,level):
		if (self.brightness==level):
			return
		#print("setting brightness to %i \n" %level)
		if (level==0): #turn off:
			self.cmd_display_on(False)	
                        self.brightness = 0
                        return
		else:  
			if (self.brightness==0): #turn on, then set wanted brightness:
				self.cmd_display_on(True)	
                        self.brightness = level
                        self.cmd("\x1F\x58%c" % chr(level-1) )

	def cmd_display_on(self,on=True):
		# blink-command: 
		# where the last value defines the blink rate, 
		# with 0=always on, 255=always off
		if (on):
			if (not self.brightness==0):
				return
			#print("setting display to on: %s \n" % str(on))
			self.cmd_blink(0)
			self.brightness=5
		else:
			if (self.brightness==0):
				return
			#print("setting display to on: %s \n" % str(on))
			self.cmd_blink(255)
			self.brightness=0

	def cmd_curmode_overwrite(self):
		self.cmd_curmode(1)			

	def cmd_curmode_vscroll(self):
		self.cmd_curmode(2)			

	def cmd_curmode_hscroll(self):
		self.cmd_curmode(3)			

	def cmd_curmode(self,val=2):
		self.cmd("\x1f%c" % chr(val))

	def cmd_home(self):
		self.cmd("\x0b")

	def cmd_clear(self):
		self.cmd("\x0c")
	
	def cmd_time(self):
		# activate timer display in lower right corner.
		# actual time has to be set via display_set_timer.
		self.cmd("\x1f\x55")

	def cmd_time_set(self,h=None,m=None):
		if (h==None or m==None):
			now = time.localtime()
			h=now.tm_hour
			m=now.tm_min
		# configures hour and minutes of the internal clock and
		# displays the time in the lower right corner.
		# turns off when something gets written into the line.
		# two bytes have to follow with hour and minute
		self.cmd("\x1F\x54%c%c" % (chr(h),chr(m)) )
		self.cmd_curmode_hscroll() # makes the time more robust...

	def cmd(self,text): #for commands: do not reset screensaver timeout
		self.write(text,False)

	def write(self,text,idle_reset=True): #for data: reset screensaver timeout
		if (not self.serialport == None):
			if (idle_reset):
				self.screensaver.idle_reset();
				self.scroll_line1=None
				self.scroll_line2=None
			self.serialport.write(text)	
		else:
			self.open_port()	

	def setIdlemessage(self,text):
		self.idlemessage=text
	
	def getIdlemessage(self):
		return self.idlemessage

	def mutex_get(self):
		self.portlock.acquire()

	def mutex_release(self):
		self.portlock.release()

	def terminate(self):
		self.runme=False
		if (not self.screensaver == None):
			self.screensaver.terminate()
			self.screensaver = None
		

import threading
import time

class Screensaver (threading.Thread):
	def __init__(self,display):
	        threading.Thread.__init__(self)
		self.display=display
		self.runme=True
		self.idlecounter=0
		self.timeout_dim=30
		self.timeout_message=60
		self.timeout_off=300
		self.mutex = allocate_lock()
	        threading.Thread.__init__(self)

	def run(self):
		#print "Starting Screensaver thread" 
		while(self.runme):
			self.mutex.acquire()
			self._main_loop()
			self.mutex.release()
			time.sleep(.5)
		#print "Exiting Screensaver thread"

	def terminate(self):
		self.runme = False


	def idle_reset(self):
		self.mutex.acquire()
		self.idlecounter=0
		self.display.cmd_brightness(5)
		self.mutex.release()


	def _main_loop(self):
		if (self.idlecounter < self.timeout_off):
			self.idlecounter+=1
		if (self.idlecounter>=self.timeout_dim and self.idlecounter <= (self.timeout_dim+7)):
			x = (8-( self.idlecounter - self.timeout_dim))/2
			self.display.mutex_get()
			self.display.cmd_brightness(1+x)
			self.display.mutex_release()
			return

		if (self.idlecounter==self.timeout_message):
			self.display.mutex_get()
			self.display.cmd_time_set()
			self.display.scroll_line1 = self.display.getIdlemessage()
			self.display.scroll_line2 = None
			self.display.mutex_release()
			return

		if (self.idlecounter==self.timeout_off):
			self.display.mutex_get()
                        self.display.cmd_display_on(False)
			self.display.mutex_release()
			return
		
	def __del__(self):
		print("bye")

