using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using AAXClean;
using AAXClean.Codecs;
using DataLayer;
using Dinah.Core.ErrorHandling;
using Dinah.Core.Net.Http;
using FileManager;
using LibationFileManager;

namespace FileLiberator
{
	public class ConvertToMp3 : AudioDecodable
	{
		public override string Name => "Convert to Mp3";
		private Mp4File m4bBook;

		private long fileSize;
		private static string Mp3FileName(string m4bPath) => Path.ChangeExtension(m4bPath ?? "", ".mp3");

		public override Task CancelAsync() => m4bBook?.CancelAsync() ?? Task.CompletedTask;

		public static bool ValidateMp3(LibraryBook libraryBook)
		{
			var paths = AudibleFileStorage.Audio.GetPaths(libraryBook.Book.AudibleProductId);
			return paths.Any(path => path?.ToString()?.ToLower()?.EndsWith(".m4b") == true && !File.Exists(Mp3FileName(path)));
		}

		public override bool Validate(LibraryBook libraryBook) => ValidateMp3(libraryBook);

		public override async Task<StatusHandler> ProcessAsync(LibraryBook libraryBook)
		{
			OnBegin(libraryBook);

			try
			{
				var m4bPaths = AudibleFileStorage.Audio.GetPaths(libraryBook.Book.AudibleProductId);

				foreach (var m4bPath in m4bPaths)
				{
					var proposedMp3Path = Mp3FileName(m4bPath);
					if (File.Exists(proposedMp3Path) || !File.Exists(m4bPath)) continue;

					m4bBook = await Task.Run(() => new Mp4File(m4bPath, FileAccess.Read));
					m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;

					fileSize = m4bBook.InputStream.Length;

					OnTitleDiscovered(m4bBook.AppleTags.Title);
					OnAuthorsDiscovered(m4bBook.AppleTags.FirstAuthor);
					OnNarratorsDiscovered(m4bBook.AppleTags.Narrator);
					OnCoverImageDiscovered(m4bBook.AppleTags.Cover);

					var config = Configuration.Instance;
					var lameConfig = GetLameOptions(config);

					//Finishing configuring lame encoder.
					AaxDecrypter.MpegUtil.ConfigureLameOptions(
						m4bBook,
						lameConfig,
						config.LameDownsampleMono,
						config.LameMatchSourceBR);

					using var mp3File = File.OpenWrite(Path.GetTempFileName());
					var result = await m4bBook.ConvertToMp3Async(mp3File, lameConfig);
					m4bBook.InputStream.Close();
					mp3File.Close();

					if (result == ConversionResult.Failed)
					{
						FileUtility.SaferDelete(mp3File.Name);
						return new StatusHandler { "Conversion failed" };
					}
					else if (result == ConversionResult.Cancelled)
					{
						FileUtility.SaferDelete(mp3File.Name);
						return new StatusHandler { "Cancelled" };
					}

					var realMp3Path = FileUtility.SaferMoveToValidPath(mp3File.Name, proposedMp3Path, Configuration.Instance.ReplacementCharacters);
					OnFileCreated(libraryBook, realMp3Path);
				}
				return new StatusHandler();
			}
			finally
			{
				OnCompleted(libraryBook);
			}
		}

		private void M4bBook_ConversionProgressUpdate(object sender, ConversionProgressEventArgs e)
		{
			var duration = m4bBook.Duration;
			var remainingSecsToProcess = (duration - e.ProcessPosition).TotalSeconds;
			var estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed;

			if (double.IsNormal(estTimeRemaining))
				OnStreamingTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining));

			double progressPercent = 100 * e.ProcessPosition.TotalSeconds / duration.TotalSeconds;

			OnStreamingProgressChanged(
				new DownloadProgress
				{
					ProgressPercentage = progressPercent,
					BytesReceived = (long)(fileSize * progressPercent),
					TotalBytesToReceive = fileSize
				});
		}
	}
}
