using System;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;
using Tamir.SharpSsh;
using System.Text;

namespace prog_tesztelo
{
	/// <summary>
	/// Delegátus az OverwriteForm-mal való kapcsolattartáshoz
	/// </summary>
	/// <param name="value">a felülírás folyamatát meghatározó érték</param>
	public delegate void OverwriteDelegate(int value);

    public partial class Copier : BackgroundWorker
    {
		/// <summary>
		/// A BackgroundWorker objektum, amely segítségével a háttérben végezzük a másolást,
		/// így a fő form futása nem akad meg a folyamat végrehajtása során
		/// </summary>
		private static BackgroundWorker bw;

		/// <summary>
		/// A másolandó fájlok listája és a rájuk vonatkozó információk
		/// </summary>
		private static CopierSettings copierSettings;

		/// <summary>
		/// Az Scp kapcsolatért felelős objektum, amely biztosítja a fájlátvitelt a szerver és a számítógép között
		/// </summary>
		/// <remarks>
		/// Az Scp osztály három eseménykezelőt biztosít:
		///		scp_OnTransferStart
		///		scp_OnTransferProgress
		///		scp_OnTransferEnd
		/// </remarks>
		private static Scp scp;

		/// <summary>
		/// Az SshExec objektum, amely segítségével egy-egy parancsot tudunk futtatni a szerveren
		/// </summary>
		/// <remarks>
		/// Az SshExec az SshShell-től eltérően egy parancs futtatására ad lehetőséget, amit lefuttat,
		/// és a parancs kimenetét adja vissza sztringként
		/// </remarks>
		private static SshExec sshexec;

		/// <summary>
		/// A felülírást vezérlő form
		/// </summary>
		private static OverwriteForm overwriteform;

		/// <summary>
		/// A felülírás típusát meghatározó adattag, amely a további teendőket határozza meg.
		/// </summary>
		/// <remarks>
		/// Különböző értékek esetén a teendők
		///	   -1:	Alapértelmezett érték, felkínáljuk a felhasználónak a felülírási lehetőségeket
		///		0:	Megszakítjuk a másolás folyamatát
		///		1:	Felülírjuk az aktuális fájlt/könyvtárat
		///		2:	Minden fájlt/könyvtárat felülírunk
		///		3:	Kihagyjuk az aktuális fájlt/könyvtárat
		///		4:	Minden fájlt/könyvtárat kihagyunk
		/// </remarks>
		private static int overwriteValue;

		/// <summary>
		/// Az alapértelmezett konstruktor
		/// </summary>
        public Copier()
        {
            InitializeComponent();
			overwriteValue = -1;
        }

		/// <summary>
		/// A konstruktorban a paraméterként kapott konténerhez hozzáadjuk az osztály aktuális példányát
		/// </summary>
		/// <remarks>
		/// Ezt a Copier objektumot a CopyForm használja fel a háttérszálon történő másoláshoz,
		/// hogy a folyamat ideje alatt a fő szál ne blokkolódjon, és a felhasználó kapjon visszajelzést
		/// a folyamat aktuális állapotáról
		/// </remarks>
		/// <param name="container"></param>
		public Copier(IContainer container)
		{
			container.Add(this);
			InitializeComponent();
			overwriteValue = -1;
		}
		
		/// <summary>
		/// A BackgroundWorder háttérszálon futó eseménykezelője, amely elvégzi a másolást
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void Copier_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                bw = sender as BackgroundWorker;
                copierSettings = e.Argument as CopierSettings;

				// példányosítunk egy új Scp objektumot a felhasználói adatokkal, és feliratkozunk
				// az Scp eseménykezelőire
				scp = new Scp(copierSettings.Host, copierSettings.User, copierSettings.Pass);
				scp.OnTransferStart += new FileTransferEvent(scp_OnTransferStart);
				scp.OnTransferProgress += new FileTransferEvent(scp_OnTransferProgress);
				scp.OnTransferEnd += new FileTransferEvent(scp_OnTransferEnd);
				scp.Connect();
				
				// az SshExec-et használjuk a szerveren történő fájl-ellenőrzésre a másolás előtt
				sshexec = new SshExec(copierSettings.Host, copierSettings.User, copierSettings.Pass);
				sshexec.Connect();

				double iss = 0;
				double d = 100 / copierSettings.Selectedfile.Count;

				bool isDir;
				bool toSep = (copierSettings.To[copierSettings.To.Length - 1] == Path.DirectorySeparatorChar);
				bool fromSep = (copierSettings.From[copierSettings.From.Length - 1] == Path.DirectorySeparatorChar);

                foreach (ListViewItem i in copierSettings.Selectedfile)
                {
					try
					{
						iss += d;
						isDir = (i.SubItems[1].Text == "<dir>");

						if (!bw.CancellationPending && i != null && i.SubItems[0].Text != "..")
						{
							// a szerverről másolunk
							if (!copierSettings.IsLocal)
							{
								// ellenőrizzük, hogy létezik-e az állomány
								bool exists = ( isDir ? Directory.Exists( Path.Combine(copierSettings.To, i.SubItems[0].Text) ) : File.Exists( Path.Combine(copierSettings.To, i.SubItems[0].Text) ) );
								
								// felülírás létező állomány esetén
								if (exists)
								{
									if (overwriteValue != 2 && overwriteValue != 4)
									{
										overwriteform = new OverwriteForm(copierSettings.To + (toSep ? "" : Path.DirectorySeparatorChar.ToString()) + i.SubItems[0].Text, copierSettings.From + "/" + i.SubItems[0].Text) { OverwriteDelegate = new OverwriteDelegate(this.overwriteCallbackFunction) };
										overwriteform.ShowDialog();
									}
									if (overwriteValue == 3) { overwriteValue = -1; continue; }
									if (overwriteValue == 4) { continue; }
									if (overwriteValue == 0) { overwriteValue = -1; break; }
									if (overwriteValue == 1)
									{ // alapértelmezetten felülírja a könyvtár tartalmát
										overwriteValue = -1;
									}
								}
								bw.ReportProgress((int)iss, copierSettings.From + "/" + i.SubItems[0].Text + @"--\\\--" + copierSettings.To + (toSep ? "" : Path.DirectorySeparatorChar.ToString()) + i.SubItems[0].Text);
								scp.From(copierSettings.From + "/" + i.SubItems[0].Text, copierSettings.To + (toSep ? "" : Path.DirectorySeparatorChar.ToString()), true);
							}
							// a szerverre másolunk
							else
							{
								// ellenőrizzük, hogy létezik-e az állomány
								if ((sshexec.RunCommand("test -e " + copierSettings.To + "/" + i.SubItems[0].Text + " && echo true")).Contains("true"))
								{
									// felülírás létező állományok esetén
									if (overwriteValue != 2 && overwriteValue != 4)
									{
										overwriteform = new OverwriteForm(copierSettings.To + "/" + i.SubItems[0].Text, copierSettings.From + (fromSep ? "" : Path.DirectorySeparatorChar.ToString()) + i.SubItems[0].Text) { OverwriteDelegate = new OverwriteDelegate(this.overwriteCallbackFunction) };
										overwriteform.ShowDialog();
									}
									if (overwriteValue == 3) { overwriteValue = -1; continue; }
									if (overwriteValue == 4) { continue; }
									if (overwriteValue == 0) { overwriteValue = -1; break; }
									if (overwriteValue == 1 || overwriteValue == 2)
									{ // alapértelmezetten felülírja a könyvtár tartalmát
										if (overwriteValue == 1) 
										{
											overwriteValue = -1; 
										}
										sshexec.RunCommand("rm " + (isDir ? "-r" : "") + copierSettings.To + "/" + i.SubItems[0].Text);
									}
								}
								bw.ReportProgress((int)iss, copierSettings.From + (fromSep ? "" : Path.DirectorySeparatorChar.ToString()) + i.SubItems[0].Text + @"--\\\--" + copierSettings.To + "/" + i.SubItems[0].Text);
								scp.To(copierSettings.From + (fromSep ? string.Empty : Path.DirectorySeparatorChar.ToString()) + i.SubItems[0].Text, copierSettings.To + "/" + i.SubItems[0].Text, true);
							}
						}
						else
						{
							if (scp.Connected) scp.Close();
							break;
						}
					}
					catch
					{
						continue;
					}
                }

				if (scp.Connected) scp.Close();
				if (sshexec.Connected) sshexec.Close();
            }
            catch (Exception exc)
            {
                MessageBox.Show("Hiba a másolás során.\r\n\r\n" + exc.Message);
            }
        }

		/// <summary>
		/// A callback függvény, ami az OverwriteForm-ból érkező értéket állítja be az overwriteValue értékének,
		/// és annak megfelelően folytatja a másolásd
		/// </summary>
		/// <param name="value"></param>
		private void overwriteCallbackFunction(int value)
		{
			overwriteValue = value;
		}

		/// <summary>
		/// A fájlátvitel kezdetét vezérlő eseménykezelő - nullázza a ProgressBar értékét
		/// </summary>
		/// <param name="src">a forrás elérési útvonala</param>
		/// <param name="dst">a cél elérési útvonala</param>
		/// <param name="transferredBytes">az átvitt bájtok száma</param>
		/// <param name="totalBytes">az összes bájt</param>
		/// <param name="message">az üzenet</param>
		void scp_OnTransferStart(string src, string dst, int transferredBytes, int totalBytes, string message)
		{
			bw.ReportProgress(0);
			if (bw.CancellationPending)
			{
				scp.Cancel();
				if (sshexec.Connected) sshexec.Close();
			}
		}

		/// <summary>
		/// A fájlátvitel folyamatát vezérlő eseménykezelő - közli a folyamat állapotát a ProgressBar-on
		/// </summary>
		/// <param name="src">a forrás elérési útvonala</param>
		/// <param name="dst">a cél elérési útvonala</param>
		/// <param name="transferredBytes">az átvitt bájtok száma</param>
		/// <param name="totalBytes">az összes bájt</param>
		/// <param name="message">az üzenet</param>
		void scp_OnTransferProgress(string src, string dst, int transferredBytes, int totalBytes, string message)
		{
			bw.ReportProgress((totalBytes == 0) ? 0 : (transferredBytes * 100 / totalBytes));
			if (bw.CancellationPending)
			{
				scp.Cancel();
				if (sshexec.Connected) sshexec.Close();
			}
		}
		
		/// <summary>
		/// A fájlátvitel végét vezérlő eseménykezelő - visszajelzi a folyamat sikerességét a ProgressBar-on
		/// </summary>
		/// <param name="src">a forrás elérési útvonala</param>
		/// <param name="dst">a cél elérési útvonala</param>
		/// <param name="transferredBytes">az átvitt bájtok száma</param>
		/// <param name="totalBytes">az összes bájt</param>
		/// <param name="message">az üzenet</param>
		void scp_OnTransferEnd(string src, string dst, int transferredBytes, int totalBytes, string message)
		{
			bw.ReportProgress( (totalBytes == 0) ? 0 : (transferredBytes * 100 / totalBytes));
			if (bw.CancellationPending)
			{
				scp.Cancel();
				if (sshexec.Connected) sshexec.Close();
			}
		}
    }
}