#
# FtpCube
# Copyright (C) 2001 Michael Gilfix
#
# This file is part of FtpCube.
#
# You should have received a file COPYING containing license terms
# along with this program; if not, write to Michael Gilfix
# (mgilfix@eecs.tufts.edu) for a copy.
#
# This version of FtpCube is open source; you can redistribute it and/or
# modify it under the terms listed in the file COPYING.
#
# 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.
#

import GTK, gtk, GtkExtra
import time, re, os.path
import main, app, ftp

class RemoteWin:

	list = None
	dir = "/"
	status_label = None

	headings = [ 'Filename', 'Size', 'Date', 'Flags', 'Truesize' ]

	list_re = re.compile ('([A-Za-z-]+)\s+(\d+)\s+([A-Za-z0-9]+)\s+([A-Za-z0-9]+)\s+(\d+)\s+([A-Za-z]+\s+[0-9]+\s+[0-9:]+)\s+(.*)', re.IGNORECASE)
	link_re = re.compile ('(.*) -> (.*)')
	broken_link_re = re.compile ('.*-> (.*)')
	time_re = re.compile ('[A-Za-z]+\s+([A-Za-z]+\s+\d+\s+\d+:\d+):(\d+)\s+\d+')

	def __init__ (self, container):

		box = gtk.GtkVBox ()
		box.show ()
		container.add (box)

		win = gtk.GtkScrolledWindow ()
		win.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		win.show ()
		box.pack_start (win)

		bar = gtk.GtkHBox ()
		bar.set_usize (-1, 15)
		bar.show ()
		box.pack_start (bar, expand=GTK.FALSE)
		self.status_label = gtk.GtkLabel ()
		self.status_label.show ()
		bar.pack_start (self.status_label, expand=GTK.FALSE, padding=6)

		self.list = gtk.GtkCList (5, self.headings)
		self.list.connect ("select_row", self.select_items)
		self.list.connect ("button_press_event", self.popup_menu)

		# Set up list column defaults
		for i in range(len (self.headings)):
			self.list.set_column_width (i, 100)
			self.list.set_column_justification (i, gtk.JUSTIFY_LEFT)
		# Explicitly make the first column for the filename bigger
		self.list.set_column_width (0, 200)
		self.list.set_column_visibility (4, 0)

		self.list.set_selection_mode (GTK.SELECTION_MULTIPLE)
		win.add_with_viewport (self.list)
		self.list.show ()

		self.load_pixmaps ()

	def load_pixmaps (self):

		self.folder_pixmap, self.folder_mask = gtk.create_pixmap_from_xpm (self.list, None, main.ICONS_PREFIX + os.sep + "folder.xpm")
		self.link_pixmap, self.link_mask = gtk.create_pixmap_from_xpm (self.list, None, main.ICONS_PREFIX + os.sep + "link.xpm")
		self.file_pixmap, self.file_mask = gtk.create_pixmap_from_xpm (self.list, None, main.ICONS_PREFIX + os.sep + "file.xpm")

	def select_pixmap (self, listitem):

		file, size, date, flags, truesize = listitem
		if not flags:
			# It's probably a directory then
			return self.folder_pixmap, self.folder_mask
		else:
			if flags[0] == 'l':
				return self.link_pixmap, self.link_mask
			elif flags[0] == 'd':
				return self.folder_pixmap, self.folder_mask
			else:
				return self.file_pixmap, self.file_mask

	def select_items (self, list, r, c, event):

		if hasattr (event, 'type'):
			# Check for double clicks
			if event.type == 5:
				selected = list.get_pixtext (r, 0)[0]

				# Construct our new dir path
				if selected == "..":
					main.app.main_thread.cwd ('..')
					main.app.main_thread.list ()

					# Update the status directory
					text = self.get_dir ()
					index = string.rfind (text, "/")
					if index == 0:
						self.update_status_dir ("/")
					else:
						self.update_status_dir (text[:index])
				else:
					# Check if we have a directory
					flags = list.get_text (r, 3)
					if flags[0] in 'ld':
						main.app.main_thread.cwd (selected)
						main.app.main_thread.list ()

					# Update the status directory
					status_text = self.get_dir ()
					if status_text == "/":
						self.update_status_dir ("/" + selected)
					else:
						self.update_status_dir ("%s/%s" %(status_text, selected))

	def popup_menu (self, list, event):

		if event.button == 3 and not list.selection == []:
			opts = [
				("/Download Selected",   None, self.download_selected),
				("/View File",           None, None),
				("/Delete Selected",     None, self.delete_selected),
				("/Recursive Delete",    None, None),
				("/Rename",              None, None),
				("/FTPSearch this file", None, None),
				("/Create Directory",    None, None),
				("/<separator>",         None, None),
				("/Change Directory",    None, None),
				("/<separator>",         None, None),
				("/Compare Directories", None, None),
				("/Add Bookmarks",       None, None),
				("/<separator>",         None, None),
				("/Set Attributes",      None, None),
				("/Custom Commands",     None, None),
				("/Directory Info",      None, None),
				("/<separator>",         None, None),
				("/Sort Order/Unsorted (but dirs first)", None, None),
				("/Sort Order/Sort by Name",              None, None),
				("/Sort Order/Sort by Size",              None, None),
				("/Sort Order/Sort by Date",              None, None),
				("/Sort Order/<separator>",               None, None),
				("/Sort Order/Directories First",         None, None),
			]

			menu = GtkExtra.MenuFactory (GtkExtra.MENU_FACTORY_MENU)
			menu.add_entries (opts)
			menu.popup (None, None, None, event.button, event.time)
			menu.show ()

	def update_listing (self, listing):

		# Create the ".." entry
		match = self.time_re.match (time.ctime (time.time ()))
		localtime = match.group (1)
		dotdotentry = ('..', '0 bytes', localtime, '', '0')
		listitems = [ dotdotentry ]

		# Update the last listing array and parse the entries to update
		# the remote listing
		for entry in listing:
			tuple = self.parse_list_entry (entry)
			if tuple is None:
				continue
			else:
				file, truesize, date, flags = tuple

			# Beautify the size string
			size = app.beautify_size (truesize)

			# Check if the file is a link and if so, clean up the file name
			if flags and flags[0] == 'l':
				match = self.link_re.match (file)
				if match is not None:
					file, dest = match.groups ()
				else:
					# Try the broken link match
					match = self.broken_link_re.match (file)
					if match is not None:
						file = match.group (1)
						file = os.path.basename (file)
					else:
						print "WARNING: Match failed for: %s" %file

			listitems.append ((file, size, date, flags, "%s" %truesize))

		# Now change the list to reflect the new listing. Notice that we
		# pause the visuals during the update so it doesn't look funny
		self.list.freeze ()
		self.clear_items ()
		for i in range (len (listitems)):
			self.list.append (listitems[i])
			listitem = listitems[i]
			pixmap, mask = self.select_pixmap (listitem)
			self.list.set_pixmap (i, 0, pixmap, mask)
			self.list.set_pixtext (i, 0, listitem[0], 4, pixmap, mask)
		self.list.sort ()
		self.list.thaw ()

	def get_status (self):
		return self.status_label.get ()

	def get_dir (self):
		return self.dir

	def update_status_dir (self, dir):

		self.dir = dir
		self.update_status (self.dir)

	def update_status (self, text):
		self.status_label.set_text (text)

	def parse_list_entry (self, entry):

		match = self.list_re.match (entry)
		if match is None:
			#print "WARNING: listing match failed for: %s" %entry
			return None
		else:
			flags, hardlinks, user, group, size, date, file =  match.groups ()
			size = int (size)
			return file, size, date, flags

	def get_file_size (self, file):

		for i in range (self.list.rows):
			if self.list.get_pixtext (i, 0)[0] == file:
				return int (self.list.get_text (i, 4))
		return None

	def download_selected (self, button):

		files = [ ]

		# Extract the data from the CList. Note we don't use
		# get_row_data because it appears to be broken
		for i in self.list.selection:
			entry = [ ]
			entry.append (self.list.get_pixtext (i, 0)[0])
			# Get rest of entries
			for j in range (self.list.columns - 1):
				entry.append (self.list.get_text (i, j + 1))

			# Discard the ".." entry
			if entry[0] == "..":
				continue

			# Fix up the type
			if not entry[3][0] == '-':
				entry[3] = entry [3][0]
			else:
				entry[3] = 'f'

			files.append (tuple (entry))

		self.list.unselect_all ()
		main.app.transfer (self.get_dir (), main.app.get_local_dir (), files, ftp.DOWNLOAD)

	def delete_selected (self, button):

		for i in self.list.selection:
			file = self.list.get_pixtext (i, 0)[0]
			type = self.list.get_text (i, 3)[0]
			if type in 'l-':
				main.app.main_thread.delete (file)
			else:
				main.app.main_thread.rmdir (file)

		self.list.unselect_all ()
		main.app.main_thread.list ()

	def clear_items (self):
		self.list.clear ()
