#
# 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
import string # For backwards compatibility with 1.5.x
import os
import cmdwin, ctrlwin, localwin, remotewin, connectwin, bookmarkwin
import optionwin, aboutwin
import main, ftp, threads, defaults

if os.environ.has_key ('HOME'):
	CONFIG_DIR = os.environ['HOME'] + os.sep + ".ftpcube"
else:
	CONFIG_DIR = main.PREFIX

class Application:

	main_window = None

	cmdwin = None
	localwin = None
	remotewin = None

	main_thread = None

	opts = { }

	def __init__ (self, title):

		self.config_file = self.check_configuration_files ()

		self.main_window = gtk.GtkWindow (gtk.WINDOW_TOPLEVEL)
		self.main_window.set_title (title)
		self.main_window.set_default_size (gtk.screen_width (), gtk.screen_height ())
		self.main_window.connect ("destroy", self.quit)

		packer = gtk.GtkPacker ()
		self.main_window.add (packer)
		packer.show ()

		self.menu_init (packer)
		self.toolbar_init (packer)

		# Create the command window
		box = gtk.GtkVBox ()
		box.set_usize (-1, 150)
		self.cmdwin = cmdwin.CmdWin (box)
		packer.add (box, side=gtk.SIDE_TOP, anchor=gtk.ANCHOR_NW, options=GTK.FILL_X)
		box.show ()

		# Create the control windows and store them in a container
		table = gtk.GtkTable (3, 1)
		table.set_homogeneous (gtk.FALSE)
		table.show ()
		packer.add (table, side=gtk.SIDE_TOP, anchor=gtk.ANCHOR_NW, options=GTK.PACK_EXPAND+GTK.FILL_X+GTK.FILL_Y)

		# Create the control window
		box = gtk.GtkFrame ()
		self.ctrlwin = ctrlwin.CtrlWin (box)
		table.attach (box, 0, 1, 0, 1, xoptions=GTK.FILL, yoptions=GTK.FILL+GTK.EXPAND)
		box.show ()

		# Create the local window
		box = gtk.GtkFrame ()
		self.localwin = localwin.LocalWin (box)
		table.attach (box, 1, 2, 0, 1, yoptions=GTK.FILL+GTK.EXPAND)
		box.show ()

		# Create the remote window
		box = gtk.GtkFrame ()
		self.remotewin = remotewin.RemoteWin (box)
		table.attach (box, 2, 3, 0, 1, yoptions=GTK.FILL+GTK.EXPAND)
		box.show ()

		self.main_window.show ()

	def __getitem__ (self, key):
		return self.opts[key]

	def __setitem__ (self, key, val):

		self.opts[key] = val
		return self.opts[key]

	def keys (self):
		return self.opts.keys ()

	def check_configuration_files (self):

		config_file = CONFIG_DIR + os.sep + "config"

		# Create configuration directory and files if necessary
		if not os.path.exists (CONFIG_DIR):
			os.mkdir (CONFIG_DIR)
		if not os.path.exists (config_file):
			if os.name == 'posix':
				default_opts = defaults.unix
			self.save_config (config_file, default_opts)

		# Load the configuration file
		options = self.load_config (config_file)
		for o in options.keys ():
			self[o] = options[o]
		return config_file

	def get_config_file (self):
		return self.config_file

	def load_config (self, path):

		config = { }
		file = open (path)
		line = file.readline ()
		while line:
			if line[-2] == '\r\n':
				line = line[:-2]
			elif line[-1] in '\r\n':
				line = line[:-1]
			index = string.find (line, '=')
			key = line[:index]
			value = line[index + 1:]
			if value == "None":
				config[key] = None
			else:
				# Check if we have an integer and convert it
				# if we do
				try:
					i = int (value)
					config[key] = i
				except ValueError, strerror:
					config[key] = value
			line = file.readline ()
		file.close ()
		return config

	def save_config (self, path, opts):

		file = open (path, 'w')
		for key in opts.keys ():
			if opts[key] is not None:
				file.write ("%s=%s\n" %(key, opts[key]))
			else:
				file.write ("%s=\n" %key)
		file.close ()

	def menu_init (self, container):

		# Add the keyboard accelerators to the main window
		ag = gtk.GtkAccelGroup ()
		item = gtk.GtkItemFactory (gtk.GtkMenuBar, "<main>", ag)
		self.main_window.add_accel_group (ag)

		# Assign references to menu handlers
		file = self.menu_process_file
		help = self.menu_process_help

		item.create_items ([
			('/_File',          None,        None,        0,   '<Branch>'),
			('/_File/_Connect', None,        file,        1,   ''),
			('/_File/_Quick Connect', None,  file,        2,   ''),
			('/_File/_Bookmarks',  None,     file,        3,   ''),
			('/_File/_Disconnect', None,     file,        4,   ''),
			('/_File/_Options', None,        file,        5,   ''),
			('/_File/<Separator>', None,     None,        0,   '<Separator>'),
			('/_File/E_xit',    '<alt>F4',   file,        6,   ''),
			('/_Help',          None,        None,        0,   '<Branch>'),
			('/_Help/_About',   None,        help,        7,   '')
		])
		mainmenu = item.get_widget ('<main>')

		# Now add the menu bar into the canvas
		menubox = gtk.GtkVBox ()
		menubox.pack_start (mainmenu, expand=gtk.FALSE)
		mainmenu.show ()
		menubox.show ()
		container.add (menubox, side=gtk.SIDE_TOP, anchor=gtk.ANCHOR_NW, options=GTK.FILL_X)

	def menu_process_file (self, action, widget):

		if action == 0:
			print "Unknown action occurred"
		elif action == 1:
			self.connect_window ()
		elif action == 2:
			self.quick_connect_window ()
		elif action == 3:
			self.bookmark_window ()
		elif action == 4:
			self.disconnect ()
		elif action == 5:
			self.options_window ()
		elif action == 6:
			self.quit (self)

	def menu_process_help (self, action, widget):

		if action == 7:
			self.about_window ()

	def toolbar_init (self, container):

		toolbox = gtk.GtkHandleBox ()
		toolbar = gtk.GtkToolbar (gtk.ORIENTATION_HORIZONTAL, gtk.TOOLBAR_ICONS)

		connect_pic, connect_mask = gtk.create_pixmap_from_xpm (self.main_window, None, main.ICONS_PREFIX + os.sep + "connect.xpm")
		quick_pic, quick_mask = gtk.create_pixmap_from_xpm (self.main_window, None, main.ICONS_PREFIX + os.sep + "quick_connect.xpm")
		bookmark_pic, bookmark_mask = gtk.create_pixmap_from_xpm (self.main_window, None, main.ICONS_PREFIX + os.sep + "bookmark.xpm")
		abort_pic, abort_mask = gtk.create_pixmap_from_xpm (self.main_window, None, main.ICONS_PREFIX + os.sep + "abort.xpm")

		def connect_button (_b, toolbar=toolbar, self=self):
			self.connect_window ()

		def quick_connect_button (_b, toolbar=toolbar, self=self):
			self.quick_connect_window ()

		def bookmark_button (_b, toolbar=toolbar, self=self):
			self.bookmark_window ()

		def abort_button (_b, toolbar=toolbar, self=self):
			self.abort ()

		toolbar.append_item ("Normal Connect", "Normal Connect",
			"Normal Connect",
			gtk.GtkPixmap (connect_pic, connect_mask),
			connect_button)
		toolbar.append_item ("Quick Connect", "Quick Connect",
			"Quick Connect",
			gtk.GtkPixmap (quick_pic, quick_mask),
			quick_connect_button)
		toolbar.append_item ("Bookmarks", "Bookmarks",
			"Bookmarks",
			gtk.GtkPixmap (bookmark_pic, bookmark_mask),
			bookmark_button)
		toolbar.append_item ("Abort", "Abort Current Actions",
			"Abort Current Actions",
			gtk.GtkPixmap (abort_pic, abort_mask),
			abort_button)

		toolbar.show ()
		toolbox.add (toolbar)
		toolbox.show ()
		container.add (toolbox, side=gtk.SIDE_TOP, anchor=gtk.ANCHOR_NW, options=GTK.FILL_X)

	def connect_window (self):

		if self.main_thread is None:
			connect_box = connectwin.ConnectWin (self.initiate_connection, self)
		else:
			self.display_disconnect_box ()

	def quick_connect_window (self):

		if self.main_thread is None:
			quick_connect_box = connectwin.QuickConnectWin (self.initiate_connection, self)
		else:
			self.display_disconnect_box ()

	def bookmark_window (self):
		bookmark_box = bookmarkwin.BookmarkWin (CONFIG_DIR)

	def display_disconnect_box (self):

		def callback (disconnect=self.disconnect, connect=self.initiate_connection, self=self):

			disconnect ()
			connect_box = connectwin.ConnectWin (connect, self)

		disconnect_box = DisconnectBox (callback)

	def disconnect (self):

		if self.main_thread is not None:
			self.main_thread.cancel_thread ()

	def options_window (self):
		option_box = optionwin.OptionWindow ()

	def about_window (self):
		about_box = aboutwin.AboutWindow ()

	def initiate_connection (self, connectwin):

		# Copy over the options into the main option hash
		opts = connectwin.get_options ()
		for o in opts.keys ():
			self[o] = opts[o]

		self.main_thread = threads.MainThread ()
		self.ctrlwin.attach_main_thread (self.main_thread)
		self.main_thread.initiate_connect ()

	def abort (self):

		if self.main_thread is not None:
			self.main_thread.abort ()

	def ftp_local_tell_user (self, msg):
		self.cmdwin.insert_local_text (msg)

	def ftp_remote_tell_user (self, msg):
		self.cmdwin.insert_remote_text (msg)

	def update_remote_status_dir (self, dir):
		self.remotewin.update_status_dir (dir)

	def get_ftp_listing (self):
		self.main_thread.list ()

	def update_remote_listing (self, dir):
		self.remotewin.update_listing (dir)

	def update_local_listing (self, dir):
		self.localwin.update_listing (dir)

	def get_local_dir (self):
		return self.localwin.get_dir ()

	def get_remote_dir (self):
		return self.remotewin.get_dir ()

	def transfer (self, remote_path, local_path, files, direction):

		server = None
		maxattempts = None

		server = self['host']
		maxattempts = self['retries']
		if server is None: server = "Unknown"
		if maxattempts is None: maxattempts = 1

		self.ctrlwin.add_to_queue (remote_path, local_path, files, server, maxattempts, direction)

	def add_to_download_list (self, file):
		self.ctrlwin.add_to_downloads (file)

	def get_cwd (self):
		return self.localwin.get_dir ()

	def get_file_size (self, file):
		return self.remotewin.get_file_size (file)

	def quit (self, win):
		gtk.mainquit ()

class DisconnectBox:

	dialog = None

	def __init__ (self, callback):

		self.callback = callback

		self.dialog = gtk.GtkDialog ()
		self.dialog.set_title ("ftpcube - Disconnect?")
		self.dialog.set_policy (gtk.FALSE, gtk.FALSE, gtk.TRUE)
		self.dialog.set_position (gtk.WIN_POS_CENTER)
		self.dialog.show ()

		box = gtk.GtkHBox (spacing=75)
		box.show ()
		self.dialog.action_area.add (box, padding=95)

		button = gtk.GtkButton ("Ok")
		button.set_usize (50, 25)
		button.connect ("clicked", self.ok)
		box.pack_start (button, expand=gtk.FALSE)
		button.show ()

		button = gtk.GtkButton ("Cancel")
		button.set_usize (50, 25)
		button.connect ("clicked", self.cancel)
		box.pack_start (button, expand=gtk.FALSE)
		button.show ()

		label = gtk.GtkLabel ("You are currently connected to: %s\nDo you wish to break the connection and connect to a new host?" %main.app['host'])
		label.show ()
		self.dialog.vbox.add (label, padding=15)

	def ok (self, button):

		self.dialog.hide ()
		self.dialog.destroy ()
		self.callback ()

	def cancel (self, button):

		self.dialog.hide ()
		self.dialog.destroy ()

# Conversion function from bytes sizes to pretty strings
def beautify_size (size):

	if size / 1073741824:
		return "%0.2f Gbytes" %(float (size) / 1073741824.0)
	elif size / 1048576:
		return "%0.2f Mbytes" %(float (size) / 1048576.0)
	elif size / 1024:
		return "%0.2f KBytes" %(float (size) / 1024.0)
	else:
		return "%s Bytes" %(size)
	return None
