#!/usr/bin/env python

# This application is released under the GNU General Public License 
# v3 (or, at your option, any later version). You can find the full 
# text of the license under http://www.gnu.org/licenses/gpl.txt. 
# By using, editing and/or distributing this software you agree to 
# the terms and conditions of this license. 
# Thank you for using free software!

#  ACPIBatteryScreenlet (c) JMDK 2007 
#
# This screenlets is heavily based on the CPUMeterScreenlet by RYX
#
# INFO:
# - a simple ACPI Battery meter
# 
# This screenlets is heavily based on the CPUMeterScreenlet by RYX

import screenlets
from screenlets.options import IntOption, BoolOption, StringOption, ColorOption
import cairo
import pango
import sys
import gobject
from os import listdir,path

class ACPIBatteryScreenlet(screenlets.Screenlet):
	"""A simple ACPI Battery meter."""
	
	# default meta-info for Screenlets
	__name__ = 'ACPIBatteryScreenlet'
	__version__ = '0.2'
	__author__ = 'JMDK'
	__desc__ = 'A themeable ACPI Battery meter.'

	# internals
	__timeout = None
	__flag=0
	
	# settings
	update_interval = 5
	file_auto = True
	file_path='/proc/acpi/battery/BAT0/'
	state_file='state'
	info_file='info'
	alarm_threshold = 5
	show_time = True
	show_percent = True
	show_background = True
	background_color = (0,0,0, 0.4)
	# constructor
	def __init__(self, **keyword_args):
		#call super (and not show window yet)
		screenlets.Screenlet.__init__(self, uses_theme=True,width=100,height=50, **keyword_args)
		# set theme
		self.theme_name = "default"
		# add settings
		self.add_options_group('ACPI Battery', 'ACPI Battery specific options')
		self.add_option(IntOption('ACPI Battery', 'update_interval', 
			self.update_interval, 'Update interval', 
			'The interval for updating the ACPI Battery meter (in seconds) ...',
			min=1, max=60))
		self.add_option(IntOption('ACPI Battery', 'alarm_threshold', 
			self.alarm_threshold, 'Alarm threshold', 
			'The threshold triggering the low battery alarm (in precent) ...',
			min=1, max=100))
		self.add_option(BoolOption('ACPI Battery', 'file_auto', 
			self.file_auto, 'Guess battery file', 'Try to guess which battery to use ...'))
		self.add_option(StringOption('ACPI Battery', 'file_path', 
			self.file_path, 'Battery files path', 'Path to the battery files ...'),
			realtime=False)
		self.add_option(StringOption('ACPI Battery', 'state_file', 
			self.state_file, 'Battery state file', 'Name of the battery state file ...'),
			realtime=False)
		self.add_option(StringOption('ACPI Battery', 'info_file', 
			self.info_file, 'Battery info file', 'Name of the battery info file ...'),
			realtime=False)
		self.add_option(BoolOption('ACPI Battery', 'show_time', 
			self.show_time, 'Display time', 'Show remaining time on ACPI battery meter ...'))
		self.add_option(BoolOption('ACPI Battery', 'show_percent', 
			self.show_percent, 'Display percentage', 'Show percentage on ACPI battery meter ...'))
		self.add_option(ColorOption('ACPI Battery','background_color', 
			self.background_color, 'Back color(only with default theme)', 'only works with default theme'))

		# init the timeout function
		self.update_interval = self.update_interval
		self.file_auto = self.file_auto
		self.file_path = self.file_path
	
	# 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:
				# TODO: raise exception!!!
				self.__dict__['update_interval'] = 1
		elif name == "file_auto":
			if value:
				dirs=listdir('/proc/acpi/battery/');
				dirs.sort();
				try:
					self.__dict__['file_path']='/proc/acpi/battery/'+dirs[0]+'/'
				except IndexError:
					pass
				
		elif name == "file_path":
			if self.__flag>0:
				self.__dict__['file_auto']=0
			self.__flag+=1
	def on_init (self):
		print "Screenlet has been initialized."
		# add default menuitems
		self.add_default_menuitems()			
	def getValue(self):
		present_rate = None
		charge_status='NA'
		ispresent='no'
		if path.exists(self.file_path+self.state_file):
			file=open(self.file_path+self.state_file,'r')
			while 1:
				line = file.readline()
				if ((not line)|(line.find('present') > -1)): break
			if line:
				linelist=line.rstrip('\n').split(' ')
				i=1
				while (i<len(linelist)):
					if ((linelist[i]=='yes')|(linelist[i]=='no')):
						ispresent=linelist[i]
						break
					else: i+=1
			if (ispresent=='yes'):
				while 1:
					line = file.readline()
					if ((not line)|(line.find('charging state') > -1)): break
				if line:
					linelist=line.rstrip('\n').split(' ')
					i=1
					while (i<len(linelist)):
						if ((linelist[i]=='charged')|(linelist[i]=='charging')|(linelist[i]=='discharging')):
							charge_status=linelist[i]
							break
						else: i+=1
				while 1:
					line = file.readline()
					if ((not line)|(line.find('present rate') > -1)): break
				linelist=line.split(' ')
				i=0
				while (i<len(linelist)):
					if (linelist[i].isdigit()):
						present_rate=linelist[i]
						break
					else: i+=1
				while 1:
					line = file.readline()
					if ((not line)|(line.find('remaining') > -1)): break
				linelist=line.split(' ')
				i=0
				while (i<len(linelist)):
					if (linelist[i].isdigit()):
						remaining=linelist[i]
						break
					else: i+=1
			else:
				present_rate=1
				remaining=1				
			file.close()
		else:
			present_rate=1
			remaining=1
		if (ispresent=='yes'):
			if path.exists(self.file_path+self.state_file):
				file=open(self.file_path+self.info_file,'r')
				while 1:
					line = file.readline()
					if ((not line)|(line.find('last full') > -1)): break
				linelist=line.split(' ')
				i=0
				while (i<len(linelist)):
					if (linelist[i].isdigit()):
						last_full=linelist[i]
						break
					else: i+=1
				file.close()
			else:
				last_full=1
		else:
			last_full=1
		if present_rate == None : present_rate = '1000'
		if int(present_rate)==0: present_rate='1000';
		return (charge_status,present_rate,remaining,last_full)
		
	# timeout-function
	def update_graph(self):
		self.redraw_canvas()
		return True
	
	def on_draw(self, ctx):
		(charge_status,present_rate,remaining,last_full)=self.getValue()
		percents="%3i" % ((100*int(remaining)) / int(last_full))
		# set size
		ctx.scale(self.scale, self.scale)
		# draw bg (if theme available)
		ctx.set_operator(cairo.OPERATOR_OVER)
		if self.theme :
			ctx.set_source_rgba(*self.background_color)
			if self.theme_name == 'default':self.draw_rounded_rectangle(ctx,0,0,9,96.6,46.5)
			self.theme.render(ctx,'acpibattery-bg')
		self.theme.render(ctx,'acpibattery-battery')
		if charge_status=='charged': 
			time_value='  Full'
		elif charge_status=='discharging':
			t=(60*int(remaining))/int(present_rate)
			time_value="%02i:%02i" % (int(t/60),int(t%60))
			if int(percents)<=self.alarm_threshold:
				self.theme.render(ctx,'acpibattery-alarm')
			else:
				self.theme.render(ctx,'acpibattery-using')
		elif charge_status=='charging':
			t=(60*(int(last_full)-int(remaining)))/int(present_rate)
			time_value="%02i:%02i" % (int(t/60),int(t%60))
			self.theme.render(ctx,'acpibattery-charging')
		else:
			time_value='   NA'
			percents='   0'
			self.theme.render(ctx,'acpibattery-alarm')
		if charge_status=='NA':
			# draw message
			ctx.save()
			ctx.translate(37,5)
			ctx.set_source_rgba(1, 0, 0, 1)
			self.draw_text(ctx,'   No',0,0,"FreeSans", 14,  self.width)
			ctx.fill()
			ctx.restore()							
			ctx.save()
			ctx.translate(37,26)
			ctx.set_source_rgba(1, 0, 0, 1)
			self.draw_text(ctx,' battery',0,0,"FreeSans", 12,  self.width)
			ctx.restore()									
		elif self.show_time and self.show_percent:
			# draw percent and time
			ctx.save()
			ctx.translate(37,5)
			if int(percents)<=self.alarm_threshold:
				ctx.set_source_rgba(1, 0, 0, 1)
			else:
				ctx.set_source_rgba(1, 1, 1, 1)
			self.draw_text(ctx,percents+"%",0,0,"FreeSans", 16,  self.width)
			ctx.restore()							
			ctx.save()
			ctx.translate(37,26)
			if int(percents)<=self.alarm_threshold:
				ctx.set_source_rgba(1, 0, 0, 1)
			else:
				ctx.set_source_rgba(1, 1, 1, 1)
			self.draw_text(ctx,time_value,0,0,"FreeSans", 14,  self.width)
			ctx.restore()							
		elif self.show_time:
			# draw time
			ctx.save()
			ctx.translate(37,13)
			if int(percents)<=self.alarm_threshold:
				ctx.set_source_rgba(1, 0, 0, 1)
			else:
				ctx.set_source_rgba(1, 1, 1, 1)
			self.draw_text(ctx,time_value,0,0,"FreeSans", 16,  self.width)
			ctx.restore()				
		else:
			# draw percent
			ctx.save()
			ctx.translate(37,13)
			if int(percents)<=self.alarm_threshold:
				ctx.set_source_rgba(1, 0, 0, 1)
			else:
				ctx.set_source_rgba(1, 1, 1, 1)
			self.draw_text(ctx,percents+"%",0,0,"FreeSans", 16,  self.width)
			ctx.restore()
		# draw glass (if theme available)
		if self.theme:
			self.theme.render(ctx,'acpibattery-glass')
		
	def on_draw_shape(self,ctx):
		if self.theme:
			ctx.scale(self.scale, self.scale)
			self.draw_rectangle(ctx,0,0,self.width,self.height)
			self.on_draw(ctx)
	
# 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__":
	import screenlets.session
	screenlets.session.create_session(ACPIBatteryScreenlet)
