// Connection.cs
//
//  Copyright (C) 2008 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.Generic;
using bareFTP.Protocol;
using System.Threading;

namespace bareFTP.Connection
{	
	public class Connection
	{
		private List<IProtocol> connections;
		private ProtocolType protocol_type;
		private ConnectionProperties conn_props;
		private IDialogHost dialog_host;
		private bareFTP.Preferences.Config config;
		
		public Connection()
		{
			connections = new List<IProtocol>();
		}
		
		public int ConnectionCount
		{
			get { return connections.Count; }	
		}
		
		private IProtocol GetConnection()
		{
			lock(connections)
			{
				IProtocol c;
				if(connections.Count == 0)
				{
					connections.Add(CreateConnection());
					c = connections[0];
				}
				else
				{
					c = FindAvailableConnection();
					if(c is IProtocol)
						return c;
					c = CreateConnection();
					c.Open(conn_props.Hostname, conn_props.Port, conn_props.User, conn_props.Password);
					c.SetCurrentDirectory(connections[0].CurrentDirectory);
					connections.Add(c);
				}
				return c;
			}
			
		}
		
		private IProtocol FindAvailableConnection()
		{
			foreach(IProtocol conn in connections)
			{
				if(!conn.TransferInProgress)
					return conn;
			}
			return null;
		}
		
		private IProtocol GetTransferConnection()
		{
			foreach(IProtocol conn in connections)
			{
					if(conn.TransferInProgress)
						return conn;
			}
			return GetConnection();
		}

		private IProtocol CreateConnection()
		{
			IProtocol conn = null;
			switch(protocol_type)
			{
				case bareFTP.Protocol.ProtocolType.FTP:
					conn = new bareFTP.Protocol.Ftp.FTPConnection(conn_props);
					break;
				case bareFTP.Protocol.ProtocolType.FTPS:
				    conn_props.FTPS = true;
					conn = new bareFTP.Protocol.Ftp.FTPConnection(conn_props);
				break;
				case bareFTP.Protocol.ProtocolType.SFTP:
					conn = new bareFTP.Protocol.Sftp.SftpConnection(conn_props);
				break;
				default:
					Console.WriteLine("Oooops..");
					break;
			}
			
			conn.Init(dialog_host, Configuration);
			conn.LogTextEmitted += OnLogTextEmitted;
			conn.LostConnection += OnConnectionLost;
			
			return conn;
		}
		
		public bool CleanUp()
		{
			Mutex m = new Mutex();
			m.WaitOne();
			for(int x=connections.Count-1; x >= 1; x--)
			{
				if(!connections[x].TransferInProgress)
				{
					connections[x].Close();
					connections.RemoveAt(x);
					//Console.WriteLine("Lukker og rydder");
				}
			}
			m.ReleaseMutex();
			
			if(connections.Count > 1)
				return false;
			return true;
		}
		
		public event EventHandler LogTextEmitted;
		public virtual void OnLogTextEmitted(object sender, EventArgs e)
		{
			LogTextEmitted(this, e);
		}
	
		public virtual void OnConnectionLost(object dead_conn, EventArgs e)
		{
			/*
			IProtocol dead = (IProtocol)dead_conn;
			
			CreateConnection();
			
			int pos = connections.FindIndex(dead);
			CleanUp();
			Open();
			*/
			//ConnectionLost(cwd, null);
		}
		
		public ProtocolType Protocol
		{
			get { return protocol_type; }
			set { protocol_type = value; }
		}

		
		public ConnectionProperties ConnProperties {
			get { return conn_props; }
			set { conn_props = value; }
		}
		
		public IDialogHost DialogHost {
			get { return dialog_host; }
			set { dialog_host = value; }
		}
		
		public bareFTP.Preferences.Config Configuration {
			get { return config; }
			set { config = value; }
		}

		
		// IProtocol implementation
		public void Open()
		{
			Open(conn_props.Hostname, conn_props.Port, conn_props.User, conn_props.Password);
		}
		
		public void Open(string remoteHost, int remotePort, string user, string password)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.Open(remoteHost, remotePort, user, password);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void Close()
		{
			if(ConnectionCount > 0)
				GetConnection().Close();
		}
		
		public bool Connected 
		{ 
			get
			{
				try{
					foreach(IProtocol conn in connections)
					{
						if(conn.Connected)
							return true;
					}
					
					return false;
				}
				catch { return false; }
			}
		}
		
		public void SendCommand(string command)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			
			try
			{
				p.SendCommand(command);
				p.TransferInProgress = false;
			}
			finally { p.TransferInProgress = false; }
		}
		
		public List<string> Dir()
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				List<string> res = p.Dir();
				p.TransferInProgress = false;
				return res;
			}
			finally { p.TransferInProgress = false; }
		}
		
		public List<RemoteFile> XDir()
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				List<RemoteFile> res = p.XDir();
				p.TransferInProgress = false;
				return res;
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void SendFile(XferFile file)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.SendFile(file);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void GetFile(XferFile file)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.GetFile(file);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void DeleteFile(string remoteFileName)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.DeleteFile(remoteFileName);
			} 
			finally { p.TransferInProgress = false; }
		}
		
		public void MoveFile(string remoteFileName, string toRemotePath)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.MoveFile(remoteFileName, toRemotePath);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void RenameFile(string fromRemoteFileName, string toRemoteFileName)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.RenameFile(fromRemoteFileName, toRemoteFileName);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void SetCurrentDirectory(string remotePath)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.SetCurrentDirectory(remotePath);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public bool IsDir(string remotePath)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				bool res = p.IsDir(p.CurrentDirectory, remotePath);
				p.TransferInProgress = false;
				return res;
			}
			finally { p.TransferInProgress = false; }
		}
		
		public string GetCurrentDirectory()
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				string res = p.GetCurrentDirectory();
				p.TransferInProgress = false;
				return res;
			}
			finally { p.TransferInProgress = false; }
		}
		
		
		public void MakeDir(string directoryName)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.MakeDir(directoryName);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void RemoveDir(string directoryName)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.RemoveDir(directoryName);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void Chmod(string mode, string filename)
		{
			IProtocol p = GetConnection();
			p.TransferInProgress = true;
			try
			{
				p.Chmod(mode, filename);
			}
			finally { p.TransferInProgress = false; }
		}
		
		public void Abort()
		{
			GetTransferConnection().Abort();
		}
	}
}
