# Copyright (c) 2003-2004 Hyriand. All rights reserved.
#
import gtk
import gobject

import re
import sre_constants
import locale
import string

from pynicotine import slskmessages

from nicotine_glade import SearchTab
from utils import InitialiseColumns, PopupMenu, FastListModel, Humanize
from dirchooser import ChooseDir

from pynicotine.utils import _

class Searches:
	def __init__(self, frame):
		self.frame = frame
		self.interval = 0
		self.searchid = 0
		self.searches = {}
		self.timer = None
		self.disconnected = 0
	
		frame.SearchNotebook.popup_enable()
		frame.combo1.disable_activate()
		
		items = self.frame.np.config.sections["searches"]["history"]
		frame.combo1.set_popdown_strings([""] + items)
		
	def SetInterval(self, msg):
		self.interval = 1000
		
		if not self.disconnected:
			for term in self.frame.np.config.sections["server"]["autosearch"]:
				self.CreateTab(self.searchid, term, 0, 1)
				self.searchid += 1
		
		self.OnAutoSearch()
		self.timer = gobject.timeout_add(self.interval*1000, self.OnAutoSearch)
	
	def ConnClose(self):
		self.disconnected = 1
		if self.timer is not None:
			gobject.source_remove(self.timer)
			self.timer = None
	
	def OnAutoSearch(self, *args):
		if self.interval == 0:
			return gtk.FALSE
		
		searches = self.frame.np.config.sections["server"]["autosearch"]
		if not searches:
			return gtk.TRUE
		
		term = searches.pop()
		searches.insert(0, term)
		
		for i in self.searches.values():
			if i[1] == term and i[4]:
				if i[2] == None:
					break
				self.DoGlobalSearch(i[0], term)
				break
		
		return gtk.TRUE
	
	def OnSearch(self):
		text = self.frame.SearchEntry.get_text().strip()
		self.frame.SearchEntry.set_text("")
		if not text:
			return
		
		s = self.frame.np.config.sections["searches"]["history"]
		if text in s:
			s.remove(text)
		s.insert(0, text)
		del s[15:]
		self.frame.np.config.writeConfig()
		self.frame.combo1.set_popdown_strings([""] + s)
		
		if self.frame.GlobalRadio.get_active():
			mode = 0
		elif self.frame.RoomsRadio.get_active():
			mode = 1
		else:
			mode = 2
		self.DoSearch(text, mode)
		
	def DoSearch(self, text, mode, users = []):
		self.CreateTab(self.searchid, text, mode)
		text = self.frame.np.encode(text)
		if mode == 0:
			self.DoGlobalSearch(self.searchid, text)
		elif mode == 1:
			self.DoRoomsSearch(self.searchid, text)
		elif mode == 2:
			self.DoBuddiesSearch(self.searchid, text)
		else:
			self.DoPeerSearch(self.searchid, text, users)
		self.searchid += 1
		
	def DoGlobalSearch(self, id, text):
		self.frame.np.queue.put(slskmessages.FileSearch(id, text))
	
	def DoRoomsSearch(self, id, text):
		users = []
		for room in self.frame.chatrooms.roomsctrl.joinedrooms.values():
			for user in room.users.keys():
				if not user in users:
					users.append(user)
		self.DoPeerSearch(id, text, users)

	def DoBuddiesSearch(self, id, text):
		users = [i[0] for i in self.frame.userlist.userlist]
		self.DoPeerSearch(id, text, users)
	
	def DoPeerSearch(self, id, text, users):
		for user in users:
			self.frame.np.ProcessRequestToPeer(user, slskmessages.FileSearchRequest(None,id,text))

	def CreateTab(self, id, text, mode, remember = gtk.FALSE):
		tab = Search(self, text, id, mode, remember)

		if mode:
			label = "(" + ("", _("Rooms"), _("Buddies"), _("User"))[mode] + ") " + text
		else:
			label = text
		self.frame.SearchNotebook.append_page(tab.vbox7, label, tab.OnCloseIgnore)

		search = [id, text, tab, mode, remember]
		self.searches[id] = search
		return search
		
	def ShowResult(self, msg, username, country):
		if not self.searches.has_key(msg.token):
			return
		
		search = self.searches[msg.token]
		if search[2] == None:
			search = self.CreateTab(search[0], search[1], search[3], search[4])
		
		search[2].AddResult(msg, username, country)

	def RemoveAutoSearch(self, id):
		if not id in self.searches:
			return
		search = self.searches[id]
		if search[1] in self.frame.np.config.sections["server"]["autosearch"]:
			self.frame.np.config.sections["server"]["autosearch"].remove(search[1])
			self.frame.np.config.writeConfig()
		search[4] = 0
		
	def RemoveTab(self, tab):
		if self.searches.has_key(tab.id):
			search = self.searches[tab.id]
			search[2] = None
			if search[4]:
				self.RemoveAutoSearch(search[0])
		
		self.frame.SearchNotebook.remove_page(tab.vbox7)

	def AutoSearch(self, id):
		if not self.searches.has_key(id):
			return
		i = self.searches[id]
		if i[1] in self.frame.np.config.sections["server"]["autosearch"]:
			return
		self.frame.np.config.sections["server"]["autosearch"].append(i[1])
		self.frame.np.config.writeConfig()
		i[4] = 1
	
class SearchTreeModel(FastListModel):
	COLUMNS = 14
	COLUMN_TYPES = [gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING,
    			gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING,
    			gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_INT]

	def __init__(self):
		FastListModel.__init__(self)
		self.all_data = []
		self.filters = None
		self.sort_col = 1
		self.sort_order = gtk.SORT_DESCENDING
		
	def append(self, results):
		ix = len(self.all_data) + 1
		l = len(self.data)
		returned = 0
		
		for r in results:
			size = r[2]
			r[2] = Humanize(size)
			speed = r[3]
			r[3] = Humanize(speed)
			queue = r[4]
			r[4] = Humanize(queue)
			row = [ix] + r + [size, speed, queue]
			self.all_data.append(row)
			if not self.filters or self.check_filter(row):
				self.data.append(row)
				iter = self.get_iter((l,))
				self.row_inserted((l,), iter)
				l += 1
				returned += 1
			ix += 1
		
		return returned

	def sort(self):
		col = self.sort_col
		order = self.sort_order
		if col == 3:
			col = 13
		elif col == 4:
			col = 14
		elif col == 5:
			col = 15

		if self.COLUMN_TYPES[col] == gobject.TYPE_STRING:
			compare = locale.strcoll
		else:
			compare = cmp

		if order == gtk.SORT_ASCENDING:
			self.data.sort(lambda r1,r2: compare(r1[col], r2[col]))
			self.all_data.sort(lambda r1,r2: compare(r1[col], r2[col]))
		else:
			self.data.sort(lambda r2,r1: compare(r1[col], r2[col]))
			self.all_data.sort(lambda r2,r1: compare(r1[col], r2[col]))

	def checkDigit(self, filter, value, factorize = gtk.TRUE):
		op = ">="
		if filter[:1] in (">", "<", "="):
			op, filter = filter[:1]+"=", filter[1:]

		if not filter:
			return gtk.TRUE

		factor = 1
		if factorize:
			if filter.lower()[-1] == "g":
				factor = 1024*1024*1024
				filter = filter[:-1]
			elif filter.lower()[-1] == "m":
				factor = 1024*1024
				filter = filter[:-1]
			elif filter.lower()[-1] == "k":
				factor = 1024
				filter = filter[:-1]

		if not filter:
			return gtk.TRUE

		if not filter.isdigit():
			return gtk.TRUE

		filter = long(filter) * factor

		if eval(str(value)+op+str(filter), {}):
			return gtk.TRUE

		return gtk.FALSE

	def check_filter(self, row):
		filters = self.filters
		if filters[0] and not filters[0].search(row[1].lower()):
			return gtk.FALSE
		if filters[1] and filters[1].search(row[1].lower()):
			return gtk.FALSE
		if filters[2] and not self.checkDigit(filters[2], row[13]):
			return gtk.FALSE
		if filters[3] and not self.checkDigit(filters[3], row[10], gtk.FALSE):
			return gtk.FALSE
		if filters[4] and row[6] != "Y":
			return gtk.FALSE
		if filters[5]:
			for cc in filters[5]:
				if not cc:
					continue
				if cc[0] == "-":
					if row[12] == cc[1:]:
						return gtk.FALSE
				elif cc != row[12]:
					return gtk.FALSE
		return gtk.TRUE
	
	def set_filters(self, enable, f_in, f_out, size, bitrate, freeslot, country):
		if not enable:
			self.filters = None
			self.data = self.all_data[:]
			return
		self.filters = [None, None, None, None, freeslot, None]
		
		if f_in:
			try:
				f_in = re.compile(f_in.lower())
				self.filters[0] = f_in
			except sre_constants.error:
				pass
		
		if f_out:
			try:
				f_out = re.compile(f_out.lower())
				self.filters[1] = f_out
			except sre_constants.error:
				pass
		
		if size:
			self.filters[2] = size
		
		if bitrate:
			self.filters[3] = bitrate

		if country:
			self.filters[5] = country.upper().split(" ")

		self.data = []
		for row in self.all_data:
			if self.check_filter(row):
				self.data.append(row)

class Search(SearchTab):
	def __init__(self, searches, text, id, mode, remember):
		SearchTab.__init__(self, gtk.FALSE)

#		self.ResultsList.set_double_buffered(gtk.FALSE)

		self.searches = searches
		self.frame = searches.frame
		self.text = text
		self.id = id
		self.mode = mode
		self.remember = remember
		self.users = []

		self.resultsmodel = SearchTreeModel()

		self.FilterIn.disable_activate()
		self.FilterOut.disable_activate()
		self.FilterSize.disable_activate()
		self.FilterBitrate.disable_activate()
		self.FilterCountry.disable_activate()

		if self.frame.np.config.sections["searches"]["enablefilters"]:
			filter = self.frame.np.config.sections["searches"]["defilter"]
			self.FilterIn.entry.set_text(filter[0])
			self.FilterOut.entry.set_text(filter[1])
			self.FilterSize.entry.set_text(filter[2])
			self.FilterBitrate.entry.set_text(filter[3])
			self.FilterFreeSlot.set_active(filter[4])
			if(len(filter) > 5):
				self.FilterCountry.entry.set_text(filter[5])
			self.checkbutton1.set_active(1)

		if mode > 0:
			self.RememberCheckButton.set_sensitive(gtk.FALSE)
		self.RememberCheckButton.set_active(remember)

		self.selected_results = []
		self.selected_users = []

		self.ResultsList.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
		
		cols = InitialiseColumns(self.ResultsList,
			["", 20, "text", self.CellDataFunc],
			[_("Filename"), 250, "text", self.CellDataFunc],
			[_("User"), 100, "text", self.CellDataFunc],
			[_("Size"), 100, "text", self.CellDataFunc],
			[_("Speed"), 50, "text", self.CellDataFunc],
			[_("In queue"), 50, "text", self.CellDataFunc],
			[_("Immediate Download"), 20, "text", self.CellDataFunc],
			[_("Bitrate"), 50, "text", self.CellDataFunc],
			[_("Length"), 50, "text", self.CellDataFunc],
			[_("Directory"), 1000, "text", self.CellDataFunc],
		)
		self.ResultsList.set_model(self.resultsmodel)
		for ix in range(len(cols)):
			col = cols[ix]
			col.connect("clicked", self.OnResort, ix)
			for r in col.get_cell_renderers():
				r.set_fixed_height_from_font(1)
		self.OnResort(cols[0], 0)

		self.ResultsList.set_headers_clickable(gtk.TRUE)
		
		self.popup_menu = popup = PopupMenu(self.frame)
		popup.setup(
			(_("Download file(s)"), self.OnDownloadFiles),
			(_("Download file(s) to..."), self.OnDownloadFilesTo),
			(_("Download containing folder(s)"), self.OnDownloadFolders),
			("", None),
			(_("Copy URL"), self.OnCopyURL),
			(_("Copy folder URL"), self.OnCopyDirURL),
			("", None),
			(_("Send message"), popup.OnSendMessage),
			(_("Show IP address"), popup.OnShowIPaddress),
			(_("Get user info"), popup.OnGetUserInfo),
			(_("Browse files"), popup.OnBrowseUser),
			("$" + _("Add user to list"), popup.OnAddToList),
			("$" + _("Ban this user"), popup.OnBanUser),
			("$" + _("Ignore this user"), popup.OnIgnoreUser),
		)
		
		self.ResultsList.connect("button_press_event", self.OnListClicked)
		
		self._more_results = 0
		self.new_results = []
	
	def SelectedResultsCallback(self, model, path, iter):
		user = model.get_value(iter, 2)
		fn = model.get_value(iter, 11)
		
		self.selected_results.append((user, fn))
		
		if not user in self.selected_users:
			self.selected_users.append(user)
	
	def OnListClicked(self, widget, event):
		if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
			self.selected_results = []
			self.selected_users = []
			self.ResultsList.get_selection().selected_foreach(self.SelectedResultsCallback)
			self.OnDownloadFiles(widget)
			self.ResultsList.get_selection().unselect_all();
			return gtk.TRUE
		elif event.button == 3:
			return self.OnPopupMenu(widget, event)
		return gtk.FALSE
		
	def OnPopupMenu(self, widget, event):
		if event.button != 3:
			return gtk.FALSE
		
		self.selected_results = []
		self.selected_users = []
		self.ResultsList.get_selection().selected_foreach(self.SelectedResultsCallback)
		
		items = self.popup_menu.get_children()
		
		act = len(self.selected_results) and gtk.TRUE or gtk.FALSE
		for i in range(0, 4):
			items[i].set_sensitive(act)

		if len(self.selected_results) == 1:
			act = gtk.TRUE
		else:
			act = gtk.FALSE
		items[4].set_sensitive(act)
		items[5].set_sensitive(act)

		act = gtk.FALSE
		if len(self.selected_users) == 1:
			act = gtk.TRUE
			user = self.selected_users[0]
			self.popup_menu.set_user(user)
			items[11].set_active(user in [i[0] for i in self.frame.np.config.sections["server"]["userlist"]])
			items[12].set_active(user in self.frame.np.config.sections["server"]["banlist"])
			items[13].set_active(user in self.frame.np.config.sections["server"]["ignorelist"])
		
		for i in range(7, 14):
			items[i].set_sensitive(act)
		
		widget.emit_stop_by_name("button_press_event")
		self.popup_menu.popup(None, None, None, event.button, event.time)
		return gtk.TRUE
		
	def AddResult(self, msg, user, country):
		if user in self.users:
			return
		self.users.append(user)
		
		results = []
		if msg.freeulslots:
			imdl = _("Y")
		else:
			imdl = _("N")
		ix = len(self.resultsmodel.data)
		decode = self.frame.np.decode
		for result in msg.list:
			name = result[1].split('\\')[-1]
			dir = result[1][:-len(name)]
			bitrate = ""
			length = ""
			br = 0
			if result[3] == "mp3" and len(result[4]) == 3:
				a = result[4]
				if a[2] == 1:
					bitrate = _(" (vbr)")
				bitrate = str(a[0]) + bitrate
				br = a[0]
				length = '%i:%02i' %(a[1] / 60, a[1] % 60)
			results.append([decode(name), user, result[2], msg.ulspeed, msg.inqueue, imdl, bitrate, length, decode(dir), br, result[1], country])
			ix += 1
			
		if results:
			self.new_results += results
			
			if self._more_results == 0:
				self._more_results = 1
				gobject.timeout_add(1000, self._realaddresults)
			else:
				self._more_results = 2
			return len(results)
	
	def _realaddresults(self):
		if self._more_results == 2:
			self._more_results = 1
			return gtk.TRUE
		
		r = self.new_results
		self.new_results = []
		self._more_results = 0

		res = self.resultsmodel.append(r)

		if res:
			self.frame.SearchNotebook.request_changed(self.vbox7)
			self.frame.RequestIcon(self.frame.SearchTabLabel)

		rows = len(self.resultsmodel.data)
		for c in self.ResultsList.get_columns():
			for r in c.get_cell_renderers():
				r.set_fixed_height_from_font(1)

		return gtk.FALSE
		
	def CellDataFunc(self, column, cellrenderer, model, iter):
		imdl = model.get_value(iter, 6)
		colour = imdl == _("Y") and "search" or "searchq"
		colour = self.frame.np.config.sections["ui"][colour] or None
		cellrenderer.set_property("foreground", colour)

	def OnDownloadFiles(self, widget, prefix = ""):
		if not self.frame.np.transfers:
			return
		for file in self.selected_results:
			self.frame.np.transfers.getFile(file[0], file[1], prefix)
	
	def OnDownloadFilesTo(self, widget):
		dir = ChooseDir(self.frame.MainWindow, self.frame.np.config.sections["transfers"]["downloaddir"])
		if dir is None:
			return
		self.OnDownloadFiles(widget, dir)
	
	def OnDownloadFolders(self, widget):
		folders = []
		for i in self.selected_results:
			dir = string.join(i[1].split("\\")[:-1], "\\")
			if (i[0], dir) in folders:
				continue
			self.frame.np.ProcessRequestToPeer(i[0], slskmessages.FolderContentsRequest(None, dir))
			folders.append((i[0], dir))

	def OnCopyURL(self, widget):
		user, path = self.selected_results[0][:2]
		self.frame.SetClipboardURL(user, path)

	def OnCopyDirURL(self, widget):
		user, path = self.selected_results[0][:2]
		path = string.join(path.split("\\")[:-1], "\\") + "\\"
		self.frame.SetClipboardURL(user, path)

	def OnToggleFilters(self, widget):
		if widget.get_active():
			self.Filters.show()
			self.OnRefilter(None)
		else:
			self.Filters.hide()
			self.ResultsList.set_model(None)
			self.resultsmodel.set_filters(0, None, None, None, None, None, "")
			self.ResultsList.set_model(self.resultsmodel)

	def OnIgnore(self, widget):
		self.RememberCheckButton.set_active(0)
		self.RememberCheckButton.set_sensitive(0)
		self.OnToggleRemember(self.RememberCheckButton)

		if self.id in self.searches.searches.keys():
			del self.searches.searches[self.id]
		
		widget.set_sensitive(gtk.FALSE)

	def OnClose(self, widget):
		self.searches.RemoveTab(self)

	def OnCloseIgnore(self, widget):
		self.OnIgnore(self.button2)
		self.OnClose(widget)

	def OnToggleRemember(self, widget):
		self.remember = widget.get_active()
		if not self.remember:
			self.searches.RemoveAutoSearch(self.id)
		else:
			self.searches.AutoSearch(self.id)

	def PushHistory(self, widget, title):
		text = widget.entry.get_text()
		if not text.strip():
        		return None
		history = self.frame.np.config.sections["searches"][title]
		self.frame.np.config.pushHistory(history, text, 5)
		widget.set_popdown_strings([""] + history)
		widget.entry.set_text(text)
		return text
		
	def OnRefilter(self, widget):
		f_in = self.PushHistory(self.FilterIn, "filterin")
		f_out = self.PushHistory(self.FilterOut, "filterout")
		f_size = self.PushHistory(self.FilterSize, "filtersize")
		f_br = self.PushHistory(self.FilterBitrate, "filterbr")
		f_free = self.FilterFreeSlot.get_active()
		f_country = self.PushHistory(self.FilterCountry, "filtercc")
		
		self.ResultsList.set_model(None)
		self.resultsmodel.set_filters(1, f_in, f_out, f_size, f_br, f_free, f_country)
		self.ResultsList.set_model(self.resultsmodel)

	def OnResort(self, column, column_id):
		if self.resultsmodel.sort_col == column_id:
			order = self.resultsmodel.sort_order
			if order == gtk.SORT_ASCENDING:
				order = gtk.SORT_DESCENDING
			else:
				order = gtk.SORT_ASCENDING
			column.set_sort_order(order)
			self.resultsmodel.sort_order = order
			self.ResultsList.set_model(None)
			self.resultsmodel.sort()
			self.ResultsList.set_model(self.resultsmodel)
			return
		cols = self.ResultsList.get_columns()
		cols[self.resultsmodel.sort_col].set_sort_indicator(gtk.FALSE)
		cols[column_id].set_sort_indicator(gtk.TRUE)
		self.resultsmodel.sort_col = column_id
		self.OnResort(column, column_id)
