using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;


// Bitte beachten Sie auch den Fortsetzungsband zu diesem Buch, falls
// Ihnen das Programmieren Spaß gemacht hat und Sie sich fortgeschritteneren 
// Themen zuwenden wollen. Alle Informationen zum Fortsetzungsband finden
// Sie auf der Website: www.tiefer-einsteigen.de 


namespace ExceptionLog
{
    public enum LoggingLevel
    {
        Low,
        Medium,
        High
    }

    public class Logger
    {
        /// <summary>
        /// Liest/setzt das Protokoll-Level
        /// </summary>
        public LoggingLevel LogLevel { get; set; }

        /// <summary>
        /// Ruft den aktuellen Namen der Protokolldatei ab (schreibgeschützt)
        /// </summary>
        public string LogFile { get; private set; }


        #region Konstruktoren
        /// <summary>
        /// Standardkonstruktor, legt den Dateinamen nach den 
        /// Standardkonventionen der Klasse fest
        /// </summary>
        public Logger()
        {
            // Standardwert für Protokoll-Level festlegen
            this.LogLevel = LoggingLevel.High;

            // Der Pfadname der Log-Datei wird mit einem StringBuilder-
            // Objekt zusammengebaut
            StringBuilder sb = new StringBuilder();

            // Pfad der ausgeführten Anwendung holen (ohne Dateinamen)
            sb.Append(Application.StartupPath);

            // Slash anhängen
            sb.Append("\\");

            // Datum im Kurzformat anhängen
            sb.Append(DateTime.Now.ToShortDateString());

            // Unterstrich anhängen
            sb.Append("_");

            // Namen der exe-Datei ermitteln und anhängen
            sb.Append(Path.GetFileName(Application.ExecutablePath));
            // Erweiterung .log anhängen
            sb.Append(".log");

            // Name in der Klassenvariablen ablegen
            this.LogFile = sb.ToString();

        
        }

        /// <summary>
        /// Konstruktor, mit dem Pfad/Name der Protokolldatei 
        /// festgelegt werden kann.
        /// </summary>
        /// <param name="logFile">Kompletter Pfadname der Protokolldatei</param>
        /// <remarks>Das in logFile angegebene Verzeichnis muss vorhanden sein.
        /// </remarks>
        public Logger(string logFile)
        {
            // Standardwert für Protkoll-Level festlegen
            this.LogLevel = LoggingLevel.High;

            // Dateinamen so eintragen, wie er an den Konstruktor übergeben wird
            this.LogFile = logFile;
        }



        /// <summary>
        /// Konstruktor, mit dem sowohl Pfad/Name der Protokolldatei 
        /// als auch das Protokoll-Level festgelegt werden kann.
        /// </summary>
        /// <param name="logFile">Kompletter Pfadname der Protokolldatei</param>
        /// <param name="logLevel">gewünschtes Protokoll-Level</param>
        /// <remarks>Das in logFile angegebene Verzeichnis muss 
        ///  vorhanden sein.
        /// </remarks>
        public Logger(string logFile, LoggingLevel logLevel)
        {
            this.LogFile = logFile;
            this.LogLevel = logLevel;
        }
        #endregion


        #region Öffentliche Methoden
        /// <summary>
        /// Schreibt Informationen über eine Ausnahme in die Protokolldatei
        /// </summary>
        /// <param name="ex">Ausnahme, die protokolliert werden soll</param>
        public void DoLog(Exception ex)
        {
            // Datei öffnen lassen
            StreamWriter sw = openLogFile();

            // Wenn das Öffnen erfolgreich war
            if (sw != null)
            {
                try
                {
                    // Datum und Uhrzeit wegschreiben
                    writeDateTime(sw);
                    // Inforamtionen über Ausnahme wegschreiben
                    writeException(sw, ex, this.LogLevel);
                }
                finally
                {
                    // Datei wieder schließen und Ressource freigeben                    
                    sw.Close();
                }
            }
        }


        /// <summary>
        /// Schreibt den übergebenen String in die Protokolldatei
        /// </summary>
        /// <param name="msg">Meldungstext</param>
        public void DoLog(string msg)
        {
            // Datei öffnen lassen
            StreamWriter sw = openLogFile();

            // wenn Datei erfolgreich geöffnet wurde
            if (sw != null)
            {
                try
                {
                    // Datum und Uhrzeit wegschreiben
                    writeDateTime(sw);
                    // Meldung wegschreiben
                    sw.WriteLine(msg);
                }
                finally
                {
                    // Datei wieder schließen und Ressource freigeben                    
                    sw.Close();
                }
            }
        }

        #endregion

        #region Interne Methoden
        /// <summary>
        /// Öffnet eine vorhandene Protokolldatei oder 
        /// erstellt eine neue.
        /// </summary>
        /// <returns>StreamWriter-Instanz oder null</returns>
        private StreamWriter openLogFile()
        {
            StreamWriter sw = null;
            try
            {
                // wenn Datei vorhanden ist
                if (File.Exists(this.LogFile))
                {
                    // Datei im Append-Modus öffnen
                    sw = File.AppendText(this.LogFile);
                    // Hinter letzten Eintrag der Datei Leerzeile einfügen
                    sw.WriteLine();
                }
                // Neue Datei erstellen, wenn Datei noch nicht da íst
                else
                {
                    sw = File.CreateText(this.LogFile);
                }
            }
            catch
            {
            }

            // Streamwriter zurückgeben, kann null sein
            return sw;
        }

        /// <summary>
        /// Schreibt das aktuelle Datum und die aktuelle Uhrzeit
        /// in die Protokolldatei.
        /// </summary>
        /// <param name="sw">StreamWriter-Instanz</param>
        /// <remarks>
        /// Wenn Sie das Format von Datum/Uhrzeit zu einem späteren
        /// Zeitpunkt ändern wollen, reicht es aus, diese Methode anzuopassen.
        /// Darum befindet sich die Funtkionalität in einer eigenen
        /// privaten Methode.
        /// </remarks>
        private void writeDateTime(StreamWriter sw)
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(DateTime.Now.ToShortDateString());
                sb.Append(" - ");
                sb.Append(DateTime.Now.ToLongTimeString());
                sw.WriteLine(sb.ToString());
            }
            catch
            {
                throw;
            }
        }


        /// <summary>
        /// Schreibt Informationen über eine Ausnahme in die Protokolldatei.
        /// </summary>
        /// <param name="sw">StreamWriter-Instanz</param>
        /// <param name="ex">Die zu protokollierende Ausnahme</param>
        /// <param name="logLevel">Das Protokoll-Level</param>
        /// <remarks>
        /// Hier können Sie anpassen, welche Informationen bei welchem
        /// Level protokolliert werden sollen.
        /// </remarks>
        private void writeException(StreamWriter sw, Exception ex, LoggingLevel logLevel)
        {
            switch (logLevel)
            {
                case LoggingLevel.Low:
                    sw.WriteLine("Message: " + ex.Message);
                    break;

                case LoggingLevel.Medium:
                    sw.WriteLine("Message: " + ex.Message);
                    if (ex.Source != null)
                        sw.WriteLine("Source: " + ex.Source);
                    if (ex.TargetSite != null)
                        sw.WriteLine("TargetSite: " + ex.TargetSite.ToString());
                    break;

                case LoggingLevel.High:
                    sw.WriteLine("Message: " + ex.Message);
                    if (ex.Source != null)
                        sw.WriteLine("Source: " + ex.Source);
                    if (ex.TargetSite != null)
                        sw.WriteLine("TargetSite: " + ex.TargetSite.ToString());
                    if (ex.StackTrace != null)
                        sw.WriteLine("StackTrace: " + ex.StackTrace);
                    break;
            }
        }
        #endregion


    }
}
