/*
 * Erstellt mit SharpDevelop.
 * Benutzer: Hauptnutzer
 * Datum: 22.12.2012
 * Zeit: 14:10
 * 
 * Sie können diese Vorlage unter Extras > Optionen > Codeerstellung > Standardheader ändern.
 */
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;

namespace OpusDrop
{
	/// <summary>
	/// Description of MainForm.
	/// </summary>
	public partial class MainForm : Form
	{
		
		bool _GUILockedUp;
		bool _KillEncodingProcess;
		bool _ProcessWasSuccKilled;
		List<EncodingGroup> _EncodingList;
		
		string _WorkingOnThatFile;
		//string _ReportProgress;
		string _DebugMessage;
		delegate void UpdateGUI();
		
		public MainForm()
		{
			//
			// The InitializeComponent() call is required for Windows Forms designer support.
			//
			InitializeComponent();
			InitalisiereBackgroundWorker();
			
			// Eventhandler für Background-Worker einrichten
			theBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(this.UpdateStatus);
			theBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.UpdateStatusAtTheEnd);
			
			
			_GUILockedUp = false;
			_KillEncodingProcess = false;
			_ProcessWasSuccKilled = false;
			_EncodingList = new List<EncodingGroup>();
			
			// Textboxen checken
			object sender = new object();
			EventArgs e = new EventArgs();
			textBox_NumberOfThreads.Text = System.Environment.ProcessorCount.ToString();
			TextBox_NumberOfThreadsTextChanged(sender, e);
		}
		

		
		
		EncodingParamterCollection ReadParametersFromGUI()
		{
			if (_GUILockedUp)
				return null;
			
			
			EncodingParamterCollection myCol = new EncodingParamterCollection();
			
			// Jetzt kommen die einzelnen Parameter
			myCol.Bitrate = Convert.ToInt32(textBox_Bitrate.Text);
			
			myCol.DownMixToMono = checkBox_DownMixToMono.Checked;
			
			myCol.FramesizeInMiliseconds = Convert.ToInt32(textBox_Framesize.Text);
			
			if (radioButton_Bitratehandling_CBR.Checked)
				myCol.UseCBREncoding = true;
			if (radioButton_Bitratehandling_CVBR.Checked)
				myCol.UseCVBREncoding = true;
			if (radioButton_Bitratehandling_VBR.Checked)
				myCol.UseVBREncoding = true;
			
//			if (radioButton_Optimization_Default.Checked)
//				myCol.Optimization_Default = true;
//			if (radioButton_Optimization_Music.Checked)
//				myCol.Optimization_Music = true;
//			if (radioButton_Optimization_Speech.Checked)
//				myCol.Optimization_Speech = true;
			
			return myCol;
			
			
		}
		
		void myDragEnter(object sender, DragEventArgs e)
		{
			
			// Abbrechen, wenn GUI verriegelt ist
			if (_GUILockedUp)
			{
				textBox_FilenameOfActiveFile.Text = "Can't encode with nonsense parameters";
				return;
			}
			
			
			if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy;
			
		}

		void MainFormDragLeave(object sender, EventArgs e)
		{
			
		}
		
		void DragAndDrop(object sender, DragEventArgs e)
		{
			

			// Wenn Dateien in die Grafik gezogen wurden
			// Quelle: stackoverflow - C# drag and drop files to form
			// von: Mahmoud Gamal (Frage) & Cody Gray (Antwort)
			if (!e.Data.GetDataPresent(DataFormats.FileDrop))
				return;
			
			
			// Files in Dateiliste schreiben
			
			string[] filesTemporary = (string[])e.Data.GetData(DataFormats.FileDrop);
			bool[] filesDirectoryMask = new bool[filesTemporary.Length];
			int NumberOfFoundDirectories = 0;
			List<string> myDirectoryIncludedFilesList = new List<string>();
			
			// Prüfen ob sich ein Ordner darin befand
			for (int i = 0; i < filesTemporary.Length; i++)
			{
				// Wenn die Datei ein Verzeichnis ist
				if (System.IO.Directory.Exists(filesTemporary[i]) )
				{
					// Vermerken, dass es sich um ein Verzeichnis handelt
					NumberOfFoundDirectories++;
					filesDirectoryMask[i] = true;
					
					// Eine Prozedur aufrufen, die alle Dateien der Liste in eine neue Liste schreibt
					// Die Prozedur kümmert sich um der Rest
					ScanDirForAllFiles(ref myDirectoryIncludedFilesList, filesTemporary[i]);
					
				}
				else
					myDirectoryIncludedFilesList.Add(filesTemporary[i]);
				
			}
			
			
			// Alle Dateien, die nicht auf *.wav enden, aus der Liste entfernen
			
			for (int i = 0; i < myDirectoryIncludedFilesList.Count; i++)
			{
				string Ext = Path.GetExtension(myDirectoryIncludedFilesList[i]);
				if ( ! ( (  String.Equals(".wav" , Ext.ToLower()) )  || ( String.Equals(".opus" , Ext.ToLower()) )   ||  ( String.Equals(".mp3" , Ext.ToLower()) )  ) )
				{
					myDirectoryIncludedFilesList.RemoveAt(i);
					i--;
				}
			}
			
			if (myDirectoryIncludedFilesList.Count == 0)
				return;
			
			// Die Dateiliste wieder in ein Stringformat überführen
			string[] files = new string[myDirectoryIncludedFilesList.Count];
			for (int i = 0; i < myDirectoryIncludedFilesList.Count; i++)
				files[i] = myDirectoryIncludedFilesList[i];
			
			
			// einzeln als Elemente in die Arbeitsliste für opusenc eintragen
			foreach (string InputFilepath in files)
			{
				
				
				// Neue Gruppe schaffen
				EncodingGroup newEncGroup = new EncodingGroup();
				// Der Gruppe die Paramter aus der GUI einschreiben
				newEncGroup.UsedEncodingParameters = ReadParametersFromGUI();
				
				// Den Dateinamen einschreiben
				newEncGroup.Filename = InputFilepath;
				
				// In die Arbeitsliste eintragen
				_EncodingList.Add(newEncGroup);
				

			}
			
			
			// Die GUI aktualisieren, dass die neuen Dateien auch in der Arbeitsliste auftauchen
			UpdateJobList();

			// Den Backgroundworker starten
			// Prüfen ob der Backgroundworker noch beschäftigt ist
			if (theBackgroundWorker.IsBusy)
			{
				// Falls es noch an ist,
				// 100 ms warten (es könnte sich ja gerade im Abbrechen befinden
				// und es dann nochmal versuchen
				if (!theBackgroundWorker.IsBusy)
					theBackgroundWorker.RunWorkerAsync();
				else // ist es immer noch beschäftigt, hat es wohl aus mitbekommen,
					// dass in der Liste inzwischen mehr Arbeit vorhanden ist
					return;
			}
			// Wenn er nichts zu tun hat, starten
			else
				// Backgroundworker anwerfen
				theBackgroundWorker.RunWorkerAsync();
			
			
			
			
			
		}
		
		List<string> ScanDirForAllFiles(ref List<string> myFileList, string DirectoryPath)
		{
			// Alle Verzeichnisse in die Liste schreiben
			string[] DateiInhalt = Directory.GetFiles(DirectoryPath);
			foreach(string myFile in DateiInhalt)
				myFileList.Add(myFile);
			
			// Alles Ordner raussuchen, damit die Funktion rekursiv aufrufen
			string[] OrdnerInhalt = Directory.GetDirectories(DirectoryPath);
			foreach(string myDir in OrdnerInhalt)
				ScanDirForAllFiles(ref myFileList, myDir);
			
			return myFileList;
		}
		
		void UpdateStatus(object sender, ProgressChangedEventArgs e)
		{
			//textBox_OutputOfCommandLine.Text = myStr;
			textBox_FilenameOfActiveFile.Text = "encoding: " + Path.GetFileName(_WorkingOnThatFile);
			
			if (! String.Equals("", (string) e.UserState) )
				textBox_Debugger.Text = (string) e.UserState;
			
			UpdateJobList();
			
		}

		
		void UpdateStatusAtTheEnd(object sender, RunWorkerCompletedEventArgs e)
		{
			UpdateJobList();
			
			// Ausgeben, dass Encodierung fertig ist
			textBox_FilenameOfActiveFile.Text = "Encoding finished";
		}

		void UpdateStatusAfterAnErrorOccurred(object sender, RunWorkerCompletedEventArgs e)
		{
			UpdateJobList();
			
			// Ausgeben, dass Encodierung fertig ist
			textBox_FilenameOfActiveFile.Text = "there seem to happened some kind of problem...";
		}
		
		
		int[] CountJoblist()
		{
			// Variable schaffen
			int[] JobList = new int[3];
			JobList[0] = 0; // Fehler bei der Encodierung
			JobList[1] = 0; // Erfolgreich encodiert
			JobList[2] = 0; // Noch in Warteposition
			
			int Number = _EncodingList.Count;
			if (Number == 0)
			{

				return JobList;
			}
			else
			{
				for (int i = 0; i < Number; i++)
				{
					if (_EncodingList[i].EncodedSuccessfully)
					{
						JobList[1]++;
					}
					else
					{
						if (_EncodingList[i].ErrorOnEncoding)
							JobList[0]++;
						else
							JobList[2]++;
					}
				}
				return JobList;
			}
		}
		
		string GetListOfEncodingErrorFilenames()
		{
			string ErrorList = "Error on encoding this files:\r\n";
			int Number = _EncodingList.Count;
			if (Number == 0)
			{

				return ErrorList;
			}
			else
			{
				for (int i = 0; i < Number; i++)
				{
					if (_EncodingList[i].ErrorOnEncoding)
						ErrorList = ErrorList + (_EncodingList[i].Filename) + " - " + _EncodingList[i].ErrorMsg + "\r\n";
				}
				return ErrorList;
			}
		}
		
		
		

		
		void MainFormFormClosing(object sender, FormClosingEventArgs e)
		{
			
			// Wenn der Backgroundworker nicht arbeitet - alles in bester Ordnung
			if (!theBackgroundWorker.IsBusy)
				return;
			
			// Wenn er noch arbeitet, sicherheitsabfrage
			DialogResult result = MessageBox.Show("Konvertierung läuft noch - sind sie sicher, dass sie abbrechen wollen?\nConversion is still running - you really want to close the application?", this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
			if (result == DialogResult.No)
				e.Cancel = true;
			else
			{
				
				
				_KillEncodingProcess = true;
				
				
				while (!_ProcessWasSuccKilled)
					System.Threading.Thread.Sleep(50);
				
				return;
			}
			
			
			
			
		}
		
		
		

	}
	
	// Partielle Klasse für den Background-Worker
	public partial class MainForm : Form
	{
		public void InitalisiereBackgroundWorker()
		{
			// Background-Handler von Hans Joachim Kiefer @ NetBlog
			
			// Neues Event für Arbeitsbeginn einlisten
			theBackgroundWorker.DoWork += new DoWorkEventHandler(theBackgroundWorker_DoWork);
			
			// FortschrittsEvent einlisten
			theBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(theBackgroundWorker_ProgressChanged);
			
			
			
			// Neues Event für Ende der Rechnung
			theBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(theBackgroundWorker_Complete);
			
			// Setzten von Eigenschaften
			theBackgroundWorker.WorkerReportsProgress = true;
			theBackgroundWorker.WorkerSupportsCancellation = true;
		}
		
		void theBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
		{
			
			BackgroundWorker worker = sender as BackgroundWorker;
			
			if ( !StarteEncoding(worker, e))
			{
				// Wenn es Probleme gegeben hat, dann abbrechen
				
			}
			else
			{
				// Arbeit beenden
				RunWorkerCompletedEventArgs WorkerComplete = new RunWorkerCompletedEventArgs(sender, null, false);
				theBackgroundWorker_Complete(sender, WorkerComplete);
			}
		}
		
		private bool StarteEncoding(System.ComponentModel.BackgroundWorker worker, System.ComponentModel.DoWorkEventArgs e)
		{
			// Das ist jetzt bereits innerhalb des Backgroundworkers
			
			// Führe das aus, solang bis wirklich alle Elemente in der Liste abgegrast sind
			int NumberOfItemsInList = 0;
			while (NumberOfItemsInList < _EncodingList.Count)
			{
				
				// Die aktuelle Nummer der Aufträge in der Liste holen
				NumberOfItemsInList = _EncodingList.Count;
				
				// Alle Aufträge abarbeiten - Durchzählen erfolgt durch Nummern -
				// wichtig, sonst kann man die Liste nicht während der Laufzeit
				// erweitern
				
				// Alle Einträge zählen, die schon bearbeitet wurden
				int Startposition = 0;
				for (int i = 0; i < NumberOfItemsInList; i++)
				{
					if ( !(_EncodingList[i].EncodedSuccessfully || _EncodingList[i].ErrorOnEncoding))
					{
						Startposition = i;
						break;
					}
				}
				ParallelOptions myOpt = new ParallelOptions();
				myOpt.MaxDegreeOfParallelism = Convert.ToInt32(textBox_NumberOfThreads.Text);
				
				
//				try {
				Parallel.For(Startposition,NumberOfItemsInList, myOpt, i =>
				             //for (int i = Startposition; i < NumberOfItemsInList; i++)
				             {
				             	if (!_KillEncodingProcess)
				             	{
				             		if (!EncodeFileViaCommandLineEncoder(_EncodingList[i]))
				             		{ // Wird im Falle eines Fehlers ausgesführf
				             			if (string.Equals(_EncodingList[i].ErrorMsg , ""))
				             				_EncodingList[i].ErrorMsg = "Allgemeiner Fehler bei Konvertierung";
				             			
				             			ProgressChangedEventArgs myEv = new ProgressChangedEventArgs(i / NumberOfItemsInList, (object) _EncodingList[i]);
				             		}
				             		else
				             		{
				             			string myStr = " ... ";
				             			ProgressChangedEventArgs myEv  = new ProgressChangedEventArgs(i / NumberOfItemsInList, (object) myStr);
				             		}
				             	}
				             }
				            );
//				}
//				catch (AggregateException ex)
//				{
//					string InnerErrorMessage = "";
//					foreach (Exception inner in ex.InnerExceptions)
//					{
//						InnerErrorMessage = InnerErrorMessage + (inner.Message) + "\r\n";
//					}
//					_DebugMessage = InnerErrorMessage;
//				}
			}
			
			return true;
			
		}
		
		private void theBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			
			
		}
		
		
		private void theBackgroundWorker_Complete(object sender, RunWorkerCompletedEventArgs e)
		{
			// Bei aufgetretenem Fehler
			if (e.Error != null)
			{
				
				MessageBox.Show(e.Error.Message);
				return;
			}
			
		}
		
		private void theBackgroundWorker_Cancled(object sender, CancelEventArgs e)
		{
			
		}
		
		
		bool EncodeFileViaCommandLineEncoder(EncodingGroup myEncGroup)
		{
			// String für Fehlermeldung
			//string ErrorMsg = "";
			
			
			
			// Wenn Datei bereits encodiert -> Nicht nochmals bearbeiten
			if ( myEncGroup.EncodedSuccessfully  || myEncGroup.ErrorOnEncoding )
				return true;
			
			// Pfad für die opusenc.exe suchen, muss im gleichen Verzeichnis sein
			string ApplFilePathEnc = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\opusenc.exe";
			// Pfad für opusdec.exe suchen, muss ebenfalls im gleichen Verzeichnis sein
			string ApplFilePathDec = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\opusdec.exe";
			// und für den LAME-Decoder:
			string ApplFilePathMP3Dec = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\lame.exe";
			
			// Prüfen, ob die Datein auch dort zu finden sind
			{
				if (! System.IO.File.Exists(ApplFilePathEnc) )
				{
					myEncGroup.ErrorOnEncoding = true;
					myEncGroup.ErrorMsg = "opusenc.exe isn't located in the directory of OpusDrop.exe";
					return false;
				}
				// Das gleiche für opusdec
				if (! System.IO.File.Exists(ApplFilePathDec) )
				{
					myEncGroup.ErrorOnEncoding = true;
					myEncGroup.ErrorMsg = "opusdec.exe isn't located in the directory of OpusDrop.exe";
					return false;
				}
				// und für Lame
				if (! System.IO.File.Exists(ApplFilePathDec) )
				{
					myEncGroup.ErrorOnEncoding = true;
					myEncGroup.ErrorMsg = "lame.exe isn't located in the directory of OpusDrop.exe";
					return false;
				}
			}
			
			// Wenn es eine MP3 ist, muss sie erst in das Wav-Format umgewandelt werden
			if (myEncGroup.IsMP3EncodingJob)
			{
				if(!DecodeMP3ToWavViaCommandLineEncoder(myEncGroup))
				{
					// Hier lief etwas bei der Decodierung falsch
					myEncGroup.ErrorOnEncoding = true;
					return false;
				}
			}
			
			
			// Strings für Argumente, Input und Output-Datei laden
			string Arguments = myEncGroup.UsedEncodingParameters.PrintTheParametersToArgumentString;
			string InputFilepath;
			if (!myEncGroup.IsMP3EncodingJob)
				InputFilepath = myEncGroup.Filename;
			else
				InputFilepath = myEncGroup.Filename + ".temp.wav";
			_WorkingOnThatFile = InputFilepath;
			string OutputFilepath = myEncGroup.GetOutputFilepath;
			
			///QUELLE FÜR DEN PROZESS-Starter
			/// How to redirect Standard Input/Output of an application
			/// von: Manish Ranjan Kumar (27.4.2007)
			
			// Neue ProcessStartInformation schreiben
			ProcessStartInfo myProStartInfo;
			{
				// Applikationspfad und Argumente übergeben
				if (myEncGroup.IsEncodingJob)
					myProStartInfo = new ProcessStartInfo(ApplFilePathEnc, " " +  Arguments + " \"" + InputFilepath + "\" \"" + OutputFilepath + "\"");
				else
					myProStartInfo = new ProcessStartInfo(ApplFilePathDec, "  \"" + InputFilepath + "\" \"" + OutputFilepath + "\"");
				// Kommandozeilenausgabe unterdrücken
				myProStartInfo.UseShellExecute = false;
				// Kommandozeilenfenster unterdrücken
				myProStartInfo.CreateNoWindow = true;
				// Fehlermeldungen unterdrücken
				myProStartInfo.ErrorDialog = false;
			}
			// Die Ausgabe umleiten
			{
				myProStartInfo.RedirectStandardError = true;
				//myProStartInfo.RedirectStandardInput = true;
				//myProStartInfo.RedirectStandardOutput = true;
				
			}
			
			// Die GUI updaten
			theBackgroundWorker.ReportProgress( 0 , (object) "");
			
			
			// neuen Prozess deklarieren und instanzieren
			Process myProcess = new Process();
			// Die Infos zuweisen
			myProcess.StartInfo = myProStartInfo;
			
			
			// enable raising events because Process does not raise events by default
			myProcess.EnableRaisingEvents = true;
			
			
			// Die Ausgabe umleiten
			System.Text.StringBuilder outputBuilder = new System.Text.StringBuilder();
			
			// attach the event handler for OutputDataReceived before starting the process
			myProcess.ErrorDataReceived += new DataReceivedEventHandler
				(
					delegate(object sender, DataReceivedEventArgs e)
					{
						// append the new data to the data already read-in
						outputBuilder.Append(e.Data + "\n\r");
					}
				);
			
			
			// Prozess starten
			bool ProzessGestartet = myProcess.Start();
			
			// Prozess als nicht so wichtig markieren
			myProcess.PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal;
			
			// Die Ausgaben in einen Steamwriter schreiben
			// nur der error-Reader wird wirklich benutzt
			//StreamWriter inputWriter = myProcess.StandardInput;
			//StreamReader outputReader = myProcess.StandardOutput;
			//StreamReader errorReader = myProcess.StandardError;
			
			// Den Output-Reader anwerfen
			//myProcess.BeginOutputReadLine();
			myProcess.BeginErrorReadLine();
			
			
			// Wenn der Prozess gestartet werden konnte -> warten bis fertig
			bool isResponding = true;
			
			string ErrorMsg = "";
			
			if (ProzessGestartet)
			{
				
				while (!myProcess.HasExited && isResponding)
				{
					// Prüfen ob der Prozess noch funktioniert
					isResponding = myProcess.Responding;
					
					// Man muss der error-Reader regelmäßig einlesen, sonst bleibt opusenc
					// bei sehr langen Encodierzeiten stehen
					//	ErrorMsg = errorReader.ReadToEnd();
					theBackgroundWorker.ReportProgress( 0 , "");
					
					// Im Falle eines Abbruchs der Konvertierung
					if (_KillEncodingProcess)
					{
						myProcess.Kill();
						_ProcessWasSuccKilled = true;
						myProcess.Close();
						System.IO.File.Delete(OutputFilepath);
						// Fehlermeldung in Encoding-Liste schreiben
						myEncGroup.ErrorOnEncoding = true;
						myEncGroup.ErrorMsg = "Process was killed during conversion...";
						return false;
						
					}
					
					System.Threading.Thread.Sleep(500);
					

				};
				
				if (myEncGroup.IsEncodingJob)
					ErrorMsg = ( ApplFilePathEnc + " " +  Arguments + " \"" + InputFilepath + "\" \"" + OutputFilepath + "\"\r\n\r\n" + outputBuilder.ToString().Replace("\n", "\r\n"));
				else
					ErrorMsg = ( ApplFilePathDec + " " +  Arguments + " \"" + InputFilepath + "\" \"" + OutputFilepath + "\"\r\n\r\n" + outputBuilder.ToString().Replace("\n", "\r\n"));
				
				//ErrorMsg = outputBuilder.ToString();
				
				theBackgroundWorker.ReportProgress(0, ErrorMsg);
			}
			// Process konnte nicht gestartet werden, also liegt ein Fehler vor.
			else
			{
				myEncGroup.ErrorOnEncoding = true;
				myEncGroup.ErrorMsg = "Couldn't start Job on File " + myEncGroup.Filename;
				return false;
			}
			
			// Falls es ein MP3-Job ist, die mp3.temp.wav Datei löschen
			if (myEncGroup.IsMP3EncodingJob)
			{
				string TempWaveFilepath = System.IO.Path.GetDirectoryName(myEncGroup.GetOutputFilepath) + "\\" + System.IO.Path.GetFileNameWithoutExtension(myEncGroup.GetOutputFilepath) + ".mp3.temp.wav";
				System.IO.File.Delete(TempWaveFilepath);
			}
			
			// errorReader auswerten - Entscheidet darüber, ob die Datei als gelungene
			// oder fehlerhafte Konvertierung eingestuft wird
			{
				
				string[] myReportLines = outputBuilder.ToString().Split("\n\n\r".ToCharArray());

				// Alle Zeilen auswerten
				for (int i = 0; i < myReportLines.Length; i++)
				{
					if (myEncGroup.IsEncodingJob)
					{
						if (string.Equals(myReportLines[i].Trim(), "Encoding complete"))
						{
							// Falls unter dem Zieldateinamen kein Datei hinterlegt wurde, ist wohl doch etwas schief gelaufen
							if (!System.IO.File.Exists(myEncGroup.GetOutputFilepath))
							{
								myEncGroup.ErrorOnEncoding = true;
								myEncGroup.ErrorMsg = "Opus said, it's OK, but I couldn't find the output file...\r\n" + ErrorMsg;
								return false;
							}
							else
							{ // Falls die Datei da ist, war wirklich alles in Ordnung
								myEncGroup.EncodedSuccessfully = true;
								return true;
							}
						}
					}
					if (myEncGroup.IsDecodingJob)
					{
						if (string.Equals(myReportLines[i].Trim( " ".ToCharArray()), "Decoding complete."))
						{
							// Falls unter dem Zieldateinamen kein Datei hinterlegt wurde, ist wohl doch etwas schief gelaufen
							if (!System.IO.File.Exists(myEncGroup.GetOutputFilepath))
							{
								myEncGroup.ErrorOnEncoding = true;
								myEncGroup.ErrorMsg = "Opus said, it's OK, but I couldn't find the output file...\r\n" + ErrorMsg;
								return false;
							}
							else
							{ // Falls die Datei da ist, war wirklich alles in Ordnung
								myEncGroup.EncodedSuccessfully = true;
								return true;
							}
						}
					}
				}
				
				// Wenn die Erfolgsmeldung oben nicht generiert wurde, liegt ein Fehler vor
				myEncGroup.ErrorOnEncoding = true;
				myEncGroup.ErrorMsg = "Couldn't find the success-message in the opus-output... do you use an new version of the Opus-Tools?\r\n" + ErrorMsg;
				return false;
				
			}
			


		}
		
		/// <summary>
		/// Die MP3-Datei wird erst in das WAV-Format überführt, später dann gelöscht
		/// </summary>
		/// <param name="myEncGroup"></param>
		/// <returns></returns>
		bool DecodeMP3ToWavViaCommandLineEncoder(EncodingGroup myEncGroup)
		{
			
			
			
			
			string ApplFilePathMP3Dec = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\lame.exe";
			
			
			
			// Strings für Argumente, Input und Output-Datei laden
			string Arguments = "--decode --silent ";
			string InputFilepath = myEncGroup.Filename;
			//_WorkingOnThatFile = InputFilepath;
			string OutputFilepath = System.IO.Path.GetDirectoryName(myEncGroup.GetOutputFilepath) + "\\" + System.IO.Path.GetFileNameWithoutExtension(myEncGroup.GetOutputFilepath) + ".mp3.temp.wav";
			
			///QUELLE FÜR DEN PROZESS-Starter
			/// How to redirect Standard Input/Output of an application
			/// von: Manish Ranjan Kumar (27.4.2007)
			
			// Neue ProcessStartInformation schreiben
			ProcessStartInfo myProStartInfo;
			{
				// Applikationspfad und Argumente übergeben
				myProStartInfo = new ProcessStartInfo(ApplFilePathMP3Dec, " " +  Arguments + " \"" + InputFilepath + "\" \"" + OutputFilepath + "\"");
				
				// Kommandozeilenausgabe unterdrücken
				myProStartInfo.UseShellExecute = false;
				// Kommandozeilenfenster unterdrücken
				myProStartInfo.CreateNoWindow = true;
				// Fehlermeldungen unterdrücken
				myProStartInfo.ErrorDialog = false;
			}
			// Die Ausgabe umleiten
			{
				myProStartInfo.RedirectStandardError = true;
				//myProStartInfo.RedirectStandardInput = true;
				//myProStartInfo.RedirectStandardOutput = true;
				
			}
			
			// Die GUI updaten
			theBackgroundWorker.ReportProgress( 0 , (object) "");
			
			
			// neuen Prozess deklarieren und instanzieren
			Process myProcess = new Process();
			// Die Infos zuweisen
			myProcess.StartInfo = myProStartInfo;
			
			
			// enable raising events because Process does not raise events by default
			myProcess.EnableRaisingEvents = true;
			
			
			// Die Ausgabe umleiten
			System.Text.StringBuilder outputBuilder = new System.Text.StringBuilder();
			
			// attach the event handler for OutputDataReceived before starting the process
			myProcess.ErrorDataReceived += new DataReceivedEventHandler
				(
					delegate(object sender, DataReceivedEventArgs e)
					{
						// append the new data to the data already read-in
						outputBuilder.Append(e.Data + "\n\r");
					}
				);
			
			
			// Prozess starten
			bool ProzessGestartet = myProcess.Start();
			
			// Prozess als nicht so wichtig markieren
			myProcess.PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal;
			
			// Die Ausgaben in einen Steamwriter schreiben
			// nur der error-Reader wird wirklich benutzt
			//StreamWriter inputWriter = myProcess.StandardInput;
			//StreamReader outputReader = myProcess.StandardOutput;
			//StreamReader errorReader = myProcess.StandardError;
			
			// Den Output-Reader anwerfen
			//myProcess.BeginOutputReadLine();
			myProcess.BeginErrorReadLine();
			
			
			// Wenn der Prozess gestartet werden konnte -> warten bis fertig
			bool isResponding = true;
			
			string ErrorMsg = "";
			
			if (ProzessGestartet)
			{
				
				while (!myProcess.HasExited && isResponding)
				{
					// Prüfen ob der Prozess noch funktioniert
					isResponding = myProcess.Responding;
					
					// Man muss der error-Reader regelmäßig einlesen, sonst bleibt opusenc
					// bei sehr langen Encodierzeiten stehen
					//	ErrorMsg = errorReader.ReadToEnd();
					theBackgroundWorker.ReportProgress( 0 , "");
					
					// Im Falle eines Abbruchs der Konvertierung
					if (_KillEncodingProcess)
					{
						myProcess.Kill();
						_ProcessWasSuccKilled = true;
						myProcess.Close();
						System.IO.File.Delete(OutputFilepath);
						// Fehlermeldung in Encoding-Liste schreiben
						myEncGroup.ErrorOnEncoding = true;
						myEncGroup.ErrorMsg = "Process was killed during conversion...";
						return false;
						
					}
					
					System.Threading.Thread.Sleep(500);
					

				};
				
				ErrorMsg = ( ApplFilePathMP3Dec + " " +  Arguments + " \"" + InputFilepath + "\" \"" + OutputFilepath + "\"\r\n\r\n" + outputBuilder.ToString().Replace("\n", "\r\n"));
				
				
				theBackgroundWorker.ReportProgress(0, ErrorMsg);
			}
			// Process konnte nicht gestartet werden, also liegt ein Fehler vor.
			else
			{
				myEncGroup.ErrorOnEncoding = true;
				myEncGroup.ErrorMsg = "Couldn't start MP3-Decoding Job on File " + myEncGroup.Filename;
				return false;
			}
			
			
			{ // Falls die Datei da ist, war wirklich alles in Ordnung
				if (System.IO.File.Exists(OutputFilepath))
					return true;
				else
				{
					myEncGroup.ErrorMsg = ErrorMsg;
					return false;
				}
				
			}
			
			
		}
		


	}



	// Partielle Klasse für die GUI-Steuerung (Prüfung der Eingabewerte)
	// This partial class is about proofing the input-values
	public partial class MainForm : Form
	{
		
		void UpdateJobList()
		{
			// Fortschritt berechnen
			int[] JobList = CountJoblist();
			
			// in GUI eintragen
			textBox_JobCounter.Text = JobList[0].ToString() + " / " + JobList[1].ToString() + " / " + JobList[2].ToString();
			
		}
		
		
		
		bool CheckIfTextboxsNumberIsWithinIntervall(TextBox myTB , int Minimum, int Maximum)
		{
			int mI;
			// Prüfen, ob Integer überhaupt lesbar
			try {
				mI = Convert.ToInt32(myTB.Text);
				
			} catch (Exception) {
				ColourTextboxToRed(myTB);
				LockUpGUI(myTB);
				return false;
			}
			
			// Prüfen ob er im Intervall liegt
			if ( (mI < Minimum) || (Maximum < mI) )
			{
				ColourTextboxToYellow(myTB);
				LockUpGUI(myTB);
				return false;
			}
			
			// Wenn ja, alles frei geben
			ColourTextBoxToLightGreen(myTB);
			UnlockGUI();
			return true;
		}
		
		bool CheckIfTextboxsNumberIsOneElementOfArray(TextBox myTB , int[] myElements)
		{
			int mI;
			// Prüfen, ob Integer überhaupt lesbar
			try {
				mI = Convert.ToInt32(myTB.Text);
				
			} catch (Exception) {
				ColourTextboxToRed(myTB);
				LockUpGUI(myTB);
				return false;
			}
			
			// Prüfen ob es eines der Elemente ist
			foreach(int aNumber in myElements)
			{
				if (mI == aNumber)
				{
					// Wenn ja, alles frei geben
					ColourTextBoxToLightGreen(myTB);
					UnlockGUI();
					return true;
				}
			}

			// Wenn der Ausstieg nicht geschafft wurde, ist die Zahl nicht enthalten gewesen
			ColourTextboxToYellow(myTB);
			LockUpGUI(myTB);
			return false;

			

		}
		
		void ColourTextboxToRed(TextBox myTB)
		{
			myTB.BackColor = Color.Red;
		}
		void ColourTextboxToYellow(TextBox myTB)
		{
			myTB.BackColor = Color.Yellow;
		}
		void ColourTextBoxToLightGreen(TextBox myTB)
		{
			myTB.BackColor = Color.LightGreen;
		}
		
		void LockUpGUI(TextBox ThisTextBoxIsntLockedUp)
		{
			// Prüfen, ob GUI schon verriegelt, dann keine Aktion nötig
			if (_GUILockedUp)
				return;
			
			// Marker setzen, damit nicht doch encodiert wird
			_GUILockedUp = true;
			
			// Alle Elemente verriegeln
			{
				textBox_Bitrate.Enabled = false;
				textBox_Framesize.Enabled = false;
				textBox_NumberOfThreads.Enabled = false;
//				radioButton_Optimization_Default.Enabled = false;
//				radioButton_Optimization_Music.Enabled = false;
//				radioButton_Optimization_Speech.Enabled = false;
				radioButton_Bitratehandling_CBR.Enabled = false;
				radioButton_Bitratehandling_CVBR.Enabled = false;
				radioButton_Bitratehandling_VBR.Enabled = false;
				label_DropFiles.Enabled = false;
				pictureBox_Opus.Enabled = false;
				checkBox_DownMixToMono.Enabled = false;
//				checkBox_OneMonoStreamPerChannel.Enabled = false;
			}
			
			// Die Ausnahme freischalten
			ThisTextBoxIsntLockedUp.Enabled = true;
			// Cousor in die Textbox legen
			ThisTextBoxIsntLockedUp.Focus();
			
			return;
		}
		
		void UnlockGUI()
		{
			// Prüfen, ob GUI schon freigegeben, falls ja, dann keine weitere Aktion nötig
			if (!_GUILockedUp)
				return;
			
			// Marker setzen, damit wieder encodiert werden kann
			_GUILockedUp = false;
			
			// Alle Elemente freigeben
			{
				textBox_Bitrate.Enabled = true;
				textBox_Framesize.Enabled = true;
				textBox_NumberOfThreads.Enabled = true;
				//radioButton_Optimization_Default.Enabled = true;
				//radioButton_Optimization_Music.Enabled = true;
				//radioButton_Optimization_Speech.Enabled = true;
				radioButton_Bitratehandling_CBR.Enabled = true;
				radioButton_Bitratehandling_CVBR.Enabled = true;
				radioButton_Bitratehandling_VBR.Enabled = true;
				label_DropFiles.Enabled = true;
				pictureBox_Opus.Enabled = true;
				checkBox_DownMixToMono.Enabled = true;
				//checkBox_OneMonoStreamPerChannel.Enabled = true;
			}
			
			return;
		}
		
		void TextBox_NumberOfThreadsTextChanged(object sender, EventArgs e)
		{
			CheckIfTextboxsNumberIsWithinIntervall(textBox_NumberOfThreads, 1, 128);
		}
		
		void TextBox_BitrateTextChanged(object sender, EventArgs e)
		{
			CheckIfTextboxsNumberIsWithinIntervall(textBox_Bitrate, 6 , 256);
		}
		void TextBox_FramesizeTextChanged(object sender, EventArgs e)
		{
			int[] AllowedValues = new int[5];
			AllowedValues[0] = 5;
			AllowedValues[1] = 10;
			AllowedValues[2] = 20;
			AllowedValues[3] = 40;
			AllowedValues[4] = 60;
			CheckIfTextboxsNumberIsOneElementOfArray(textBox_Framesize, AllowedValues);
		}
		
		void TextBox_DebuggerDoubleClick(object sender, System.EventArgs e)
		{
			textBox_Debugger.Text = "The Debugger is saying:\r\n" + _DebugMessage;
		}
		
		void TextBox_JobCounterDoubleClick(object sender, EventArgs e)
		{
			if (CountJoblist()[0] == 0)
				return;
			
			// Box aktualisieren
			UpdateJobList();
			
			textBox_Debugger.Text = GetListOfEncodingErrorFilenames();
			
		}
		
	}




//	public static class ReadOpusEnc_exe_Output
//	{
//
//		public static string GetLastMessageOfOpusEncExe(StreamReader myReader)
//		{
//			bool FoundLastline = false;
//
//			return myReader.ReadToEnd();
//
//			string LastLine = "NothingNew";
//			while (true)
//			{
//
//				string newLine = myReader.ReadLine();
//
//				if (  (newLine == null) || (newLine.Length == 0)  )
//					FoundLastline = true;
//				else
//				{
//					if ( '[' == newLine.ToCharArray()[0])
//						LastLine = newLine;
//				}
//
//
//
//
//				if (FoundLastline)
//				{
//					return LastLine.Trim(' ');
//				}
//			}
//
//
//
//		}
//
//	}

	// Enthält alle Daten die pro Datei dem Encoding zugeordnet werden können
	public class EncodingParamterCollection
	{
		
		bool _Bitrateregime_VBR;
		bool _Bitrateregime_CVBR;
		bool _Bitrateregime_CBR;
		bool _DownmixToMono;
		bool _OneStreamPerChannel;
		bool _Optimization_Default;
		bool _Optimization_Speech;
		bool _Optimization_Musik;
		int _BitratePerSecond;
		int _FrameSizeInMiliseconds;
		
		
		public EncodingParamterCollection()
		{
			
		}
		
		public string PrintTheParametersToArgumentString
		{
			get {
				string Arguments = "";
				
				// Optimierung
				{
					if ( _Optimization_Musik )
						Arguments = Arguments + " --music";
					
					if (_Optimization_Speech)
						Arguments = Arguments + " --speech";
				}
				
				// Bitrate
				{
					Arguments = Arguments + " --bitrate " + _BitratePerSecond.ToString() + " ";
				}
				
				// VBR, CBR,
				{
					if (_Bitrateregime_CBR)
						Arguments = Arguments + " --hard-cbr";
					if (_Bitrateregime_VBR)
						Arguments = Arguments + " --vbr";
					if (_Bitrateregime_CVBR)
						Arguments = Arguments + " --cvbr";
				}
				
				// Mono-Optionen
				{
					if (_DownmixToMono)
						Arguments = Arguments + " --downmix-mono";
					
					if (_OneStreamPerChannel)
						Arguments = Arguments + " --uncoupled";
				}
				
				Arguments = Arguments + " --ignorelength";
				
				return Arguments;
			}
		}
		
		public bool UseVBREncoding
		{
			get {
				return _Bitrateregime_VBR;
			}
			set {
				if (!value)
					throw new ArgumentException("Don't disable VBR, instead enable CBR or CVBR");
				_Bitrateregime_VBR = true;
				_Bitrateregime_CBR = false;
				_Bitrateregime_CVBR = false;
			}
			
		}
		
		public bool UseCBREncoding
		{
			get {
				return _Bitrateregime_CBR;
			}
			set {
				if (!value)
					throw new ArgumentException("Don't disable CBR, instead enable VBR or CVBR");
				_Bitrateregime_CBR = true;
				_Bitrateregime_VBR = false;
				_Bitrateregime_CVBR = false;
			}
			
		}
		public bool UseCVBREncoding
		{
			get {
				return _Bitrateregime_CVBR;
			}
			set {
				if (!value)
					throw new ArgumentException("Don't disable CVBR, instead enable CBR or VBR");
				_Bitrateregime_VBR = true;
				_Bitrateregime_CBR = false;
				_Bitrateregime_CVBR = false;
			}
			
		}
		
		public bool DownMixToMono
		{
			get { return _DownmixToMono; }
			set { _DownmixToMono = value;}
		}
		
		public bool OneStreamPerChannel
		{
			get { return _OneStreamPerChannel; }
			set  { _OneStreamPerChannel = value; }
		}
		public bool Optimization_Default {
			get { return _Optimization_Default; }
			set {
				if (!value)
					throw new ArgumentException("Don't disable an optimization, enable just another one");
				
				_Optimization_Default = true;
				_Optimization_Speech = false;
				_Optimization_Musik = false;
			}
		}
		
		public bool Optimization_Speech {
			get { return _Optimization_Speech; }
			set {
				if (!value)
					throw new ArgumentException("Don't disable an optimization, enable just another one");
				
				_Optimization_Default = false;
				_Optimization_Speech = true;
				_Optimization_Musik = false;
			}
		}
		
		public bool Optimization_Music {
			get { return _Optimization_Musik; }
			set {
				if (!value)
					throw new ArgumentException("Don't disable an optimization, enable just another one");
				
				_Optimization_Default = false;
				_Optimization_Speech = false;
				_Optimization_Musik = true;
			}
		}
		
		public int Bitrate
		{
			get {return _BitratePerSecond;}
			set {_BitratePerSecond = value;}
		}
		public int FramesizeInMiliseconds {
			get {
				return _FrameSizeInMiliseconds;
			}
			set { _FrameSizeInMiliseconds = value ;}
		}
		
		
	}

	public class EncodingGroup
	{
		///<summary>
		///  Besteht aus dem Dateinamen sowie den zu verwendenden Parametern
		///  Consists of the filename and the used encoding-parameters
		/// </summary>
		
		EncodingParamterCollection _myCol;
		string _Filename;
		string _ErrorMsg;
		bool _WasSuccessfullyEncoded;
		bool _GaveAnErrorDurringEncoding;
		bool _IsDecodingJob;
		bool _IsEncodingJob;
		bool _IsMP3EncodingJob;
		
		public EncodingGroup()
		{
			_myCol = new EncodingParamterCollection();
			_ErrorMsg = "";
			_Filename = "";
		}
		
		public string Filename{
			get {return _Filename;}
			set {
				_Filename = value;
				
				// Anhand der Dateiendung feststellen, ob Encodiert oder Decodiert werden muss
				if ( String.Equals(Path.GetExtension(_Filename).ToLower(), ".wav") )
				{
					_IsDecodingJob = false;
					_IsEncodingJob = true;
					_IsMP3EncodingJob = false;
				}
				else
				{ // -> Jetzt müsste es ein Decoding-Job sein
					if ( String.Equals(Path.GetExtension(_Filename).ToLower(), ".opus") )
					{
						_IsDecodingJob = true;
						_IsEncodingJob = false;
						_IsMP3EncodingJob = false;
					}
					else
					{
						if ( String.Equals(Path.GetExtension(_Filename).ToLower(), ".mp3") )
						{
							_IsDecodingJob = false;
							_IsEncodingJob = true;
							_IsMP3EncodingJob = true;
						}
						else
						{
							// Mit dem Dateinamen stimmt offenbar etwas nicht
							_IsEncodingJob = false;
							_IsDecodingJob = false;
							_IsMP3EncodingJob = false;
							throw new ArgumentException("Error on classifying " + _Filename + " wether it's a encoding or a decoding-job");
						}
					}
				}
			}
		}
		
		public string ErrorMsg{
			get {
				return _ErrorMsg;
			}
			set {
				_ErrorMsg = value;
			}
		}
		
		public bool IsEncodingJob{
			get {return _IsEncodingJob;}
		}
		
		public bool IsMP3EncodingJob{
			get {return _IsMP3EncodingJob;}
		}
		
		public bool IsDecodingJob{
			get{return _IsDecodingJob;}
		}
		
		public EncodingParamterCollection UsedEncodingParameters
		{
			get { return _myCol;}
			set { _myCol = value;	}
		}
		
		public string GetOutputFilepath
		{
			get {
				if (_IsEncodingJob)
				{
					return Path.ChangeExtension(_Filename , ".opus");
				}
				
				if (_IsDecodingJob)
					return Path.ChangeExtension(_Filename , ".wav");
				
				return "";
				
			}
		}
		
		public bool EncodedSuccessfully
		{
			get {
				return _WasSuccessfullyEncoded;
			}
			set { _WasSuccessfullyEncoded = value;}
		}
		
		public bool ErrorOnEncoding
		{
			get {
				return _GaveAnErrorDurringEncoding;
			}
			set { _GaveAnErrorDurringEncoding = value;}
		}
		
		
		
	}

}
