/// <summary>
 /// About window.
 /// </summary>
 /// <param name = "mainWindow">
 /// The parent window.
 /// </param>
 /// <param name="programInfo">
 /// This program's information (main project info).
 /// </param>
 /// <param name="assemblies">
 /// Assemblies to use.
 /// </param>
 public About(MainProgramElements mainWindow, FileVersionInfo programInfo, ProjectAssemblies assemblies)
 {
     this.VersionInfo = new VersionInfo(programInfo, assemblies, disclaimer);
     this.DataContext = this.VersionInfo;
     this.mainWindow  = mainWindow;
     InitializeComponent();
 }
        /// <summary>
        /// Url Manipulation window.
        /// </summary>
        /// <param name="add">
        /// This is the Url Adding window.
        /// </param>
        /// <param name="mainWindow">
        /// The parent window.
        /// </param>
        public UrlManipulation(bool add, MainProgramElements mainWindow)
        {
            this.DataContext = this.urlShapingVars;
            InitializeComponent();
            this.defaultHeight      = this.Height;
            this.defaultWidth       = this.Width;
            this.videoQueue         = mainWindow.Videos;
            this.MainWindow         = mainWindow;
            this.persistantUrlIndex = mainWindow.CurrentlySelectedQueueIndex;
            this.add   = add;
            this.Title = string.Format(CultureInfo.CurrentCulture, "{0} the Queue", add ? "Add Url(s) to" : "Modify Url(s) in");
            this.basicManipulateUrlButton.Content = add ? "Add Url to the Queue" : "Modify Current Url";

            if (!add && videoQueue.Any())
            {
                this.avTextBox.Text                    = string.Join(string.Empty, videoQueue);
                this.urlShapingVars.BasicText          = videoQueue[this.persistantUrlIndex].Location;
                this.urlShapingVars.AudioOnlyEnabled   = videoQueue[this.persistantUrlIndex].IsAudioFile;
                this.urlShapingVars.SelectedFormat     = this.formatComboBox.Items.IndexOf(videoQueue[this.persistantUrlIndex].Format);
                this.urlShapingVars.SelectedResolution = videoQueue[this.persistantUrlIndex].Quality;
            }
            else
            {
                this.avTextBox.Text = string.Format(CultureInfo.CurrentCulture, "{0} {1} Mp4 False (REMOVE!)", UrlShaping.ExampleText, UrlShaping.DefaultResolution);
            }
        }
        /// <summary>
        /// About window.
        /// </summary>
		/// <param name = "mainWindow">
		/// The parent window.
		/// </param>
        /// <param name="programInfo">
        /// This program's information (main project info).
        /// </param>
        /// <param name="assemblies">
        /// Assemblies to use.
        /// </param>
        public About (MainProgramElements mainWindow, FileVersionInfo programInfo, ProjectAssemblies assemblies)
        {
            this.VersionInfo = new VersionInfo (programInfo, assemblies, disclaimer);
            this.DataContext = this.VersionInfo;
            this.mainWindow = mainWindow;
            InitializeComponent();
        }
        /// <summary>
        /// Url Manipulation window.
        /// </summary>
        /// <param name="add">
        /// This is the Url Adding window.
        /// </param>
        /// <param name="mainWindow">
        /// The parent window.
        /// </param>
        public UrlManipulation (bool add, MainProgramElements mainWindow)
        {
            this.DataContext = this.urlShapingVars;
            InitializeComponent();
            this.defaultHeight = this.Height;
            this.defaultWidth = this.Width;
            this.videoQueue = mainWindow.Videos;
            this.MainWindow = mainWindow;
            this.persistantUrlIndex = mainWindow.CurrentlySelectedQueueIndex;
            this.add = add;
            this.Title = string.Format(CultureInfo.CurrentCulture, "{0} the Queue", add ? "Add Url(s) to" : "Modify Url(s) in");
            this.basicManipulateUrlButton.Content = add ? "Add Url to the Queue" : "Modify Current Url";

			if (!add && videoQueue.Any())
			{
				this.avTextBox.Text = string.Join(string.Empty, videoQueue);
				this.urlShapingVars.BasicText = videoQueue[this.persistantUrlIndex].Location;
				this.urlShapingVars.AudioOnlyEnabled = videoQueue[this.persistantUrlIndex].IsAudioFile;
				this.urlShapingVars.SelectedFormat = this.formatComboBox.Items.IndexOf(videoQueue[this.persistantUrlIndex].Format);
				this.urlShapingVars.SelectedResolution = videoQueue[this.persistantUrlIndex].Quality;
			}
			else this.avTextBox.Text = string.Format(CultureInfo.CurrentCulture, "{0} {1} Mp4 False (REMOVE!)", UrlShaping.ExampleText, UrlShaping.DefaultResolution);
        }
        public void DownloadHandler (MainProgramElements mainWindow)
        {
	    	#region Initialization
	    	mainWindow.WindowEnabled = false;
	    	mainWindow.CurrentDownloadOutputText = "Starting Downloading Process....";
	    	
        	int selectedIndex = mainWindow.CurrentlySelectedQueueIndex;
        	var urlList = mainWindow.Videos.ToList().AsReadOnly();
            urlList.WriteToFile(Storage.QueueFile, ".bak");
            List<Video> finishedVideos = new List<Video>();
            int[] retryCount = new int[urlList.Count + 1];
            const int maxRetrys = 4;
            
            Thread.Sleep(1000);
            #endregion  
            #region Handle Download Cycle
			int position = 0, urlListCount = urlList.Count;
            while (position < urlListCount)
			{
            	bool exceptionWasCaught = false;
            	bool ableToRetryOnFail = retryCount[position] < maxRetrys;
				var video = urlList[position];
				try
				{
					mainWindow.CurrentlySelectedQueueIndex = position;
					if (retryCount[position] <= 0)
					{
						mainWindow.CurrentDownloadOutputText = string.Format(CultureInfo.InstalledUICulture, "Beginning download from '{0}'", video.Location);
					}
					mainWindow.CurrentDownloadProgress = 0;
					
					//var result = await Task.Run(() => Download.SetupDownload(video, mainWindow));
					var result = this.SetupDownload(video, mainWindow);
					
					if (result is DownloadCanceledException)
					{
						mainWindow.CurrentDownloadOutputText = result.Message;
						if (App.IsDebugging) result.Message.Log("Youtube Download Helper");
						Thread.Sleep(!UserSettings.ContinueOnFail ? 1000 : 850);
					}
					else if (result != null) throw result;
					finishedVideos.Add(video);
				}
				catch (Exception ex)
				{
					exceptionWasCaught = true;
					var exceptionMessage = ex.Message;
					retryCount[position] += ex is NotSupportedException ? maxRetrys + 1 : 1;
					if (ableToRetryOnFail)
					{
						mainWindow.CurrentDownloadOutputText = string.Format(CultureInfo.InstalledUICulture, "URL {0}: {1}. Retrying.... ({2}/{3})", video.Position, exceptionMessage.Truncate(50), (retryCount[position]).ToString(CultureInfo.CurrentCulture), maxRetrys);
					}
					else if (!UserSettings.ContinueOnFail)
					{
						mainWindow.CurrentDownloadOutputText = exceptionMessage.Truncate(100);
						ex.Log(GenericCondition.None);
						break;
					}
					if(!UserSettings.ContinueOnFail)
					{
						Thread.Sleep(850);
					}
				}
				if (!exceptionWasCaught || (!ableToRetryOnFail && UserSettings.ContinueOnFail)) position++;
			}
            #endregion
            #region Final Steps
            bool noMajorErrors = retryCount.All(count => count <= maxRetrys);
            if (noMajorErrors || (!noMajorErrors && finishedVideos.Count() > 5))
			{
            	IEnumerable<Video> leftOverVideos = urlList.Where(url => finishedVideos.All(finishedUrl => !url.ToString().Equals(finishedUrl.ToString())));
            	leftOverVideos.Sort();
				mainWindow.Videos = new ObservableCollection<Video>(leftOverVideos);
			}
            
            if (noMajorErrors) mainWindow.CurrentDownloadOutputText = "Finished!";
			else if (string.IsNullOrWhiteSpace(mainWindow.CurrentDownloadOutputText))
			{
				mainWindow.CurrentDownloadOutputText = "An Error Has Occurred!";
			}
			
            if (mainWindow.Videos.Any())
            {
            	mainWindow.RefreshQueue(mainWindow.Videos, selectedIndex > mainWindow.Videos.All() ? 0 : selectedIndex);
            }
            mainWindow.Videos.WriteToFile(Storage.QueueFile);
            mainWindow.WindowEnabled = true;
            #endregion
        }
        private void Downloader (YouTubeVideo video, MainProgramElements mainWindow, bool isAudio)
        {
            string temporaryDownloadPath = Path.Combine(this.UserSettings.TemporarySaveLocation, video.FullName);
            string movingPath = Path.Combine(this.UserSettings.MainSaveLocation, video.FullName);
            if (this.UserSettings.ValidationLocations.All(path => !File.Exists(Path.Combine(path, video.FullName)) && !File.Exists(movingPath))
            {
        		if(isAudio)
        		{
			        var audioDownloader = new AudioDownloader (video, temporaryDownloadPath);;
			        audioDownloader.AudioExtractionProgressChanged += (sender, args) => mainWindow.CurrentDownloadProgress = (int)(85 + args.ProgressPercentage * 0.15);
			        audioDownloader.Execute();
        		}
        		else
        		{
        			var videoDownloader = new VideoDownloader (video, temporaryDownloadPath);
        			videoDownloader.DownloadProgressChanged += ((sender, args) => mainWindow.CurrentDownloadProgress = (int)args.ProgressPercentage);
	                videoDownloader.Execute();
        		}
        		if (!temporaryDownloadPath.Equals(movingPath, StringComparison.OrdinalIgnoreCase)) File.Move(temporaryDownloadPath, movingPath);
            }
            else
            {
            	throw new DownloadCanceledException(string.Format(CultureInfo.CurrentCulture, "The download of #{0} '{1}({2})' has been canceled because it already existed.", videoToUse.Position, RemoveIllegalPathCharacters(video.Title).Truncate(10), videoToUse.Location.Truncate(100)));
            }
        }
//        private void DownloadThread ()
//        {
//        	
//        }
        
        private Exception SetupDownload (Video video, MainProgramElements mainWindow)
        {
        	try
        	{
        		IEnumerable<YouTubeVideo> videos = new List<YouTubeVideo>();
        		using(var service = Client.For(YouTube.Default))
        		{
        			videos = service.GetAllVideos(video.Location);
        		}
        		
	        	YouTubeVideo currentVideo = default(YouTubeVideo);
	        	
	        	if(video.IsAudioFile || video.VideoFormat != VideoFormat.Mp4 || (!video.IsAudioFile && video.VideoFormat == VideoFormat.Mp4 && video.Quality == 360))
	        	{
	        		currentVideo = videos.FirstOrDefault(info => !video.IsAudioFile ? (info.Format == video.VideoFormat && info.Resolution == video.Quality) : (video.AudioFormat == AudioFormat.Mp3 && info.AudioFormat == video.AudioFormat && info.AudioBitrate == video.Quality));
	        	}
	        		
	        	if (currentVideo != default(YouTubeVideo))
	            {
	        		mainWindow.CurrentDownloadOutputText = string.Format(CultureInfo.InstalledUICulture, "Downloading (#{0}/{1}) '{2}' at {3}{4}", video.Position, mainWindow.Videos.Count(), currentVideo.FullName.Truncate(56), !video.IsAudioFile ? currentVideo.Resolution : currentVideo.AudioBitrate, !video.IsAudioFile ? "p resolution" : " bitrate");
					
	        		this.Downloader(currentVideo, mainWindow, video.IsAudioFile);
	        		
	        		return null;
	            } 
	            else
	            {      	
	                var formatsEstablished = new List<VideoFormat> ();
	                var qualitiesEstablished = new List<int> ();
	                using (StreamWriter outfile = new StreamWriter ("Acceptable Options.txt"))
	                {
	                    outfile.Write(string.Format(CultureInfo.CurrentCulture, "This file will show you all formats available for the current URL, as well as the resolutions that are acceptable for that URL.\n\n{0}:\n\nVideo Formats:\n", video.Location));
	                    for (var position = videos.Where(info => info.Format != VideoFormat.Unknown && formatsEstablished.All(format => info.Format != format)).Select(info => info.VideoType).GetEnumerator(); position.MoveNext();)
	                    {
	                        VideoFormat format = position.Current;
	                        formatsEstablished.Add(format);
	                        switch (format)
	                        {
	                            case VideoFormat.Mp4:
	                                outfile.Write(string.Format(CultureInfo.CurrentCulture, "Format: {0} | Resolution: {1}p\n", format, "360"));
	                                break;
	                            default:
	                                var validVideos = videoInfos.Where(videoInfo => (videoInfo.Resolution >= UrlShaping.MinimumQuality[typeof(VideoType)] && videoInfo.Resolution <= UrlShaping.MaximumQuality) && videoInfo.VideoType.Equals(format) && qualitiesEstablished.All(quality => videoInfo.Resolution != quality)).Select(videoInfo => videoInfo.Resolution);
									foreach (int currentResolution in validVideos)
									{
										outfile.Write(string.Format(CultureInfo.CurrentCulture, "Format: {0} | Resolution: {1}p\n", format, currentResolution));
									}
	                                break;
	                        }
	                        qualitiesEstablished.Clear();
	                    }
	                    
	                    outfile.Write("\nAudio Formats:\n");
	                    var audioQualitiesEstablished = new List<AudioType> ();
	                    
	                    for (var position = videoInfos.Where(info => info.AudioType != AudioType.Unknown && audioQualitiesEstablished.All(quality => info.AudioType != quality)).Select(info => info.AudioType).GetEnumerator(); position.MoveNext();)
	                    {
	                        AudioType format = position.Current;
	                        audioQualitiesEstablished.Add(format);
							var validAudioTracks = videoInfos.Where(videoInfo => (videoInfo.AudioBitrate >= UrlShaping.MinimumQuality[typeof(AudioType)] && videoInfo.AudioBitrate <= UrlShaping.MaximumQuality) && videoInfo.AudioType.Equals(format)).Select(videoInfo => videoInfo.AudioBitrate);
							foreach (int currentQuality in validAudioTracks)
							{
								outfile.Write(string.Format(CultureInfo.CurrentCulture, "Format: {0} | Bitrate: {1}\n", format, currentQuality));
							}
	                        qualitiesEstablished.Clear();
	                    }
	                }
	                throw new NotSupportedException ("An acceptable options file has been exported to the program's root folder. Check there for more information.");
	            }
        	}
        	catch (Exception ex) { return ex; }
        }
        private void Downloader (IEnumerable<VideoInfo> videoInfos, MainProgramElements mainWindow, Video videoToUse)
        {
        	bool audioTrack = videoToUse.IsAudioFile;
            VideoInfo video = videoInfos.First(info => !audioTrack ? (info.VideoType == videoToUse.VideoFormat && info.Resolution == videoToUse.Quality) : (info.AudioType == videoToUse.AudioFormat && info.AudioBitrate == videoToUse.Quality));
            
            if (video.RequiresDecryption) DownloadUrlResolver.DecryptDownloadUrl(video);
            
            string videoName = string.Format(CultureInfo.InvariantCulture, "{0}{1}", RemoveIllegalPathCharacters(video.Title), !audioTrack ? video.VideoExtension : video.AudioExtension);
            string temporaryDownloadPath = Path.Combine(this.UserSettings.TemporarySaveLocation, videoName);
            string movingPath = Path.Combine(this.UserSettings.MainSaveLocation, videoName);
            if (this.UserSettings.ValidationLocations.All(path => !File.Exists(Path.Combine(path, videoName))) && !File.Exists(movingPath))
            {
        		if(audioTrack)
        		{
			        var audioDownloader = new AudioDownloader (video, temporaryDownloadPath);;
			        audioDownloader.AudioExtractionProgressChanged += (sender, args) => mainWindow.CurrentDownloadProgress = (int)(85 + args.ProgressPercentage * 0.15);
			        audioDownloader.Execute();
        		}
        		else
        		{
        			var videoDownloader = new VideoDownloader (video, temporaryDownloadPath);
        			videoDownloader.DownloadProgressChanged += ((sender, args) => mainWindow.CurrentDownloadProgress = (int)args.ProgressPercentage);
	                videoDownloader.Execute();
        		}
        		if (!temporaryDownloadPath.Equals(movingPath, StringComparison.OrdinalIgnoreCase)) File.Move(temporaryDownloadPath, movingPath);
            }
            else
            {
            	throw new DownloadCanceledException(string.Format(CultureInfo.CurrentCulture, "The download of #{0} '{1}({2})' has been canceled because it already existed.", videoToUse.Position, RemoveIllegalPathCharacters(video.Title).Truncate(10), videoToUse.Location.Truncate(100)));
            }
        }
 /// <summary>
 /// Options window.
 /// </summary>
 /// <param name="mainWindow">
 /// The parent window.
 /// </param>
 public Options(MainProgramElements mainWindow)
 {
     this.DataContext = this.savedSettings;
     InitializeComponent();
     this.MainWindow = mainWindow;
 }
 /// <summary>
 /// Options window.
 /// </summary>
 /// <param name="mainWindow">
 /// The parent window.
 /// </param>
 public Options (MainProgramElements mainWindow)
 {
     this.DataContext = this.savedSettings;
     InitializeComponent();
     this.MainWindow = mainWindow;
 }