#!/usr/bin/python
#  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, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#  

from calendar import day_name
from gi.repository import Gtk
import random, os,sys
import gettext

MAX_CHKS = 168

class DialogWindow(Gtk.Dialog):

	def __init__(self, parent, title, message):
		Gtk.Dialog.__init__(self, title, parent, 0,(Gtk.STOCK_OK, Gtk.ResponseType.OK))
		lns1= Gtk.Separator(expand=True)
		lns2= Gtk.Separator(expand=True)
		self.set_default_size(len(message)*7, 100)

		label = Gtk.Label(message)

		box = self.get_content_area()
		box.add(lns1)
		box.add(label)
		box.add(lns2)
		self.show_all()

class Scheduler(Gtk.Window):

	def __init__(self, program_to_execute):
		Gtk.Window.__init__(self, title=_("Scheduler"))
		self.dayButtons = [Gtk.Button]*7
		hourButtons = [Gtk.Button]*24
		self.chkProg = [Gtk.CheckButton]*MAX_CHKS
		self.checkDay={}
		self.checkHour={}
		
		mainBox = Gtk.VBox(spacing=10)
		grid = Gtk.Grid()
		mainBox.add(grid)

		separator= Gtk.Separator(expand=True)
		mainBox.add(separator)

		optBox = Gtk.HBox(spacing=2)
		mainBox.add(optBox)

		bottomBox = Gtk.HBox(spacing=2)
		mainBox.add(bottomBox)

		separator2= Gtk.Separator(expand=True)
		mainBox.add(separator2)

		self.add(mainBox)

		# day buttons
		for i in range(7):
			self.dayButtons[i]=Gtk.Button(day_name[i].capitalize(),relief="none",image_position="left")
			self.dayButtons[i].connect("clicked",self.on_click,i)
			self.dayButtons[i].set_name='btD'+`i`
			grid.attach(self.dayButtons[i], 0, i+1, 1, 1)
		# hour buttons
		for i in range(24):
			hourLabel= Gtk.Label(`i`+":00",angle = 90)
			hourButtons[i]=Gtk.Button(relief="none")
			hourButtons[i].add(hourLabel)
			hourButtons[i].set_name='btH'+`i`
			hourButtons[i].connect("clicked",self.on_click,i+10)
			grid.attach(hourButtons[i], i+1, 0, 1, 1)

		# checkboxes associated to hours and days
		for i in range(7):
			self.checkDay[i]=[[],False]
			for j in range(24):
				self.chkProg[i*24+j] = Gtk.CheckButton(draw_indicator=True, can_focus = True, use_underline = True)
				self.chkProg[i*24+j].set_name='chk'+`i`+'_'+`j`
				grid.attach(self.chkProg[i*24+j], j+1, i+1, 1, 1)
				self.checkDay[i][0].append(self.chkProg[i*24+j])

		for i in range(24):
			self.checkHour[i]=[[],False]
			for j in range(7):
				self.checkHour[i][0].append(self.chkProg[j*24+i])

		
		self.entryShell=Gtk.Entry()
		# set program given as paramerater
		self.entryShell.set_text(program_to_execute)
		# update combobox
		self.entryShell.connect("key-press-event",self.on_text_mod)
		# check if exist previous selection
		self.entryShell.connect("focus-out-event",self.check_prg)

		img=Gtk.Image()
		img.set_from_stock("gtk-open",Gtk.IconSize.BUTTON)
		openBtn=Gtk.Button()
		openBtn.add(img)
		openBtn.connect("clicked",self.on_click_search)

		optBox.pack_start(self.entryShell, True, True, 4)
		optBox.pack_start(openBtn, False, False, 1)
		
		self.optLst = Gtk.ListStore(int, str, str)
		self.optLst.append([0, "", _("Custom")])
		self.read_prgs_conffile()


		self.dfltOpt = Gtk.ComboBox.new_with_model_and_entry(self.optLst)
		self.dfltOpt.connect("changed", self.on_dfltOpt_changed)
		self.dfltOpt.set_entry_text_column(2)
		self.dfltOpt.set_active(0)
		optBox.pack_end(self.dfltOpt, False, True, 4)

		adjustment = Gtk.Adjustment(0, 0, 100, 1, 10, 0)
		self.selQty = Gtk.SpinButton()
		self.selQty.set_adjustment(adjustment)
		btRnd=Gtk.Button(_("Random"))
		btRnd.connect("clicked",self.random_chk)
		btClean=Gtk.Button(_("Clean"))
		btClean.connect("clicked",self.clean_chk)
		btApply=Gtk.Button(_("Apply"))
		btApply.connect("clicked",self.save_prg_selections)
		btListApps=Gtk.Button(_("List programmed applications"))
		btListApps.connect("clicked", self.show_apps)
		btEnd=Gtk.Button(_("Exit"))
		btEnd.connect("clicked", Gtk.main_quit)

		bottomBox.pack_start(self.selQty, False, True, 4)
		bottomBox.pack_start(btRnd, False, True, 4)
		bottomBox.pack_start(btClean, False, True, 4)
		bottomBox.pack_start(btApply, False, True, 4)
		bottomBox.pack_end(btEnd, False, True, 4)
		bottomBox.pack_end(btListApps, False, True, 4)

	# returns number of inactive checkboxes
	def active_ops(self):
		try:
			n=0
			for i in range(MAX_CHKS):
				if self.chkProg[i].get_active():
					n+=1
			n = MAX_CHKS - n 
		except Exception as e:
			print e
			
		return n


	# check if given program has previous configurations
	#if it has, then function loads them on scheduler
	def check_prg(self, widget, data):
		for files in os.listdir('/etc/cron.d/'):
			if files.endswith('scheduler'):
				if  self.get_prg_name() == files.split('.')[0]:
					messageBox(self.get_prg_name(), _("Restored previous configuration"))
					self.read_config_data()


	#deactivate all checkboxes
	def clean_chk(self,widget):
		try:
			for i in range(MAX_CHKS):
				self.chkProg[i].set_active(False)
				
		except Exception as e:
			print e

	def generate_cron_file(self, full_path):
		fileName = "/etc/cron.d/"+self.get_prg_name()+"_scheduler"
		if not fileName.find('mirror') == -1:
			minute = random.randint(0, 60)
		else:
			minute = 0

		exist = False
		created = False

		if os.path.exists(fileName):
			try:
				os.remove(fileName)
				exist=True
			except:
				print "Exception: ",str(sys.exc_info())


		try:
			if (MAX_CHKS - self.active_ops()) > 0:
				created = True

				f=open(fileName,"w")
				f.write("SHELL=/bin/bash\n")
				f.write("PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n")

				for day in range(7):
					hours = ""
					count = 0
					for j in range(24):
						if self.chkProg[day*24+j].get_active():
							hours = hours + `j` +','
							count+=1

					if count > 0 :
						if count == 24 :
							hours = '*'
						else:
							hours=hours[0:-1]

						day+=1
						f.write(`minute`+" "+hours+" *"+" * "+`day`+" root "+full_path+" > /dev/null\n")
				f.close()
				
		except:
			print "Exception: ",str(sys.exc_info())

		print "exist: "+`exist` + " created: " + `created`
		
		if exist:
			if created:
				messageBox(_("Updated Cron file"),_("Cron of ") + self.get_prg_name() + _(" updated"))
			else:
				messageBox(_("Removed Cron file"),_("Cron of ") + self.get_prg_name() + _(" removed"))
		else:
			if created:
				messageBox(_("Created Cron file"),_("Cron of ") + self.get_prg_name() + _(" created"))
			else:
				messageBox(_("Nothing to do"),_("No selections made"))

	def get_data(self,data):
		name = ''
		app = ''
		if len(data.strip())<>0 and not data.startswith('#'):
			data=data.rstrip('\n')
			data= data.split('\"')
			name = data [1]
			app= data [3]
		return(name,app)


	def get_data_cron_format(self,data):
		hour = ''
		weekday = ''
		if len(data.strip())<>0 and not data.startswith('#'):
			try:
				i=int(data[0])
				data=data.rstrip('\n')
				data= data.split(' ')
				hour = data [1]
				weekday= data [4]
			except:
				print _("ommited")
		return(hour, weekday)


	#get program name from textbox
	def get_prg_name(self):
		return(self.entryShell.get_text().split('/')[-1]).split(' ')[0]


	def on_click(self,widget,data):
		try:
			if data <10:
				self.checkDay[data][1]=not self.checkDay[data][1]
				for item in self.checkDay[data][0]:
					item.set_active(self.checkDay[data][1])
			else:
				data=data-10
				self.checkHour[data][1]=not self.checkHour[data][1]
				for item in self.checkHour[data][0]:
					item.set_active(self.checkHour[data][1])
				
		except Exception as e:
			print e

	def on_click_search(self,widget):
		try:
			dialog = Gtk.FileChooserDialog(_("Choose binary file"), self,Gtk.FileChooserAction.OPEN,(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
			response = dialog.run()
			if response == Gtk.ResponseType.OK:
				self.dfltOpt.set_active(0)
				self.entryShell.set_text(dialog.get_filename())
				self.check_prg(self.entryShell,"")
			elif response == Gtk.ResponseType.CANCEL:
				print "Cancel clicked"
			dialog.destroy()

			widget.get_toplevel().child_focus(Gtk.DirectionType.TAB_FORWARD)
		except Exception as e:
			print e


	def on_dfltOpt_changed(self, combo):
		tree_iter = combo.get_active_iter()
		if tree_iter != None:
			model = combo.get_model()
			row_if, path_binary = model[tree_iter][:2]
			self.entryShell.set_text(path_binary)
			self.check_prg(self.entryShell,"")
		else:
			combo.set_active_text("")

	# on text modify change combobox to "custom"
	def on_text_mod(self, widget,data):
		self.dfltOpt.set_active(0)

	#activate random n-given checkboxes 
	def random_chk(self,widget):
		i =int(self.selQty.get_value())

		if i > self.active_ops():
			messageBox(_("Atention"),_("Not enougth free hours"))
		else:
			while i>0:
				n= random.randint(0, MAX_CHKS-1)
				if self.chkProg[n].get_active() == False :
					self.chkProg[n].set_active(True)
					i-=1

	def read_config_data(self):
		fileName = "/etc/cron.d/"+self.get_prg_name()+"_scheduler"
		try:
			if os.path.exists(fileName):
				try:
					ins = open(fileName, "r" )
					for line in ins:
						hours, week_day = self.get_data_cron_format(line)
						if not hours == '':
							if hours == '*':
								self.dayButtons[int(week_day)-1].clicked()
							else:
								hours=hours.split(',')
								for hour in hours:
									value = int(hour)+(int(week_day)-1)*24
									self.chkProg[value].set_active(True)
				except:
					print "Exception: ",str(sys.exc_info())
		except Exception as e:
			print e


	def read_prgs_conffile(self):
		fileName = "/etc/scheduler.conf"
		i=0
		try:
			if os.path.exists(fileName):
				try:
					ins = open(fileName, "r" )
					for line in ins:
						name, app = self.get_data(line)
						if not name == '':
							i+=1
							self.optLst.append([i, app, _(name)])
				except:
					print "Exception: ",str(sys.exc_info())
			else:
				print fileName + " not exist using default values"
				self.optLst.append([1, "/usr/sbin/lliurex-mirror-non-gtk update", _("Update mirror")])
				self.optLst.append([2, "/sbin/shutdown -h now", _("Shutdown")])

		except Exception as e:
			print e


	def save_prg_selections(self,widget):
		binary=self.entryShell.get_text()
		path_to_bin=binary.split(' ')[0]
		full_path=''
		
		#check if program exist
		if os.path.exists(path_to_bin) and path_to_bin[-1] != '/':
			full_path=self.entryShell.get_text()
		else:
			full_path = self.search_binary() + binary[len(binary.split (' ')[0]):len(binary)]

		if full_path == '':
			messageBox(_("Atention"),_("Binary path not exist"))
		else:
			self.generate_cron_file(full_path)
		print full_path

	def search_binary(self):
		binary_dirs = ['/bin/','/sbin/','/usr/bin/','/usr/sbin/','/usr/local/bin/','/usr/local/sbin/']
		full_path=''
		name=self.get_prg_name()

		for path in binary_dirs:
			for files in os.listdir(path):
				if files == name:
					full_path = path+files
					break
			if not full_path == '':
				break
		return full_path


	def show_apps(self, widget):
		text=''
		for files in os.listdir('/etc/cron.d/'):
			if files.endswith('scheduler'):
					text=text+'\n*  '+files.split('.')[0]+'\n'

		if not text == '':
			messageBox(_("Programmed Applications"), text)


############
##  main
############

def messageBox(title, msg):
	dialog = DialogWindow(0,title,msg)
	dialog.run()
	dialog.destroy()

gettext.textdomain('scheduler')
_ = gettext.gettext


if os.geteuid() != 0:
	messageBox(_("ERROR"),_("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting."))
	exit(_("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting."))

program_to_execute =""
if len(sys.argv) > 2:
	for i in range(1,len(sys.argv)):
		program_to_execute=program_to_execute+sys.argv[i]+" "
	win = Scheduler(program_to_execute)
else:
	win = Scheduler("")

win.set_property("resizable",False)
win.set_resizable(False)
win.set_position(Gtk.WindowPosition.CENTER)
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
