#!/usr/bin/env python

#  Manometer (c) Mathieu Villegas 2007 <walloo13@yahoo.ca>
#
# INFO:
# 	This is a simple screenlet which displayCPU and Memory usage using and old Manometer design
# 
# TODO:
# 	Improve design and add functionalities
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 2 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program.  If not, see <http://www.gnu.org/licenses/>.

import screenlets, math
import screenlets.sensors
import commands
from screenlets.options import FloatOption, BoolOption, StringOption, IntOption
from screenlets.options import FontOption, ColorOption
import cairo
import pango
import gobject
import sys
import re
#use gettext for translation
import gettext

_ = screenlets.utils.get_translator(__file__)

def tdoc(obj):
	obj.__doc__ = _(obj.__doc__)
	return obj

@tdoc
class ManometerScreenlet (screenlets.Screenlet):
	"""A Manometer System information for CPU, memory usage and temperatures"""
	p_layout = None 

	# default meta-info for Screenlets
	__name__	= 'Manometer'
	__version__	= '0.15'
	__author__	= 'Mathieu Villegas 2007'
	__desc__	= __doc__

	# internals
	__timeout = None
	
	# settings
	update_interval = 0.2
	show_info = 'cpu'
	step = 5
	last = 0
	new = 0
	cLabel_text=''
	cLabel_color = (0.0,0.0,0.0,0.75)
	cLabel_font = 'Arial 10'
	cLabel_y=0

	iLabel_color = (0.0,0.0,0.0,0.75)
	iLabel_font = 'Arial 10'
	iLabel_y=0

	network_displayInILabel = True
	network_display_bit = False
	network_speed_MB = _('Mbit/s')
	network_speed = 100

	old_cuse = 0
	net_lastValue = 0
	
	# constructor
	def __init__ (self, **keyword_args):
		#call super (and not show window yet)
		screenlets.Screenlet.__init__(self, width=133, height=200, uses_theme=True, **keyword_args)
		# set theme
		self.theme_name = "default"
		# add default menu items
		self.add_default_menuitems()
		# add settings
		self.add_options_group(_('Manometer'), _('Manometer specific options'))
		
		choices = self.get_cpu_list ()
		choices.append(_('Memory usage'))
		choices.append(_('Swap usage'))
		##
		sensors = self.get_sensor_list()
		for sens in sensors:
			choices.append(sens)
		netCards = self.get_network_cards_list()
		for nc in netCards:
			choices.append(_('Network download: ')+nc)
			choices.append(_('Network upload: ')+nc)

		self.add_option(StringOption(_('Manometer'), 'show_info', self.show_info,_('Select information to display'), '',choices),realtime=True)
		self.add_options_group(_('Custom label'), _('Custom label options'))
		self.add_option(StringOption(_('Custom label'), 'cLabel_text',self.cLabel_text, _('Label text'),_('Label text')),realtime=True)
		self.add_option(FontOption(_('Custom label'), 'cLabel_font',self.cLabel_font, _('Label font'), _('Font used to display label ...')))
		self.add_option(ColorOption(_('Custom label'), 'cLabel_color', self.cLabel_color, _('Label color'), _('Color used to display label ...')))
		self.add_option(IntOption(_('Custom label'), 'cLabel_y',self.cLabel_y, _('Label vertical position'),_('Label vertical position'),min=-66, max=150),realtime=True)

		self.add_options_group(_('Information label'), _('Information label options'))
		self.add_option(FontOption(_('Information label'), 'iLabel_font',self.iLabel_font, _('Label font'), _('Font used to display label ...')))
		self.add_option(ColorOption(_('Information label'), 'iLabel_color', self.iLabel_color, _('Label color'), _('Color used to display label ...')))
		self.add_option(IntOption(_('Information label'), 'iLabel_y',self.iLabel_y, _('Label vertical position'),_('Label vertical position'),min=-66, max=150),realtime=True)

		self.add_options_group(_('Network'), _('Network card information'))
		self.add_option(BoolOption(_('Network'), 'network_displayInILabel',self.network_displayInILabel, _('Display in information label:'), _('Display in information label:')))
		self.add_option(IntOption(_('Network'), 'network_speed',self.network_speed, _('Network speed'),_('Set your network speed'),min=1, max=1000000),realtime=True)
		self.add_option(StringOption(_('Network'), 'network_speed_MB', self.network_speed_MB,_('Network speed is set in:'), _('Network speed is set in:'),[_('Mbit/s'),_('Kbit/s')]),realtime=True)
		self.add_option(BoolOption(_('Network'), 'network_displayInILabel',self.network_displayInILabel, _('Display in information label:'), _('Display in information label:')))
		self.add_option(BoolOption(_('Network'), 'network_display_bit',self.network_display_bit, _('Display speed in bit instead of byte:'), _('Display speed in bit instead of byte:')))
		# init the timeout function
		self.update_interval = self.update_interval
	
	# attribute-"setter", handles setting of attributes
	def __setattr__ (self, name, value):
		# call Screenlet.__setattr__ in baseclass (ESSENTIAL!!!!)
		screenlets.Screenlet.__setattr__(self, name, value)
		# check for this Screenlet's attributes, we are interested in:
		if name == "update_interval":
			if value > 0:
				self.__dict__['update_interval'] = value
				if self.__timeout:
					gobject.source_remove(self.__timeout)
				self.__timeout = gobject.timeout_add(int(value * 1000), self.update_graph)
			else:
				self.__dict__['update_interval'] = 1
				pass
	
	# timeout-function
	def update_graph (self):
		self.redraw_canvas()
		return True

	

	def on_draw (self, ctx):
		load = 0
		if self.show_info.startswith(_('CPU load: ')):
			self.update_interval = 0.2
			if self.step >= 5:
				self.last = self.new
				self.new = self.get_selected_cpu_load(self.show_info)
				self.step = 0
			load = self.last+self.step*(self.new - self.last)/5
			self.step = self.step+1
			if self.last == self.new:
				self.update_interval = 1.0
				self.step = 5
				
				
		elif self.show_info == _('Memory usage'):
			load = self.get_usedmem()
			self.update_interval = 3

		elif self.show_info == _('Swap usage'):
			load = self.get_usedswap()
			self.update_interval = 3
		elif self.show_info.startswith(_('Temperature: ')):
			self.update_interval = 10
			load = self.get_sensor_temperature(self.show_info)
		
		elif self.show_info.startswith(_('Network ')):
			self.update_interval = 1
			if(self.show_info.startswith(_('Network download:'))):
				card = self.show_info[17:].strip()
				down = True
			else:
				card = self.show_info[17:].strip()
				down = False
			
			load,self.iLabel_text = self.get_network_load(down,card,self.network_speed,self.network_speed_MB,self.network_display_bit)
		

		if load > 99: load = 99
		elif load < 0: load=0
		
		ctx.scale(self.scale, self.scale)
		# now, draw manometer
		ctx.set_operator(cairo.OPERATOR_OVER)
		if self.theme:
			
			
			#if self.shadow:
			#	ctx.save()
			#	ctx.translate(-3,-3)
			#	self.theme.render(ctx, 'ombrefond')
			#	ctx.restore()
			
			self.theme.render(ctx, 'fond')
			
			if self.cLabel_text != '':
				ctx.save()	
				ctx.translate(0,70+self.cLabel_y)
				if self.p_layout == None : 

                                     self.p_layout = ctx.create_layout() 
                                else: 

                                     ctx.update_layout(self.p_layout)
				ctx.set_source_rgba(self.cLabel_color[0],self.cLabel_color[1],self.cLabel_color[2],self.cLabel_color[3])
				
				p_fdesc = pango.FontDescription(self.cLabel_font)
				self.p_layout.set_font_description(p_fdesc)
				self.p_layout.set_width((self.width) * pango.SCALE)
				self.p_layout.set_markup(self.cLabel_text)
				self.p_layout.set_alignment(pango.ALIGN_CENTER)
				ctx.show_layout(self.p_layout)
				ctx.restore()

			

			if (self.show_info.startswith(_('CPU load: '))):
				if(self.cLabel_text == ''):	
					ctx.save()
					ctx.translate(53,75)
					self.theme.render(ctx, 'cpu')
					ctx.restore()
				ctx.save()
				ctx.translate(77,96)
				self.theme.render(ctx, 'pourcent')
				ctx.restore()
				
			elif (self.show_info == _('Memory usage')):
				if(self.cLabel_text == ''):
					ctx.save()
					ctx.translate(51,75)
					self.theme.render(ctx, 'mem')
					ctx.restore()
				ctx.save()
				ctx.translate(77,96)
				self.theme.render(ctx, 'pourcent')
				ctx.restore()
				
			elif (self.show_info == _('Swap usage')):
				if self.cLabel_text == '':
					ctx.save()
					ctx.translate(50,75)
					self.theme.render(ctx, 'swap')
					ctx.restore()
				ctx.save()
				ctx.translate(77,96)
				self.theme.render(ctx, 'pourcent')
				ctx.restore()
				
				
			elif (self.show_info.startswith(_('Temperature: '))):
				if self.cLabel_text == '':
					ctx.save()
					ctx.translate(50,75)
					self.theme.render(ctx, 'temperature')
					ctx.restore()
				ctx.save()
				ctx.translate(77,96)
				self.theme.render(ctx, 'celcius')
				ctx.restore()
				
				

			elif (self.show_info.startswith(_('Network '))):					
				if self.network_displayInILabel:
					ctx.save()	
					ctx.translate(0,70+self.iLabel_y)
					if self.p_layout == None : 

                                              self.p_layout = ctx.create_layout() 
                                        else: 

                                              ctx.update_layout(self.p_layout)
					ctx.set_source_rgba(self.iLabel_color[0],self.iLabel_color[1],self.iLabel_color[2],self.iLabel_color[3])
				
					p_fdesc = pango.FontDescription(self.iLabel_font)
					self.p_layout.set_font_description(p_fdesc)
					self.p_layout.set_width((self.width) * pango.SCALE)
					self.p_layout.set_markup(self.iLabel_text)
					self.p_layout.set_alignment(pango.ALIGN_CENTER)
					ctx.show_layout(self.p_layout)
					ctx.fill()
					
					ctx.restore()
 
				ctx.save()
				ctx.translate(77,96)
				self.theme.render(ctx, 'pourcent')
				ctx.restore()
				
			
			ctx.save()
			ctx.translate(47,98)			
			dec = int(math.floor(load/10))
			img = str(dec)
			self.theme.render(ctx, img)
			
			ctx.restore()
			
			ctx.save()
			ctx.translate(61,98)
			dec = int(load -dec*10)
			img = str(dec)
			self.theme.render(ctx, img)

			ctx.restore()

			
			angle = math.pi*1.5*(load-50)/100
			cosa = math.cos(-angle)
			sina = math.sin(-angle)
			#y1 = math.cos(-angle)*45
			#x1 = math.sin(-angle)*45
			#x2 = math.cos(-angle)*6
			#y2 = math.sin(-angle)*6
			
			ctx.save()
			ctx.translate(68-sina*46-cosa*7,68-cosa*46+sina*7)
			ctx.rotate (angle)
			self.theme.render(ctx, 'ombre')
			ctx.restore()

			ctx.save()
			#y1 = math.cos(-angle)*43
			#x1 = math.sin(-angle)*43
			#x2 = math.cos(-angle)*5
			#y2 = math.sin(-angle)*5
			ctx.translate(66-sina*44-cosa*5,66-cosa*44+sina*5)
			ctx.rotate (angle)
			self.theme.render(ctx, 'aiguille')
			ctx.restore()
			
			
			
			
			ctx.save()
			ctx.translate(10,10)
			self.theme.render(ctx, 'vitre')
			ctx.restore()
		
	def on_draw_shape (self,ctx):
		if self.theme:
			ctx.scale(self.scale, self.scale)
			self.theme.render(ctx, 'fond')





	def get_selected_cpu_load (self,cpu):
		# Let's try if we can calc system load.
		try:
			f = open("/proc/stat", "r")
			tmp = f.readlines(2000)
			f.close()
		except:
			print "Failed to open /proc/stat"
			sys.exit(1)
		load = 0
		cpu = cpu[10:]

		for line in tmp:
			if line.strip().startswith(cpu+" "):
				line = line.replace(cpu,"")
				reg = re.compile('[0-9]+')
				load_values = reg.findall(line)
				
				cuse = int(load_values[0])
				csys = int(load_values[2])
				load = cuse + csys - self.old_cuse
				
				self.old_cuse = cuse + csys
		return load

	def get_cpu_list (self):
		try:
			f = open("/proc/stat", "r")
			tmp = f.readlines(2000)
			f.close()
		except:
			print "Failed to open /proc/stat"
			sys.exit(1)

		list = []
		for line in tmp:
			if line.startswith("cpu"):
				list.append(_('CPU load: ')+ line.split(' ')[0])
				
		return list
	
	def get_usedmem(self):
		meminfo_file = open('/proc/meminfo')
		meminfo = {}
		for x in meminfo_file:
		    try:
                        (key,value,junk) = x.split(None, 2)
		        key = key[:-1] 
		        meminfo[key] = int(value)
		    except:
		        pass
		meminfo_file.close()
		return int((100*(int(meminfo['MemTotal'])-int(meminfo['Cached']) - int(meminfo['Buffers']) - int(meminfo['MemFree'])))/int(meminfo['MemTotal']))
	
	def get_usedswap(self):
		meminfo_file = open('/proc/meminfo')
		meminfo = {}
		for x in meminfo_file:
		    try:    
                        (key,value,junk) = x.split(None, 2)
		        key = key[:-1] 
		        meminfo[key] = int(value)
		    except:
		        pass
		meminfo_file.close()
		return int((100*(int(meminfo['SwapTotal'])-int(meminfo['SwapCached']) - int(meminfo['SwapFree'])))/int(meminfo['SwapTotal']))
	
	def get_sensor_list(self):
		res = commands.getstatusoutput('sensors')
		output = []
		if res[0]==0:
			sol = res[1].splitlines()
			for i in sol:
				if (i.find('\xb0')!= -1) or (i.find('\xc2')!= -1) or (i.find('temp')!= -1) or (i.find(_('Temp'))!= -1):
					output.append(_('Temperature: ')+i.lstrip().split(')')[0]+')')
		return output


	def get_sensor_temperature(self,sensorName):
		sensor =  sensorName[13:].lstrip().split(' ')[0]
		res = commands.getoutput('sensors')
		sol = res.splitlines()
		for s in sol:
			if s.find(sensor)!= -1:
				s = s.split(':')[1].strip(' ').strip('\t')
				
				i = 0
				while(((s[i]>='0') and (s[i]<='9')) or (s[i]=='.') or (s[i]=='+') or (s[i]=='-')):
					i = i+1
				return int(float(s[0:i]))
	
	#Pour recuperer la charge reseau. Si c'est pour l'upload, downlaod doit etre True 	
	def get_network_load(self, download,card, maxSpeed, Mb, use_bit):
		data = commands.getoutput("cat /proc/net/dev")
		data = data[data.find(card + ":")+5:]
		if(download):
			newNet = float( data.split()[0] )
		else:
			newNet = float( data.split()[8] )

		val = int(newNet -self.net_lastValue)
		
		if(use_bit):
			val = val*8
			foot = 'b/s'
		else:
			 foot = _('B/s')
		
		if val < (2*1024): 
			val = str(val)+ ' '+foot			
		elif  val <(2*1024*1024): 
			val = str(float(val/1024))+ ' K'+foot		
		elif val < (2*1024*1024*1024): 
			val = str(float(int(val/1024)/1024))+ ' M'+foot	
		else: 
			val = str(float(int(val/(1024*1024))/1024))+ ' G'+foot	

		
		if Mb == _('Mbit/s'):
			multi = 1024*1024
		else:
			multi = 1024
		percent = (800*(newNet -self.net_lastValue))/(maxSpeed*multi)
		self.net_lastValue = newNet	
		return percent, val
		

	def get_network_cards_list(self):
		list = []
		data = commands.getoutput("cat /proc/net/dev |grep :")
		for f in data.split(':'):
			f = f[::-1]
			f = f[:f.find(' ') ].strip()
			f = f[::-1]
			if f != None	 and f != '0' and f != 'lo':  list.append(str(f))
		return list

	
	
	
# If the program is run directly or passed as an argument to the python
# interpreter then create a Screenlet instance and show it
if __name__ == "__main__":
	# create new session
	import screenlets.session
	screenlets.session.create_session(ManometerScreenlet)

