// MainWindow.cs
//
//  Copyright (C) 2008-2010 Christian Eide
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

using System;
using System.Collections;
using System.Collections.Generic;
using Gtk;
using bareFTP.Protocol;
using bareFTP.Connection;
using bareFTP.Gui.FileManager;
using bareFTP.Gui.Dialog;
using bareFTP.Common.Utils;

namespace bareFTP
{
 //TODO: This class is beyond bloated and messed up.. Needs some reorganizing
	
	public partial class MainWindow: Gtk.Window
	{	
		//private string version;
		ProtocolActionQueue pq;
	
		protected Gtk.TreeView treeview1;
		private ProtocolHost pHost;
		private long fileid = 0;
		private bareFTP.Gui.ProgressMonitor.ProgressList progresslist;
		private Connection.Connection conn;
	
		private FileManagerLocal file_local;
		private FileManagerRemote file_remote;
		private bareFTP.Gui.BookmarkUtils bookmarkutils;
		private ComboBox combobox1; 
		private Gtk.ListStore ls;
		private Gtk.TextBuffer logbuffer;
		private string remote_server = string.Empty;
		private Preferences.Config conf;
		private uint statusbar_context_id;
		private uint statusbar2_context_id;
		private Gui.ActivityProgressBar progressbar;
		private System.Threading.Thread connectthread;
		private Gui.KeyBinder keybindings;
		private AccelGroup agroup;
		
		public MainWindow (Preferences.Config conf, bool debug): base (Gtk.WindowType.Toplevel)
		{
			
			Mono.Unix.Catalog.Init("bareftp", Defines.LOCALE_DIR);
			this.conf = conf;
			
			if(conf.GUI_Maximized)
				this.Maximize();
			
			Build ();

			progressbar = new Gui.ActivityProgressBar();
			progressbar.WidthRequest = 150;
			hbox4.PackStart(progressbar, false, false, 0);
			this.hbox4.ShowAll();
			
			disconnect.Sensitive = false;
			pHost = new ProtocolHost();
			this.notebook1.SwitchPage += new SwitchPageHandler(OnNotebookClicked);
				
			file_local = new FileManagerLocal(conf);
			file_remote = new FileManagerRemote(conf, progressbar);
			this.Title = "bareFTP " + bareFTP.MainClass.Version;
			string title = Title.Clone().ToString();
			logbuffer = this.textview1.Buffer;
			
			
			TextTag tag = new TextTag("default");
			tag.Font = conf.GUI_MessageFont;
			logbuffer.TagTable.Add(tag);
			
			tag = new TextTag("welcome");
			tag.Foreground = "dark green";
			tag.Font = conf.GUI_MessageFont;
			logbuffer.TagTable.Add(tag);
			
			tag = new TextTag("error");
			tag.Foreground = "red";
			tag.Font = conf.GUI_MessageFont;
			logbuffer.TagTable.Add(tag);
			
			tag = new TextTag("client");
			tag.Foreground = "dark blue";
			tag.Font = conf.GUI_MessageFont;
			logbuffer.TagTable.Add(tag);
			
			hbox2.PackStart(file_local, true, true, 0);
			hbox2.ReorderChild(file_local,0);
			
			hpaned1.Pack2(file_remote, true, true);
			hpaned1.Remove(hbox2);
			hpaned1.Pack1(hbox2, true, true);
			hpaned1.ShowAll();
			
			combobox1 = bareFTP.Gui.Common.MakeProtocolComboBox();
			ls = (ListStore)combobox1.Model;
			hbox1.PackStart(combobox1, false, false, 10);
			hbox1.ReorderChild(combobox1,9);
			
			combobox1.Show();
			combobox1.Active = conf.NetworkDefaultProtocol - 1;
			
			conf.DefaultProtocolChanged += delegate {
				if(conn == null || !conn.Connected)
				combobox1.Active = conf.NetworkDefaultProtocol - 1;
			};
			
			progresslist = new bareFTP.Gui.ProgressMonitor.ProgressList();
			progresslist.XferFileReordered += new EventHandler(OnXferFileReordered);
			progresslist.XferFileAbort += delegate {
				//if(pq != null)
				//	pq.AbortAllTransfers();
				conn.Abort();
			};
			
			progresslist.ProgressEvent += delegate(object sender, EventArgs e) {
				
				bareFTP.Gui.ProgressMonitor.ProgressEventArgs args = e as bareFTP.Gui.ProgressMonitor.ProgressEventArgs;
				Title = args.CompletedTotal + "%" + " - " + args.Speed;
			};
			this.scrolledwindow2.Add(progresslist);
			this.scrolledwindow2.ShowAll();
			
			file_local.DownloadRequest += onDownloadClicked;
			file_local.UploadRequest += onUploadClicked;
			file_local.DirChanged += onLocalDirChanged;
			file_remote.DownloadRequest += onDownloadClicked;
			file_remote.UploadRequest += onUploadClicked;
			file_remote.DirChanged += onRemoteDirChanged;
			file_remote.UserAction += on_user_action;
			file_local.ChangeDir(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal));
			file_remote.ChangeDir(null, false);
			file_remote.ExternalUploadRequest += new ExternalUploadRequestHandler(onExternalUpload);
			file_remote.StatusBarMessage += OnStatusBarMessage;
			
			conf.ShowHiddenFilesChanged += delegate {
				// First remove setting from bookmark if we have it
				file_local.ShowHidden = string.Empty;
				file_remote.ShowHidden = string.Empty;
				
				file_local.ChangeDir(file_local.CurrentDir.LocalPath);
				if(conn != null && conn.Connected)
					file_remote.ChangeDir(file_remote.CurrentDir.RemotePath, false);
			};
			this.btn_ClearTransfers.TooltipText = Mono.Unix.Catalog.GetString("Clear finished transfers");
			this.WindowStateEvent += winstate;
			this.ExposeEvent += exposed;
			
			statusbar_context_id = statusbar1.GetContextId("ErrorMsg");
			statusbar2_context_id = statusbar2.GetContextId("StateInfo");
			
			bookmarkutils = new bareFTP.Gui.BookmarkUtils();
			bookmarkutils.BookmarkMenuBar = menubar1;
			bookmarkutils.Refresh();
			
			bookmarkutils.BookmarkActivated += onBookmarkActivated;
			
			hbox1.PackStart(bookmarkutils.BookmarkButton, false, false, 0);
			hbox1.ReorderChild(bookmarkutils.BookmarkButton, 0);
			
			AddKeybindings();
			
			this.entryHost.GrabFocus();
			
			this.WindowStateEvent += delegate(object o, WindowStateEventArgs args) {
				if(args.Event.NewWindowState == Gdk.WindowState.Iconified)
					progresslist.WindowHidden = true;
				else
				{
					progresslist.WindowHidden = false;
					Title = title;
				}
			};
			
		}
		
		

		protected void onBookmarkActivated(object sender, EventArgs e)
		{
			if(conn != null && conn.Connected)
			{
				if(ConfirmNewConnection())
				{
					Disconnect();
				}
				else
					return;
			}
			
			bareFTP.Gui.BookmarkMenuItem item = sender as bareFTP.Gui.BookmarkMenuItem;
			Preferences.Bookmarks.BookmarkEntry entry = item.BookmarkEntry;
			entryHost.Text = entry.Host;
			entryUser.Text = entry.User;
			entryPassword.Text = entry.Pass;
			entryPort.Text = entry.Port;
			combobox1.Active = entry.Protocol - 1;
			SynchronizedBrowsingAction.Active = entry.SyncedBrowse;
			Connect(entry);
		}
		protected void winstate(object sender, Gtk.WindowStateEventArgs args)
		{
			Gdk.EventWindowState ews = (Gdk.EventWindowState)args.Event;
			conf.GUI_Maximized = (ews.NewWindowState == Gdk.WindowState.Maximized);
		}
		
		protected void exposed(object sender, EventArgs args)
		{
				if(conf.GUI_HPanedPosition >= 0)
					hpaned1.Position = conf.GUI_HPanedPosition;
				if(conf.GUI_VPanedPosition >= 0)
					vpaned1.Position = conf.GUI_VPanedPosition;
				this.ExposeEvent -= exposed;
		}
		
		protected void onLocalDirChanged(object sender, EventArgs args)
		{
			if(SynchronizedBrowsingAction.Active)
				if(!file_remote.ChangeDirSynchronized(sender.ToString()))
					DisableSynchBrowsing();
		}
		
		protected void onRemoteDirChanged(object sender, EventArgs args)
		{
			if(SynchronizedBrowsingAction.Active)
				if(!file_local.ChangeDirSynchronized(sender.ToString()))
					DisableSynchBrowsing();
		}
		
		private void DisableSynchBrowsing()
		{
			SynchronizedBrowsingAction.Active = false;
			this.statusbar2.Pop(statusbar2_context_id);
			this.statusbar2.Push(statusbar2_context_id, Mono.Unix.Catalog.GetString("Syncronized browsing disabled"));
		}
		
		private void Upload(List<LocalFile> files, BarePath bpath)
		{
			bpath.SetBaseLocal(file_local.CurrentDir.LocalPath);
			statusbar1.Pop(statusbar_context_id);
			
			// Create a Dictionary<dir, List<files>> to organize our paths and files
			Dictionary<string, List<RemoteFile>> paths = new Dictionary<string,List<RemoteFile>>(); 
			
			// Adding current dir and remote files
			paths.Add("", file_remote.CurrentDirRemoteFiles);
			
			bool showFileActionDialog = false;
			
			// Create list of files and directories that will get transfered
			List<XferFile> xferfiles = new List<XferFile>();
			List<XferFile> xferdirs = new List<XferFile>();
			List<string> neededdirs = new List<string>();
			
			List<LocalFile> lfiles = null;

			if(files == null)
				lfiles = file_local.SelectedFiles;
			else
				lfiles = files;
			
			
			foreach(LocalFile lfile in lfiles)
			{
				// Create xferfile
				
				string dir = lfile.Path.Replace(lfile.RootDir, string.Empty).Trim(System.IO.Path.DirectorySeparatorChar);
				lfile.Permissions = PermissionParser.UFAPToMode(new Mono.Unix.UnixFileInfo(lfile.Path).FileAccessPermissions);
				
				BarePath xcwd = new BarePath();
				xcwd.SetBaseLocal(lfile.RootDir);
				xcwd.SetBaseRemote(bpath.RemotePath);
				
				if(!lfile.Isdir)
				{
					xcwd.SetFileName(string.Empty, dir);
					XferFile file = new XferFile(GetNewFileId(), xcwd, 0, TransferDirection.Upload, lfile.Isdir, remote_server);
					file.Permissions = lfile.Permissions;
					// Compare remote
					
					long marker = getRemoteFileMarker(dir, paths);
					// If the marker is > 0 the file exists remote
					if(marker > 0)
					{
						file.Marker = marker;
						showFileActionDialog = true;
					}
					else
						file.Action = bareFTP.Protocol.FileAction.Overwrite;
					xferfiles.Add(file);
				}
				else
				{
					// Do we need to create this directory?
					List<string> myneededdirs = new List<string>();
					getRemoteNeededDirs(dir, myneededdirs, paths);
					foreach(string s in myneededdirs)
					{
						if(!neededdirs.Contains(s) && s.Length > 0)
						{
							foreach(string element in s.Split(System.IO.Path.DirectorySeparatorChar))
									xcwd.AddRelativeElement(element);
							
							XferFile file = new XferFile(GetNewFileId(), xcwd, 0, TransferDirection.Upload, true, remote_server);
							file.Permissions = lfile.Permissions;
							if(conf.General_SimultaneousTransfers)
								xferdirs.Add(file);
							else
								xferfiles.Add(file);
							neededdirs.Add(s);
							
						}
					}
				}
			}
			
			if(showFileActionDialog)
			{
				AskFileAction md = new AskFileAction(xferfiles);
				ResponseType result = (ResponseType)md.Run ();

				if (result != ResponseType.Ok)
				{
					md.Destroy();
					return;
				}
				else
					md.Destroy();
			}
			
			// In case of simultaneous transfer, send all dirs first
			if(conf.General_SimultaneousTransfers)
			{
				foreach(XferFile file in xferdirs)
				{
					if(file.Action != bareFTP.Protocol.FileAction.Skip)
					{
						pq.Add(new ActionFTPSendFile(file));
					}
				}	
			}
			
			foreach(XferFile file in xferfiles)
			{
				if(file.Action != bareFTP.Protocol.FileAction.Skip)
				{
					pq.Add(new ActionFTPSendFile(file));
					// for now we don't send directory "transfers" to progress window..
					if(!file.IsDir)
						progresslist.AppendList(file);
				}
			}
			
			pq.StartTransfers();
			notebook1.CurrentPage = 0;
			
		}
		
		protected virtual void onUploadClicked(object sender, System.EventArgs e)
		{
			if(conn != null && conn.Connected)
			{
				BarePath rcwd = new BarePath();
				rcwd.SetBaseRemote(file_remote.CurrentDir.RemotePath);
				if(sender != null && sender is FileTreeNode)
				{
					FileTreeNode rnode = sender as FileTreeNode;
					if(rnode.IsDir)
						rcwd.AddElementRemote(rnode.FileName);
				}
				Upload(null, rcwd);
			}
		}
		
		protected virtual void onExternalUpload(List<string> lfiles, FileTreeNode rnode)
		{
			
			if(conn != null && conn.Connected)
			{
				BarePath rcwd = new BarePath();
				rcwd.SetBaseRemote(file_remote.CurrentDir.RemotePath);
				if(rnode != null && rnode.IsDir)
					rcwd.AddElementRemote(rnode.FileName);
				
				Upload(FileUtils.CompleteLocalPaths(lfiles, file_local.CurrentDir.LocalPath), rcwd);
			}
			
		}
		
		private void Download(BarePath cwd)
		{
			statusbar1.Pop(statusbar_context_id);
			bool showFileActionDialog = false;
			List<XferFile> xferfiles = new List<XferFile>();
			List<string> neededdirs = new List<string>();
			
			foreach(RemoteFile rfile in file_remote.SelectedFileTree)
			{
				rfile.Path.SetBaseLocal(file_local.CurrentDir.LocalPath);
				
				if(!rfile.IsDir)
				{
					XferFile file = new XferFile(GetNewFileId(), rfile.Path,  rfile.Size, TransferDirection.Download, false, remote_server);
					file.Permissions = rfile.Permissions;
					long marker = getLocalFileMarker(System.IO.Path.Combine(file.Path.RelativePathLocal, file.Path.FileName));
					if(marker > 0)
					{
						showFileActionDialog = true;
						file.Marker = marker;
					}
					else
						file.Action = bareFTP.Protocol.FileAction.Overwrite;
					
					xferfiles.Add(file);
				}
				else
				{
					// Do we need to create this directory?
					List<string> myneededdirs = new List<string>();
					getLocalNeededDirs(rfile.Path, myneededdirs);
					//getRemoteNeededDirs(dir, myneededdirs, paths);
					foreach(string s in myneededdirs)
					{
						if(!neededdirs.Contains(s) && s.Length > 0)
						{
							BarePath tpath = new BarePath();
							tpath.SetBaseRemote(file_remote.CurrentDir.ToString(true));
							tpath.SetBaseLocal(file_local.CurrentDir.LocalPath);
							tpath.AddRelativeElement(s);
							
							XferFile file = new XferFile(GetNewFileId(), tpath, 0, TransferDirection.Download, true, remote_server);
							file.Permissions = rfile.Permissions;
							xferfiles.Add(file);
							neededdirs.Add(s);
						}
					}
				}
			}
			
			if(showFileActionDialog)
			{
				AskFileAction md = new AskFileAction(xferfiles);
				ResponseType result = (ResponseType)md.Run ();

				if (result != ResponseType.Ok)
				{
					md.Destroy();
					return;
				}
				else
					md.Destroy();
			}
			
			foreach(XferFile file in xferfiles)
			{
				if(file.Action != bareFTP.Protocol.FileAction.Skip)
				{
					pq.Add(new ActionFTPGetFile(file));
					// for now we don't send directory "transfers" to progress window..
					if(!file.IsDir)
						progresslist.AppendList(file);
				}
			}
			
			pq.StartTransfers();
			notebook1.CurrentPage = 0;
		}
		// We are doing pretty much the same here as above. Could this be cleaned up a bit?
		protected virtual void onDownloadClicked(object sender, System.EventArgs e)
		{
			
			if(conn != null && conn.Connected)
			{
				BarePath cwd = new BarePath();
				cwd.SetBaseRemote(file_remote.CurrentDir.RemotePath);
				if(sender != null && sender is FileTreeNode)
				{
					FileTreeNode node = sender as FileTreeNode;
					if(node.IsDir)
						cwd.AddElementLocal(node.FileName);
				}
				try
				{
					Download(cwd);
				}
				catch(Exception ex)
				{
					Console.WriteLine(ex.ToString());
				}
			}
		}
		
		protected virtual void onFileEdit(object sender, System.EventArgs e)
		{
			
		}
		
		protected void OnXferFileReordered(object sender, EventArgs e)
		{
			bareFTP.Gui.ProgressMonitor.XferFileReorderedArgs args = e as bareFTP.Gui.ProgressMonitor.XferFileReorderedArgs;
			pq.Reorder(args.File, args.Direction);
		}
		
		protected void OnDeleteEvent (object sender, DeleteEventArgs a)
		{
			CleanUpBeforeExit();
			Application.Quit ();
			a.RetVal = true;
		}
		
		private long getLocalFileMarker(string path)
		{
			string lpath = System.IO.Path.Combine(file_local.CurrentDir.LocalPath, path);
			if(System.IO.File.Exists(lpath))
			{
				System.IO.FileInfo fi = new System.IO.FileInfo(lpath);
				return fi.Length;
			}
			else
				return 0;
		}
		
		private void getLocalNeededDirs(BarePath path, List<string> neededdirs)
		{
			string[] parts = path.RelativePathLocal.Split(System.IO.Path.DirectorySeparatorChar);
			string tmp = string.Empty;
			foreach(string s in parts)
			{
				tmp += s + System.IO.Path.DirectorySeparatorChar.ToString();
				if(!System.IO.Directory.Exists(tmp.TrimEnd(new char[] {'/','.'})))
					if(!neededdirs.Contains(tmp.TrimEnd(new char[] {'/','.'})))
						neededdirs.Add(tmp.TrimEnd(new char[] {'/','.'}));
			}
		}
		
		private void getRemoteNeededDirs(string dir, List<string> neededdirs, Dictionary<string, List<RemoteFile>> paths)
		{
			BarePath bpath = new BarePath();
			bpath.SetBaseRemote(file_remote.CurrentDir.RemotePath);
			
			bool resetCurrentDir = false;
			
			string[] parts = dir.Split(System.IO.Path.DirectorySeparatorChar);
			
			List<string> dirs = new List<string>();
			string tmp = string.Empty;
			
			for(int y=0;y<parts.Length;y++)
			{				
				if(tmp.Length > 0)
					tmp += System.IO.Path.DirectorySeparatorChar;
				tmp += parts[y];
				dirs.Add(tmp);
			}
			
			for(int x=0;x<dirs.Count;x++)
			{
				string _d = string.Empty;
				if(x > 0)
					_d = dirs[x-1];
				if(paths.ContainsKey(_d))
				{
					bool has_dir = false;
					foreach(RemoteFile f in paths[_d])
					{
						if(f.Filename == parts[x] && f.IsDir)
							has_dir = true;
						
					}
					// We found the dir remote. Enter it and fill paths..
					if(has_dir)
					{
						if(!paths.ContainsKey(dirs[x]))
						{
							bpath.AddRelativeElement(dirs[x]);
							conn.SetCurrentDirectory(bpath.FileNameRemoteAbs);
							resetCurrentDir = true;
							paths.Add(dirs[x], conn.XDir());
						}
					}
					else
					{
						if(!neededdirs.Contains(dirs[x]))
							neededdirs.Add(dirs[x]);
					}
				}
				else
				{
					if(!neededdirs.Contains(dirs[x]))
							neededdirs.Add(dirs[x]);
				}
			}
			
			if(resetCurrentDir)
				conn.SetCurrentDirectory(file_remote.CurrentDir.RemotePath);
			
		}
		
		private long getRemoteFileMarker(string path, Dictionary<string, List<RemoteFile>> paths)
		{
			long marker = 0;
			string dir = "";
			string filename = "";
			BarePath bpath = new BarePath();
			bpath.SetBaseRemote(file_remote.CurrentDir.RemotePath);
			bool resetCurrentDir = false;
			
			if(path.IndexOf(System.IO.Path.DirectorySeparatorChar) > 0)
			{
				// Separate directory tree and filename
				dir = path.Substring(0, path.LastIndexOf(System.IO.Path.DirectorySeparatorChar));
				filename = path.Substring(path.LastIndexOf(System.IO.Path.DirectorySeparatorChar)+1);
			}
			else
			{
				filename = path;
			}
			
			string[] dirs = dir.Split(System.IO.Path.DirectorySeparatorChar);
			string td = "";
			
			for(int x=0;x<=dirs.Length;x++)
			{
				if(paths.ContainsKey(td))
				{
					bool has_dir = false;
					foreach(RemoteFile f in paths[td])
					{
						if(f.Filename == dir && f.IsDir)
						{
							if(!string.IsNullOrEmpty(td))
								td += System.IO.Path.DirectorySeparatorChar;
							td += dirs[x];
							has_dir = true;
						}
						
					}
					// We found the dir remote. Enter it and fill paths..
					if(has_dir)
					{
						if(!paths.ContainsKey(td))
						{
							bpath.AddRelativeElement(td);
							conn.SetCurrentDirectory(bpath.RemotePathAbs);
							resetCurrentDir = true;
							paths.Add(td, conn.XDir());
						}
					}
				}
			}
			
			if(!paths.ContainsKey(dir))
				return 0;
			
			foreach(RemoteFile file in paths[dir])
			{
				if(file.Filename == filename)
				{	
					marker = file.Size;
					break;
				}
			}
			
			if(resetCurrentDir)
				conn.SetCurrentDirectory(bpath.RemotePath);
			return marker;
			
		}
		
		
		private void CleanUpBeforeExit()
		{
			conf.GUI_HPanedPosition = hpaned1.Position;
			conf.GUI_VPanedPosition = vpaned1.Position;
			conf.GUI_RightColWidths = file_remote.ColumnWidths;
			conf.GUI_LeftColWidths = file_local.ColumnWidths;
			try
			{
				if(pq != null)
					pq.AbortAllTransfers();
				if(conn != null)
					if(conn.Connected)
						conn.Close();
			}
			catch(Exception ex)
			{
				Console.WriteLine(ex.ToString());
			}
		}
		protected virtual void onMenuQuit (object sender, System.EventArgs e)
		{
			CleanUpBeforeExit();
			
			Application.Quit ();
		}
		
		private void OnQueueEmpty(object sender, EventArgs e)
		{
			if(conn.CleanUp())
			{
				file_local.ChangeDir("");
				try
				{
					file_remote.ChangeDir("", false);
				}
				catch {}
			}
		}
		
		private void OnLogTextEmitted(object sender, EventArgs e)
		{
			Gtk.Application.Invoke( delegate {
				Gdk.Color color = new Gdk.Color();
				TextIter startIter = logbuffer.EndIter;
				LogTextEmittedArgs args = e as LogTextEmittedArgs;
				List<FTPReply> reply = args.Reply;
				string error = string.Empty;

				foreach(FTPReply r in reply)
				{
					if(r.MessageType == bareFTP.Protocol.MessageType.Info)
					{
						logbuffer.InsertWithTagsByName(ref startIter, r.ToString() + Environment.NewLine, "default");
					}
					else if(r.MessageType == bareFTP.Protocol.MessageType.Error || (r.MessageType == bareFTP.Protocol.MessageType.ClientError))
					{
						logbuffer.InsertWithTagsByName(ref startIter, r.ToString() + Environment.NewLine, "error");
						if(!r.Silent)
						{
							error =r.Message;
						}
					}
					else if(r.MessageType == bareFTP.Protocol.MessageType.Welcome)
					{
						logbuffer.InsertWithTagsByName(ref startIter, r.ToString() + Environment.NewLine, "welcome");
					}
					else if(r.MessageType == bareFTP.Protocol.MessageType.ClientCommand)
					{
						logbuffer.InsertWithTagsByName(ref startIter, r.Message + Environment.NewLine, "client");
					}
					else
						logbuffer.InsertWithTagsByName(ref startIter, r.ToString() + Environment.NewLine, "default");
					
				}
				
				if(logbuffer.LineCount > 500)
				{
					int excesslines = logbuffer.LineCount - 200;
					TextIter beginning = logbuffer.GetIterAtLine(0);
					TextIter rem = logbuffer.GetIterAtLine(excesslines);
					logbuffer.Delete(ref beginning, ref rem);
					
				}
				
				textview1.ScrollMarkOnscreen(logbuffer.InsertMark);
				
				if(notebook1.CurrentPage != 1)
				{
					if(!string.IsNullOrEmpty(error))
					{
						Gdk.Color.Parse("red", ref color);
						Console.WriteLine("Error: " + error);
					}
					else
					{
						Gdk.Color.Parse("blue", ref color);
					}

					lblMessages.ModifyFg(StateType.Active, color);
				}
				
				if(!string.IsNullOrEmpty(error))
					SetStatusBarMessage("Error: " + error, true);
			});
		}
		protected void on_user_action(object sender, EventArgs e)
		{
			statusbar1.Pop(statusbar_context_id);
		}
		private void OnNotebookClicked(object sender, EventArgs e)
		{		
			if(this.notebook1.CurrentPage == 1)
			{
				Gdk.Color color = new Gdk.Color();
				Gdk.Color.Parse("black", ref color);
				lblMessages.ModifyFg(StateType.Active, color);
			}
		}

		protected virtual void onMenuAbout (object sender, System.EventArgs e)
		{
			
			bareFTP.AboutDialog dialog = new bareFTP.AboutDialog ();
            dialog.Run ();
            dialog.Destroy ();			
		}
		
		public long GetNewFileId()
		{
			return fileid++;
		}
		
		protected virtual void OnConnectClicked (object sender, System.EventArgs e)
		{	
			Connect(null);
		}
		
		protected virtual void OnDisconnectClicked (object sender, System.EventArgs e)
		{
			Disconnect();
		}
		
		private void Disconnect()
		{
			pq.AbortAllTransfers();
			try
			{
				conn.Close();
				file_remote.RemoveConnection();
			} catch {}
			
			progressbar.Stop();
			file_remote.Sensitive = true;
			progresslist.ClearFinished(true);
			btnDisconnect.Visible = false;
			btnConnect.Visible = true;
			remote_server = string.Empty;
			bookmarkutils.BookmarkButton.Sensitive = true;
		}
		
		private ConnectionProperties conn_props;
		
		private void Connect(Preferences.Bookmarks.BookmarkEntry bookmark)
		{
			if(conn != null && conn.Connected)
			{
				if(!ConfirmNewConnection())
					return;
			}
			
			if(string.IsNullOrEmpty(entryHost.Text.Trim()))
				return;
			
			if(bookmark != null)
			{
				if(!string.IsNullOrEmpty(bookmark.User) && string.IsNullOrEmpty(bookmark.Pass))
				{
					bareFTP.Gui.Dialog.AskPasswordDialog ad = new bareFTP.Gui.Dialog.AskPasswordDialog();
					if(ad.Run() == (int)ResponseType.Ok)
					{
						entryPassword.Text = ad.Password;
						bookmark.Pass = ad.Password;
					}
					ad.Destroy();
				}
			}
			SetStatusBarMessage(Mono.Unix.Catalog.GetString("Connecting") + "...", false);
			
			btnConnect.Sensitive = false;
			conn_props = new ConnectionProperties();
			conn_props.Hostname = entryHost.Text.Trim();
			conn_props.User = entryUser.Text.Trim();
			conn_props.Password = entryPassword.Text;
			conn_props.Bookmark = bookmark;
			conn_props.Protocol = combobox1.Active;
			conn_props.Passive = conf.FTP_PassiveMode;
			
			if(bookmark != null)
				conn_props.RemoteCharset = bookmark.CharSet;
			else
				conn_props.RemoteCharset = conf.General_RemoteCharset;
			
			int port = -1;
			if(!string.IsNullOrEmpty(entryPort.Text))
			{
				if(!Int32.TryParse(entryPort.Text, out port))
				{
					Gui.Dialog.Dialogs.ErrorDialog(Mono.Unix.Catalog.GetString("Invalid port number"));
				}
			}
			
			conn_props.Port = port;
			Gtk.TreeIter iter;
			combobox1.GetActiveIter(out iter);
			bareFTP.Protocol.ProtocolType type = (bareFTP.Protocol.ProtocolType)ls.GetValue(iter,0);
			
			conn = new Connection.Connection();
			conn.ConnProperties = conn_props;
			conn.DialogHost = pHost;
			conn.Protocol = type;
			conn.Configuration = conf;
			
			pq = new bareFTP.Connection.ProtocolActionQueue(conn);
			pq.QueueEmpty += new EventHandler(OnQueueEmpty);
			conn.LogTextEmitted += new EventHandler(OnLogTextEmitted);
			
			connectthread = new System.Threading.Thread(DoConnect);
			connectthread.Start();
			
		}

		private void DoConnect()
		{
			try
			{
				abortButton.Sensitive = true;
				logbuffer.Clear();
				progressbar.Start();
				bookmarkutils.BookmarkButton.Sensitive = false;
				conn.Open();
			}
			catch(Exception ex)
			{
				conn = null;
				progressbar.Stop();
				if(ex is System.Threading.ThreadAbortException)
				{
					Gtk.Application.Invoke( 
			  			delegate {
							SetStatusBarMessage(Mono.Unix.Catalog.GetString("Operation aborted"), true);
						});
				}
				else
				{
					Gtk.Application.Invoke( 
			  			delegate {
							//SetStatusBarMessage("ERROR: " + ex.Message, true);
						OnLogTextEmitted(this, new LogTextEmittedArgs(bareFTP.Protocol.MessageType.Error, ex.Message));
						});
				}
				
			}
			finally
			{
				progressbar.Stop();
				abortButton.Sensitive = false;
				Gtk.Application.Invoke( 
			  delegate {
				btnConnect.Sensitive = true;
				bookmarkutils.BookmarkButton.Sensitive = true;
				});
				
			}
			
			Gtk.Application.Invoke( 
			  delegate
			  {
				
				if(conn != null && conn.Connected)
				{
					SetStatusBarMessage(Mono.Unix.Catalog.GetString("Connected"), false);
					
					// Give our connection to the remote file manager.
					// TODO: This may have to be changed, so the remote file manager controls this itself.
					string startDir = string.Empty;
					if(conn_props.Bookmark != null)
					{
						if(!string.IsNullOrEmpty(conn_props.Bookmark.RemotePath))
							startDir = conn_props.Bookmark.RemotePath;
						if(!string.IsNullOrEmpty(conn_props.Bookmark.LocalPath))
							file_local.ChangeDir(conn_props.Bookmark.LocalPath);
						else
						{
							if(conn.ConnectionCount <= 1)
								file_local.ChangeDir(file_local.CurrentDir.LocalPath);
						}
						file_local.ShowHidden = conn_props.Bookmark.ShowHidden.ToString();
						file_remote.ShowHidden = conn_props.Bookmark.ShowHidden.ToString();
						
					}
					
					file_remote.SetConnection(conn, startDir);
					btnDisconnect.Visible = true;
					btnConnect.Visible = false;
					remote_server = conn_props.Hostname;
				}
			  });
		}
		
		protected virtual void onPreferencesClicked (object sender, System.EventArgs e)
		{
			new bareFTP.Gui.Preferences.PreferencesDialog(conf);
		}

		protected virtual void OnEntriesActivated (object sender, System.EventArgs e)
		{
			Connect(null);
		}

		protected virtual void OnCleanProgressListClicked (object sender, System.EventArgs e)
		{
			this.progresslist.ClearFinished(false);
		}

		protected void OnStatusBarMessage(object sender, EventArgs e)
		{
			StatusBarMessageArgs args = e as StatusBarMessageArgs;
			if(args != null)
			{
				Gtk.Application.Invoke(delegate {
					SetStatusBarMessage(args.Msg, args.Error);
				});
			}
		}
		
		private void SetStatusBarMessage(string msg, bool error)
		{
			
			string color = "black";
			if(error)
				color = "red";
			
			try
			{
				//TODO: Find these initially.. don't look em up every time..
				Gtk.Frame f = null;
				Gtk.Label l = null;
				
				IEnumerator ie = statusbar1.AllChildren.GetEnumerator();
				while(ie.MoveNext())
				{
					if(ie.Current is Frame)
					{
						f = (Frame)ie.Current;
						IEnumerator i2 = f.AllChildren.GetEnumerator();
						while(i2.MoveNext())
						{
							if(i2.Current is Label)
							{
								l = (Label)i2.Current;
								break;
							}
							if(i2.Current is HBox)
							{
								HBox b = (HBox)i2.Current;
								IEnumerator i3 = b.AllChildren.GetEnumerator();
								while(i3.MoveNext())
								{
									if(i3.Current is Label)
									l = (Label)i3.Current;
									break;
								}
							}
							
						}
					}
				}
				
				if(l != null)
				{
					Gdk.Color scol = Gdk.Color.Zero;
					if(!Gdk.Color.Parse(color, ref scol))
						throw new Exception("No such color");
					l.ModifyFg(StateType.Normal, scol);
					l.UseMarkup = true;
				}
			}
			catch{}
			
			if(msg != null)
			{
				this.statusbar1.Pop(statusbar_context_id);
				this.statusbar1.Push(statusbar_context_id, msg);
			}
		}

		protected virtual void onEditBookmarksClicked (object o, EventArgs args)
		{
			bareFTP.Gui.Preferences.BookmarkDialog d = new bareFTP.Gui.Preferences.BookmarkDialog();
			d.Run();
			//Consider using cancel in the dialog.. now we always save..
			d.SaveBookmarks();
			d.Destroy();
			
			bookmarkutils.Refresh();
		}

		protected virtual void onAddBookmarkClicked (object sender, System.EventArgs e)
		{
			if(conn == null || !conn.Connected)
			{
				bareFTP.Gui.Dialog.Dialogs.ErrorDialog(Mono.Unix.Catalog.GetString("You need to connect to a remote site first"));
				return;
			}
			
			bareFTP.Gui.Preferences.BookmarkDialog d = new bareFTP.Gui.Preferences.BookmarkDialog(conn.ConnProperties);
			
			d.NewBookmarkLocalPath = file_local.CurrentDir.LocalPath;
			d.NewBookmarkRemotePath = file_remote.CurrentDir.RemotePathAbs;
			d.NewBookmarkSyncedBrowse = SynchronizedBrowsingAction.Active;
			d.InsertNewBookmark();
			
			//Consider using cancel in the dialog.. now we always save..
			d.Run();
			d.SaveBookmarks();
			d.Destroy();
			
			bookmarkutils.Refresh();
		}

		protected virtual void onAbortClicked (object sender, System.EventArgs e)
		{
			connectthread.Abort();
			abortButton.Sensitive = false;
		}
		
		private void AddKeybindings()
		{
			
			agroup = new AccelGroup();
			AddAccelGroup(agroup);
			keybindings = new Gui.KeyBinder(agroup);
			
			keybindings.AddAccelerator(delegate {
				file_local.GrabFocus();
			}, (uint)Gdk.Key.Left, Gdk.ModifierType.ControlMask, Gtk.AccelFlags.Visible);
			keybindings.AddAccelerator(delegate {
				file_remote.GrabFocus();
			}, (uint)Gdk.Key.Right, Gdk.ModifierType.ControlMask, Gtk.AccelFlags.Visible);	
			keybindings.AddAccelerator(delegate {
				if(file_remote.HasFocus)
					file_remote.CdUp();
				else if(file_local.HasFocus)
					file_local.CdUp();
			}, (uint)Gdk.Key.Up, Gdk.ModifierType.Mod1Mask, AccelFlags.Visible);
			
			keybindings.AddAccelerator(delegate {
				if(file_remote.HasFocus)
					file_remote.ChangeDir(file_remote.CurrentDir.FileNameRemoteAbs, false);
				else if(file_local.HasFocus)
					file_local.ChangeDir(file_remote.CurrentDir.FileNameLocalAbs);
			}, (uint)Gdk.Key.r, Gdk.ModifierType.ControlMask, AccelFlags.Visible);
			
		}
		
		private bool ConfirmNewConnection()
		{
			return bareFTP.Gui.Dialog.Dialogs.QuestionDialog(Mono.Unix.Catalog.GetString("This will close the current connection. Do you want to continue connecting?"));
		}
		
		protected virtual void onSynchBrowseToggeled (object sender, System.EventArgs e)
		{
			this.statusbar2.Pop(statusbar2_context_id);
			if(SynchronizedBrowsingAction.Active)
				this.statusbar2.Push(statusbar2_context_id, Mono.Unix.Catalog.GetString("Syncronized browsing enabled"));
		}
		
		
	}
}
