#!/usr/bin/env python
##############################################
#                                            #
#             TheZulk's Portage GUI          #
#                  by TheZulk  ;)            #
#                                            #
#                                            #
#      This version is using Tkinter for     #
#            the GUI interface               #
#                                            #
#     The code is very ugly, but i didn't    #
#    have time to clean it up :/             #
#                                            #
#     Please don't steal my code without     #
#      giving me credit for it               #
#                                            #
#  And btw, I've stolen GetEbuildPath and    #
#  GetDescription from portage.              #
#                                            #
#                                            #
##############################################

from Tkinter import *
from threading import *
from glob import glob
from os import system, listdir
import portage
import re

terminal = 'xterm'

packList = {}
current = []
selected = ''
searching = -1
searchList = []

def GetEbuildPath(strBase):  ## Returns the full path of the ebuild ##
        strBaseFinal = '/usr/portage/' + strBase.strip()
        find_package = re.compile('/')
        p = find_package.split(port.dep_bestmatch(strBase).strip())
        strBaseFinal = strBaseFinal + "/" + p[1] + ".ebuild"
        return strBaseFinal

def GetDescription(strEbuild, intVersion): ## Gets description from latest ebuild ##
        f = open(strEbuild)
        data = "temp"
        while data != "":
                data = f.readline()
                if re.search("description=\"", data.lower()):
                        dump_prefix = re.compile('DESCRIPTION="')
                        dump_suffix = re.compile('"')
                        p = dump_prefix.split(data)
                        p = dump_suffix.split(p[1])
                        result = p[0].replace("${PV}", intVersion)
                        result = result.replace("${pv}", intVersion)
                        result = result.replace("$PV", intVersion)
                        result = result.lower().replace("${description}", "KDE "  + intVersion + " ")
        return result

class runProg(Thread):
	def __init__(self):
		Thread.__init__(self)
		self.text = 'Hej'
		self.prog1 = 'echo hej'
		self.sync = 0
		self.pret = 0

	def run(self):
		if self.sync:
			system(terminal+' -T "Syncing portagetree" -e su -c "emerge sync"')
		if self.pret:
			file('/tmp/genpack.sh','w').write('%s\necho Done\nread A\n'%self.prog1)
		else:
			file('/tmp/genpack.sh','w').write('su -c "%s"\necho Done\nread A\n'%self.prog1)
		system(terminal+' -T "%s" -e sh /tmp/genpack.sh'%self.text)
		system('rm /tmp/genpack.sh')
		

def openTerm(text, prog1, sync=0, pret=0):
	tmpProg = runProg()
	tmpProg.text = text
	tmpProg.prog1 = prog1
	tmpProg.sync = sync
	tmpProg.pret = pret
	tmpProg.start()

def initList():
	global packList,port,portage,vartree
	reload(portage)
	port = portage.portagetree()
	vartree = portage.vartree()

	for a in portage.categories:
		b = a.split('-')
		if len(b) < 2:
			continue
		if not packList.has_key(b[0]):
			packList[b[0]] = {}
		packList[b[0]][b[1]] = []
		for c in glob('/usr/portage/%s/*'%a):
			packList[b[0]][b[1]].append(c.split('/')[-1])

initList()

class List(Frame):
	def __init__(self,master=0,boxes=1,names=['Listbox']):
		Frame.__init__(self,master,relief=RAISED,borderwidth=1)
		self.box = []
		self.lab = []
		self.frm = []
				
		self.scroll = Scrollbar(self,command=self.yview)
		self.scroll.pack(expand=0,side=RIGHT,fill=Y)
		for a in range(boxes):
			tmpFrame = Frame(self,relief=SUNKEN,borderwidth=2)
			tmpFrame.pack(expand=1,side=LEFT,fill=BOTH)
			tmpLabel = Label(tmpFrame,text = names[a])
			tmpLabel.pack(expand=0,side=TOP,fill=X)
			tmpBox = Listbox(tmpFrame,yscrollcommand=self.yset,borderwidth=0,bg='white',)
			tmpBox.pack(expand=1,side=BOTTOM,fill=BOTH)
			tmpBox.bind('<1>',self.clicked)
			self.box.append(tmpBox)
			self.lab.append(tmpLabel)
			self.frm.append(tmpFrame)
						
        def yview(self,a,b,c=None):
        	for aa in self.box:
        	        aa.yview(a,b,c)
						
        def add(self,lista):
        	return self.insert(END,lista)
				
        def insert(self,pos,lista):
        	if type(lista) == list:
        		if len(lista) != len(self.box):
        			raise 'Err: List must be %s colums big... it is %s'%(len(self.box),len(lista))
        		for a in range(len(lista)):
        			self.box[a].insert(pos,lista[a])
        	elif type(lista) == str:
        		self.box[0].insert(pos,lista)
        	else:
        		raise 'Err: Must be string or list of strings'

	def update(self,index,col,value):
		self.box[col].delete(index)
		self.box[col].insert(index,value)

	def delete(self, index):
		for a in self.box:
			a.delete(index)
	
	def clear(self):
		for a in range(len(self.box)):
			self.box[a].delete(0,END)

	def yset(self, a, b):
		for aa in self.box:
			aa.yview('moveto',a)
		self.scroll.set(a,b)
				
	def clicked(self, evnt):
		global selected, current, searching, searchList
		if searching==-1:
			if len(current) >= 2:
				ind = self.box[0].nearest(evnt.y)
				for a in range(len(self.box)-1):
					self.box[a].select_clear(0,END)
					self.box[a].select_set(ind)
				selected = "%s-%s/%s"%(current[0], current[1], self.box[0].get(ind))
		if not searching:
			ind = self.box[0].nearest(evnt.y)
			selected = searchList[ind]			
		
		self.lab[0].config(text='Package:\n%s'%selected)

class App(Tk):
	def __init__(self):
		global packList
		Tk.__init__(self)
		menu = Menu(self)

		fileMenu = Menu(menu,tearoff=0)
		fileMenu.add_command(label='Exit', command=self.quit)
		menu.add_cascade(label='File', menu=fileMenu)

		pretend = Menu(menu,tearoff=0)
		pretend.add_command(label='Pretend to merge selected package', command=self.pret_sel)
		pretend.add_command(label='Pretend to update world', command=self.pret_world)
		pretend.add_command(label='Sync and pretend to update world', command=self.pret_sworld)
		menu.add_cascade(label='Pretend', menu=pretend)

		merge = Menu(menu,tearoff=0)
		merge.add_command(label='Merge selected package', command=self.merg_sel)
		merge.add_command(label='Unmerge selected package', command=self.umerg_sel)
		merge.add_command(label='Update world', command=self.merg_world)
		merge.add_command(label='Sync and update world', command=self.merg_sworld)
		menu.add_cascade(label='Merge', menu=merge)
		self.config(menu=menu)

		self.mainFrame = Frame(self)
		self.mainFrame.pack(side=TOP,fill=BOTH,expand=1)
		self.lista = List(self.mainFrame, 1, ['Packages'])
		self.lista.pack(side=LEFT,fill=Y)
		tmp = packList.keys()
		tmp.sort()
		for a in tmp:
			self.lista.add([a])

		self.pList = List(self.mainFrame, 6, ['Package\n','Installed\nversion','Installed\nrevision','Available\nversion','Availabe\nrevision','Description\n'])
		for a in range(4):
			self.pList.box[1+a].config(width=12)
			self.pList.frm[1+a].pack_configure(fill=Y,expand=0)
			
		self.pList.box[0].config(width=20)
		self.pList.frm[0].pack_configure(fill=Y,expand=0)
		self.pList.box[5].config(width=70)
		self.pList.pack(side=RIGHT,fill=BOTH,expand=1)
		self.lista.box[0].bind('<1>' , self.clicked)
		
		self.searchFrame = Frame(self)
		self.searchFrame.pack(side=BOTTOM,fill=X)
		Label(self.searchFrame,text='Search:').pack(side=LEFT)
		self.searchBox = Text(self.searchFrame,bg='white',height=1)
		self.searchBox.bind("<Key>", self.txt_press)
		self.searchBox.pack(side=LEFT,fill=X,expand=1)
		Button(self.searchFrame,command=self.but_search,text='Search').pack(side=RIGHT)
		self.searching = -1

	def pret_sel(self):
		global selected
		if selected:
			openTerm('Pretending merge of: %s'%selected,'/usr/bin/emerge -p %s'%selected , 0, 1)

	def pret_world(self):
		openTerm('Pretending world update','/usr/bin/emerge -p -u world' , 0, 1)
		
	def pret_sworld(self):
		openTerm('Pretending world update','/usr/bin/emerge -p -u world' , 1, 1)
		initList()

	def merg_sel(self):
		global selected
		if selected:
			openTerm('Merging: %s'%selected,'/usr/bin/emerge %s'%selected , 0)
			initList()

	def umerg_sel(self):
		global selected
		if selected:
			openTerm('Unmerging: %s'%selected,'/usr/bin/emerge unmerge %s'%selected , 0)
			initList()

	def merg_world(self):
		openTerm('Updating world','/usr/bin/emerge -u world' , 0)
		initList()

	def merg_sworld(self):
		openTerm('Updating world','/usr/bin/emerge -u world' , 1)
		initList()


	def txt_press(self, evnt):
		try:
			if ord(evnt.char) == 13:
				self.but_search()
			if ord(self.searchBox.get(0.0,END)[-2:-1]) == 10:
				self.searchBox.delete(0.0,END)
		except:
			pass

	def but_search(self):
		global searching, searchList
		self.searching = 1
		selected = ''
		searchList = []
		self.pList.clear()
		for a in packList.keys():
			for b in packList[a]:
				for c in packList[a][b]:
					if re.search(self.searchBox.get(0.0,END)[:-1], c):
						pack='%s-%s/%s'%(a,b,c)
						self.addPack(pack)
						searchList.append(pack)
		searching = 0
		
	def addPack(self,name):
				inst = portage.catpkgsplit(vartree.dep_bestmatch(name))
				new  = portage.catpkgsplit(port.dep_bestmatch(name))
				if not inst:
					inst = ['','','None','None']
				if not new:
					new = ['','','[MASKED]','[MASKED]']
				iver=inst[2]
				irev=inst[3]
				nver=new[2]
				nrev=new[3]

				if new[0]:
					try:
						desc = GetDescription(GetEbuildPath(name) , nver)
					except:
						desc = '[No description]'
				else:
					desc = '[MASKED]'

				self.pList.add([name.split('/')[1],inst[2],inst[3],new[2],new[3],desc])


	def clicked(self,evnt):
		global current, selected, searching
		box = self.lista.box[0]
		tmp = []

		box.select_clear(0,END)
		ind = box.nearest(evnt.y)
		box.select_set(ind)
		sel = box.get(box.curselection()[0])
		
		if sel == '...':
			current.pop()
			if len(current):
				tmp = ['...'] + packList[current[-1]].keys()
				tmp.sort()
			else:
				tmp = packList.keys()
				tmp.sort()
		
		elif len(current) >= 1:
			initList()
			for a in range(6):
				self.pList.box[a].delete(0, END)
			tmp = packList[current[0]][sel]
			tmp.sort()
			for a in tmp:
				name = "%s-%s/%s"%(current[0],sel,a)
				self.addPack(name)
			tmp = []
			if len(current) >= 2:
				del current[1]
			current.append(sel)

		
		elif len(current) == 0:
			tmp = ['...'] + packList[sel].keys()
			current.append(sel)
			tmp.sort()
		
		if len(tmp):
			self.lista.box[0].delete(0, END)
			for a in tmp:
				self.lista.add([a])
		searching = -1
		
		
		
app = App()
app.title('Portage GUI')
app.mainloop()
