using KarleyLibrary.Erweiterungen;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WK5.Core;
using WK5.Core.Drucken.Briefe;
using WK5.Core.Drucken.Rechnungen;
using WK5.Core.Models;

namespace CoreTester
{
    /// <summary>
    /// Diese Klasse enthält nur Beispiele, für Karsten, damit er weiß, wie etwas umgesetzt werden kann
    /// </summary>
    internal class BeispieleFürKarsten
    {

        /// <summary>
        /// Dieses Beispiel zeigt, wie man mit dem FbController umgeht
        /// </summary>
        /// <returns></returns>
        public async Task UmgangMitFbControllerAsync()
        {
            // Erstellen eines FbControllers immer mit using!
            // Die einzige Ausnahme hierbei ist, wenn der FbController in einer Forms Anwednung einmalig generiert wird und an alle Komponenten weitergegeben wird.
            // Beispiel: ShopImport
            // Wird der FbController ohne using erstellt, dann bleibt die Verbindung solange offen, bis das Programm geschlossen wurde.
            using FbController2 fbController = new FbController2();

            // Asynchrone Funktionen gehen immer vor.
            // Die synchronen Funktionen sollten nur im absoluten Ausnahmefall benutzt werden.
            // Prinzipiell gilt: Wenn man die synchrone Funktion nutzt, macht man was falsch

            // Beispiel 1: Einzelnen Datensatz laden
            // Wir möchten alle Daten für den Kunden 10100 laden

            fbController.AddParameter("@KUND_A_NR", "10100");
            DataRow? row = await fbController.SelectRowAsync("SELECT * FROM KUNDEN WHERE KUND_A_NR = @KUND_A_NR");
            // Sofern die Abfrage ohne Fehler durchläuft, wurden an dieser Stelle die zuvor erstellten Parameter gelöscht.
            // Im Falle eines Fehler würde dieser Abschnitt so aussehen:
            try
            {
                fbController.AddParameter("@KUND_A_NR", "10100");
                DataRow? row2 = await fbController.SelectRowAsync("SELECT * FROM KUNDEN WHERE KUND_A_NR = @KUND_A_NR");
            }
            catch (Exception)
            {
                // Tue was du willst
                // Parameter können noch über den FbController fürs Logging abgefragt werden.
            }
            finally
            {
                fbController.ClearParameters();
            }


            // Beispiel 2: Mehrere Daten Laden
            // Wir hätten gerne alle Selektionsmerkmale vom Typen 1
            DataTable data = await fbController.SelectDataAsync("SELECT * FROM SELEKTIONSMERKMAL WHERE SELE_N_TYP = 1");

            // Daten verarbeiten
            // Hier darf nicht mit var gearbeitet werden. DataRow muss spezifiziert sein
            foreach (DataRow selektionsRow in data.Rows)
            {
                // Siehe UmgangMitObjectErweiterungAsync für weitere Hilfe
                Selektionsmerkmal merkmal = UmgangMitObjectErweiterung(selektionsRow);
            }

            // Beispiel 3: SQL ausführen
            // Ziemlich selbsterklärend:
            await fbController.QueryAsync("UPDATE KUNDEN SET KUND_A_NAME2 = 'TEST' WHERE KUND_A_NR = '10100'");


            // Beispiel 4: Einzelnen Datensatz via Dapper laden
            // Der Unterschied zu Dapper ist, dass die Parameter nochmals als anonymous type deklariert werden müssen. 
            // fbController.AddParameter(); ist hierbei komplett irrelevant
            // Der Vorteil mit Dapper ist, dass die Umwandlung in etwa 5 mal schneller läuft als über die ObjektErweiterung und viele unnötige Codezeilen gespart werden können.
            // Die CompareField-Logik findet hier ebenfalls statt. Die Zuordnung für Dapper wird einmalig im statischen Konstruktor des FbController2 generiert.
            Kunde? kunde = await fbController.SelectRowAsync<Kunde>("SELECT * FROM KUNDEN WHERE KUND_A_NR = @KUND_A_NR AND 1 = @TEST", new
            {
                KUND_A_NR = "10100",
                TEST = 10000
            });

            // Beispiel 5: Mehrere Daten mit Dapper laden
            // Ziemlich selbsterklärend:
            // Wenn keine Ergebnisse, dann ist die Liste leer.
            List<Selektionsmerkmal> selektionsmerkmale = await fbController.SelectDataAsync<Selektionsmerkmal>("SELECT * FROM SELEKTIONSMERKMAL WHERE SELE_N_TYP = 1");


            // Beispiel 6: Transaction durchführen
            // Wichtig hierbei ist nur, dass du bei jeder Funktion die SQL durchführt den FbController übergibst und keinen neuen generierst,
            // da ansonsten nicht auf die bereits geänderten aber nicht committen Daten zugegriffen werden kann.
            // Ein relevativ bekanntes Beispiel hierbei wäre das Lagerregal, dieses muss die geänderten Bestandsdaten auslesen, bevor die neuen Belege (LS und RE) in die Datenbank commited werden
            // Sollte ziemlich selbsterklärend sein:
            //await fbController.StartTransactionAsync();
            //// Mache hier was immer du willst
            //await fbController.CommitChangesAsync();
            //await fbController.RollbackChangesAsync();


        }
        /// <summary>
        /// Dieses Beispiel zeigt eine ObjektUmwandlung mittels <see cref="ObjectErweiterung.DataRowZuObjekt{T}(T, DataRow)"/>
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        private Selektionsmerkmal UmgangMitObjectErweiterung(DataRow row)
        {
            // Wir haben eine eigene Klasse geschrieben, um eine DataRow automatisch in ein Objekt umzuwandeln.
            // Dazu geben wir das Objekt per new als ersten Parameter und die DataRow als zweiten Parameter an die folgende Funktion:
            Selektionsmerkmal merkmal = ObjectErweiterung.DataRowZuObjekt(new Selektionsmerkmal(), row);

            /* Hierbei gibt es ein paar Besonderheiten:
             * 1. Firebird Booleans werden automatisch zu einem C# Boolean umgewandelt
             * 2. Er sucht standardmäßig nach den Property Namen, ob dieser 1:1 übereinstimmt.
             * 3. Wenn der Name nicht 1:1 übereinstimmt, dann geht er noch zusätzlich her und prüft auf ein CompareFieldAttribute. In diesem
             * wird ein Datenbankname umgewandelt in eine Property.
             * 
             * Beispiel:
             * [CompareField("ARTI_A_NR")]
             * public string Artikelnummer { get; set; }
             * 
             * Die Eigenschaft heißt Artikelnummer, über unserer SQL bekommen wir aber ARTI_A_NR zurück.
             * Im ersten Schritt wird ARTI_A_NR nicht als Property gefunden. Im Zweiten allerdings schon, da das CompareField angibt, dass ARTI_A_NR zur Artikelnummer gehört.
             * Diese Zuordnung findet so auch 1:1 für Dapper statt.
            */
            


            return merkmal;
        }
    
        /// <summary>
        /// Dieses Beispiel zeigt, wie mit den Klassen gearbeitet wird, die ein PDF generieren sollen.
        /// </summary>
        /// <returns></returns>
        private async Task PdfGenerierenAsync()
        {
            // Als Beispiel nehmen wir hier eine Rechnung
            using FbController2 fbController = new FbController2();

            Rechnung? rechnung = await Rechnung.GetRechnungAsync(30000, fbController);

            // Die Syntax ist für jedes Dokument die gleiche. 
            // Es gibt eine public CreateAsync Methode, welche alle Daten entgegennimmt, die für die Generierung des PDFs notwendig sind.
            // Die Funktion ruft dann einen privaten Konstruktor auf und setzt alle Daten entsprechend und bereitet Regeln und das Template vor.
            // In den Regelsatz sind nur die Regeln relevant, die nicht durch die Funktion SetRegeln() des PrintRechnung Objektes gesetzt werden können.
            // Der generelle Aufbau aller Funktionen ist immer identisch und kann mit der Implentierung von PrintBase automatisch erzeugt werden.
            // Halte dich einfach an die Struktur vom Drucken Ordner und du bist good to go.
            // Die Templates und CSS Dateien sind in Drucken/DruckTemplates und Drucken/DruckTemplates/CSS enthalten.
            // Wichtig ist, dass in den Eigenschaften der Dateien die Option "Immer kopieren" ausgewählt ist, da die Templates ansonsten nicht mit in den Projektoutput kopiert werden
            PrintRechnungRegelsatz regeln = new PrintRechnungRegelsatz
            {
                ShowHeader = true,
                ShowFooter = true
            };
            PrintRechnung printRechnung = await PrintRechnung.CreateAsync(rechnung, regeln, fbController);
            // PDF auf F generieren
            printRechnung.Print("F:");
        }
    }
}
