using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.IO;
using XMLParser.Model;

namespace XMLParser
{
    public class XMLParse
    {
        private string xmlFile;
        private List<AbwassertechnischeAnlage> stammdaten = null;
        private List<InspizierteAbwassertechnischeAnlage> inspektionsdaten = null;
        private XmlNamespaceManager manager = null;
        private XmlNode rootChild = null;
        private List<KanalObjekt> kanalObjekte = null;
        private Queue<AbwassertechnischeAnlage> QueueToRebuild = new Queue<AbwassertechnischeAnlage>();

        public List<KanalObjekt> KanalObjekte { get => kanalObjekte; private set => kanalObjekte = value; }
        public string XmlFile {get => xmlFile; private set => xmlFile = value;}
        public XMLParse(string xmltoparse)
        {
            XmlFile = xmltoparse;
            KanalObjekte = new List<KanalObjekt>();
            stammdaten = new List<AbwassertechnischeAnlage>();
            inspektionsdaten = new List<InspizierteAbwassertechnischeAnlage>();
            ParseFile();
            RebuildStammdaten();
        }

        private void RebuildStammdaten()
        {
            // liste an knoten
            List<AbwassertechnischeAnlage> knoten = stammdaten.FindAll(x => x.ObjektArt == EObjektArt.KNOTEN);
            while(QueueToRebuild.Count > 0)
            {
                AbwassertechnischeAnlage anlage = QueueToRebuild.Dequeue();
                anlage.Kante.KnotenZulauf = knoten.Find(x => x.Objektbezeichnung.Equals(anlage.Kante.knotenZulaufTemp)).Knoten;
                anlage.Kante.KnotenAblauf = knoten.Find(x => x.Objektbezeichnung.Equals(anlage.Kante.knotenAblaufTemp)).Knoten;
            } 
        }

        private void ParseFile()
        {
            Tuple<List<AbwassertechnischeAnlage>,List<InspizierteAbwassertechnischeAnlage>> src = LoadRawFile();
            stammdaten = src.Item1;
            inspektionsdaten = src.Item2;
            foreach(InspizierteAbwassertechnischeAnlage iat in inspektionsdaten) {
                AbwassertechnischeAnlage stammdatens = stammdaten.Find(x => x.Objektbezeichnung.Equals(iat.Objektbezeichnung));//;
                string[] filepathsplited = this.XmlFile.Split(Path.DirectorySeparatorChar);
                
                KanalObjekte.Add(new KanalObjekt() {
                    XmlFileName = filepathsplited[filepathsplited.Count()-1],
                    Stammdaten = stammdatens,
                    Inspektionsdaten = iat,
                });
            }
            
        }
        private Tuple<List<AbwassertechnischeAnlage>,List<InspizierteAbwassertechnischeAnlage>> LoadRawFile() {
            List<AbwassertechnischeAnlage> Stammdaten = new List<AbwassertechnischeAnlage>();
            List<InspizierteAbwassertechnischeAnlage> Inspektionsdaten = new List<InspizierteAbwassertechnischeAnlage>();

            if(!File.Exists(XmlFile)) throw new FileNotFoundException(string.Format("XML Datei mit den Pfad {0} wurde nicht gefunden",XmlFile));
            XmlDocument doc = new XmlDocument();
            doc.Load(XmlFile);
            rootChild = doc.LastChild;

            manager = new XmlNamespaceManager(doc.NameTable);
            manager.AddNamespace("e", "http://www.ofd-hannover.la/Identifikation");
            rootChild = rootChild.SelectSingleNode("//e:Datenkollektive", manager);

            Stammdaten = LoadStammdatenCollectiv();
            Inspektionsdaten = LoadInspektionsdatenCollectiv();
            return new Tuple<List<AbwassertechnischeAnlage>, List<InspizierteAbwassertechnischeAnlage>>(Stammdaten,Inspektionsdaten);
        }

        private List<InspizierteAbwassertechnischeAnlage> LoadInspektionsdatenCollectiv()
        {
            List<InspizierteAbwassertechnischeAnlage> result = new List<InspizierteAbwassertechnischeAnlage>();
            XmlNode zstdk = rootChild.SelectSingleNode("//e:Zustandsdatenkollektiv", manager);
            XmlNodeList Inspektionsdaten = zstdk.SelectNodes("//e:InspizierteAbwassertechnischeAnlage", manager);

            foreach(XmlNode node in Inspektionsdaten) {
                result.Add(ParseInspektion(node));
            }

            return result;
        }

        private static InspizierteAbwassertechnischeAnlage ParseInspektion(XmlNode node) 
        {
            InspizierteAbwassertechnischeAnlage result = new InspizierteAbwassertechnischeAnlage();
            foreach (XmlNode d in node.ChildNodes)
            {
                switch (d.Name)
                {
                    case "Objektbezeichnung": result.Objektbezeichnung = d.InnerText; break;
                    case "Anlagentyp": result.Anlagentyp = (EAnlagetyp)Convert.ToInt32(d.InnerText); break;
                    case "Lage":
                        if (!d.HasChildNodes) break;
                        string strassename ="";
                        string ortname = "";
                        foreach (XmlNode _temp in d.ChildNodes)
                        {
                            switch (_temp.Name)
                            {
                                case "Strassenname": strassename = _temp.InnerText; break;
                                case "Ortsteilname": ortname = _temp.InnerText; break;
                                //case "LageOberflaeche": break;
                                case "Strassenschluessel": break;
                                //case "Ortsteilschluessel": break;
                                default: throw new NotImplementedException(_temp.Name);
                            }
                        }
                        result.Lage = new Lage(strassename, ortname);
                        break;
                    case "OptischeInspektion": result.OptischeInspektion = parseOptischeInspektion(d); break;
                }
            }
            return result;
        }

        private static OptischeInspektion parseOptischeInspektion(XmlNode d)
        {
            OptischeInspektion result = new OptischeInspektion();
            string datum = "";
            string time = "";
            foreach(XmlNode node in d.ChildNodes)
            {
                switch(node.Name)
                {
                    case "Auftragskennung": result.Auftragskennung = Convert.ToInt32(node.InnerText);
                     break;
                    case "Inspektionsdatum": datum = node.InnerText; break;
                    case "Inspektionsverfahren": result.Inspektionsverfahren = (EInspektionverfahren)Convert.ToInt32(node.InnerText); break;
                    case "NameUntersucher": result.NameUntersucher = node.InnerText; break;
                    case "Uhrzeit": time = node.InnerText; break;
                    case "Wetter": result.Wetter = (EWetter)Convert.ToInt32(node.InnerText); break;
                    case "Reinigung": result.Reinigung = node.InnerText.Equals("1") ? true: false; break;
                    case "Wasserhaltung": result.Wasserhaltung = (EWasserhaltung)Convert.ToInt32(node.InnerText);  break;
                    case "Rohrleitung": result.Rohrleitung = parseRohrleitung(node); break;
                    case "Videoablagereferenz": break;
                    case "Temperatur": break;
                    case "VideoSpeichermedium": break;
                    case "Knoten": break;
                    default: throw new NotImplementedException(node.Name);
                }
            }
            result.Inspektionstime = makeInspektionstime(datum, time);
            
            return result;
        }

        private static Rohrleitung parseRohrleitung(XmlNode node)
        {
            Rohrleitung result = new Rohrleitung();
            foreach (XmlNode d in node)
            {
                switch (d.Name)
                {
                    case "Rohrleitungstyp":
                        result.Rohrleitungstyp = (ERohrleitungstyp)Convert.ToInt32(d.InnerText);
                        break;
                    case "Inspektionslaenge":
                        result.Inspektionslaenge = Convert.ToDecimal(d.InnerText.Replace('.', ','));
                        break;
                    case "Inspektionsrichtung":
                        result.Inspektionsrichtung = d.InnerText;
                        break;
                    case "RGrunddaten":
                        result.Grunddaten = ParseGrundDaten(d);
                        break;
                    case "Inspektionsdaten":
                        result.Zustaende = ParseRZustand(d);
                        break;
                }
            }
            return result;
        }

        private static List<RZustand> ParseRZustand(XmlNode node)
        {
            List<RZustand> result = new List<RZustand>();

            foreach(XmlNode child in node.ChildNodes)
            {
                RZustand rZustand = new RZustand();
                foreach(XmlNode d in child.ChildNodes)
                {
                    
                    switch(d.Name)
                    {
                        case "Station":
                            rZustand.Station = Convert.ToDecimal(d.InnerText.Replace('.', ','));
                            break;
                        case "InspektionsKode":
                            rZustand.Inspektionskode = d.InnerText;
                            break;
                        case "Charakterisierung1":
                            rZustand.Charakterisierung1 = d.InnerText;
                            break;
                        case "Charakterisierung2":
                            rZustand.Charakterisierung2 = d.InnerText;
                            break;
                        case "Verbindung":
                            rZustand.Verbindung = d.InnerText.Equals("0") ? false : true;
                            break;
                        case "PositionVon":
                            rZustand.PositionVon = Convert.ToInt32(d.InnerText);
                            break;
                        case "PositionBis":
                            rZustand.PositionBis = Convert.ToInt32(d.InnerText);
                            break;
                        case "Quantifizierung1Numerisch":
                            Quantifizierung quantifizierung1 = new Quantifizierung();
                            quantifizierung1.Numerisch = Convert.ToDecimal(d.InnerText.Replace('.', ','));
                            rZustand.Quantifizierung1 = quantifizierung1;
                            break;
                        case "Quantifizierung2Numerisch":
                            Quantifizierung quantifizierung2 = new Quantifizierung();
                            quantifizierung2.Numerisch = Convert.ToDecimal(d.InnerText.Replace('.', ','));
                            rZustand.Quantifizierung1 = quantifizierung2;
                            break;
                        case "Quantifizierung1Text":
                            Quantifizierung quantifizierung3 = new Quantifizierung();
                            quantifizierung3.Text = d.InnerText;
                            rZustand.Quantifizierung1 = quantifizierung3;
                            break;
                        case "Quantifizierung2Text":
                            Quantifizierung quantifizierung4 = new Quantifizierung();
                            quantifizierung4.Text = d.InnerText;
                            rZustand.Quantifizierung2 = quantifizierung4;
                            break;
                        case "Frame": break;
                        case "Klassifizierung":break;
                        case "Kommentar": rZustand.Kommentar = d.InnerText; break;
                        case "Streckenschaden": rZustand.Streckenschaden = d.InnerText; break;
                        case "StreckenschadenLfdNr": rZustand.Streckenschadennr = Convert.ToInt32(d.InnerText); break;
                        case "BezeichnungSanierung": break;
                        case "RVerfahrenSanierung":break;
                        case "Fotodatei":break;
                        case "FotoSpeichermedium":break;
                        case "Fotonummer":break;
                        case "Timecode":break;
                        case "GrundAbbruch":break;
                        default: throw new NotImplementedException(d.Name);

                    }
                }
                result.Add(rZustand);
            }

            
            return result;
        }

        private static RGrunddaten ParseGrundDaten(XmlNode intRGrunddaten)
        {
            RGrunddaten grunddaten = new RGrunddaten();
            foreach(XmlNode d in intRGrunddaten.ChildNodes)
            {
                switch(d.Name)
                {
                    case "KnotenZulauf": grunddaten.KnotenZulauf = d.InnerText; break;
                    case "KnotenZulaufTyp": grunddaten.KnotenZulaufTyp = (EKnotenTyp)Convert.ToInt32(d.InnerText); break;
                    case "KnotenAblauf": grunddaten.KnotenAblauf = d.InnerText; break;
                    case "KnotenAblaufTyp": grunddaten.KnotenAblaufTyp = (EKnotenTyp)Convert.ToInt32(d.InnerText); break;
                    case "HerkunftProfilmasse": grunddaten.HerkunftProfilmasse = Convert.ToInt32(d.InnerText); break;
                    case "Profilhoehe": grunddaten.Profilhoehe = Convert.ToInt32(d.InnerText); break;
                    case "Profilbreite": grunddaten.Profilbreite = Convert.ToInt32(d.InnerText); break;
                    case "Profilart": grunddaten.Profilart = Convert.ToInt32(d.InnerText); break;
                    case "HerkunftMaterial": grunddaten.HerkunftMaterial = Convert.ToInt32(d.InnerText); break;
                    case "Material": grunddaten.Material = d.InnerText; break;
                    case "Regeleinzelrohrlaenge": grunddaten.Regeleinzelrohrlaenge = Convert.ToDecimal(d.InnerText.Replace('.', ',')); break;
                    case "ArtAuskleidung": grunddaten.ArtAuskleidung = Convert.ToInt32(d.InnerText); break;
                    case "Kanalart": grunddaten.Kanalart = d.InnerText; break;
                    case "Anschlussdaten": grunddaten.Anschlussddaten = ParseAnschlussdaten(d); break;
                    case "Innenschutz": grunddaten.Innenschutz = d.InnerText; break;
                    default: throw new NotImplementedException(d.Name);
                }
            }
            return grunddaten;
        }
        private static DateTime makeInspektionstime(string datum, string time)
        {
            if(datum.Equals("") && time.Equals("")) return DateTime.Now;
            string[] parseddatum = datum.Split('-');
            int year = Convert.ToInt32(parseddatum[0]);
            int month = Convert.ToInt32(parseddatum[1]);
            int day = Convert.ToInt32(parseddatum[2]);

            string[] parsedtime = time.Split(':');
            int hour = Convert.ToInt32(parsedtime[0]);
            int minute = Convert.ToInt32(parsedtime[1]);
            int second = Convert.ToInt32(parsedtime[2]);
            return new DateTime(year, month, day, hour, minute, second);
        }
        private static Anschlussdaten ParseAnschlussdaten(XmlNode anschlussdaten)
        {
            Anschlussdaten result = new Anschlussdaten();
            foreach(XmlNode d in anschlussdaten.ChildNodes)
            {
                switch(d.Name)
                {
                    case "Objektbezeichnung": result.Objektbezeichnung = d.InnerText; break;
                    case "Kantentyp": result.Kantentyp = (EKantenTyp)Convert.ToInt32(d.InnerText);break;
                    case "Entfernung": result.Entfernung = Convert.ToDecimal(d.InnerText.Replace('.', ',')); break;
                    case "Fixierung": result.Fixierung = d.InnerText; break;
                    
                }
            }
            return result;
        }

        private List<AbwassertechnischeAnlage> LoadStammdatenCollectiv()
        {
            List<AbwassertechnischeAnlage> result = new List<AbwassertechnischeAnlage>();
            XmlNode zstdk = rootChild.SelectSingleNode("//e:Stammdatenkollektiv", manager);
            XmlNodeList Stammdaten = zstdk.SelectNodes("//e:AbwassertechnischeAnlage", manager);

            foreach(XmlNode node in Stammdaten)
            {
                result.Add(ParseStammdaten(node));
            }
            return result;
        }

        private AbwassertechnischeAnlage ParseStammdaten(XmlNode node)
        {
            AbwassertechnischeAnlage result = new AbwassertechnischeAnlage();
            foreach(XmlNode aktuell in node.ChildNodes)
            {
                switch(aktuell.Name)
                {
                    case "Objektbezeichnung":result.Objektbezeichnung = aktuell.InnerText;break;
                    case "Objektart":result.ObjektArt = (EObjektArt)Convert.ToInt32(aktuell.InnerText);break;
                    case "Entwaesserungsart":result.Entwaesserungsart = aktuell.InnerText;break;
                    case "Knoten":result.Knoten = parseStammdatenKnoten(aktuell); break;
                    case "Kante":
                        result.Kante = parseKante(aktuell);
                        QueueToRebuild.Enqueue(result);
                    break;
                    case "Lage":
                        break;
                    case "AlteObjektbezeichnung":break;
                    case "Status":break;
                    case "Baujahr":break;
                    case "Umweltparameter":break;
                    case "Kommentar": break;
                    case "Geometrie": result.Geometrie = parseGeometrie(aktuell); break;
                    default: throw new NotImplementedException(aktuell.Name);
                }
            }

            return result;
        }

        private Geometrie parseGeometrie(XmlNode aktuell)
        {

            return new Geometrie();
            //throw new NotImplementedException();
        }

        private Kante parseKante(XmlNode aktuell)
        {
            /*
            KantenTyp
            KnotenZulauf
            KnotenZulaufTyp
            KnotenAblauf
            KnotenAblaufTyp
            SohlhoeheZulauf
            SohlhoeheAblauf
            Laenge
            Material
            Profil
            Haltung 
            */
            Kante result = new Kante();

            foreach(XmlNode s in aktuell.ChildNodes)
            {
                switch(s.Name)
                {
                    case "KnotenZulauf": result.knotenZulaufTemp = s.InnerText; break;
                    case "KnotenAblauf": result.knotenAblaufTemp = s.InnerText; break;
                    case "Profil": result.Profil = getProfil(s);break; 

                }
            }
            return result;
        }

        private Profil getProfil(XmlNode s)
        {
            int Profilart =0, Profilbreite=0, Profilhoehe =0;
            foreach(XmlNode d in s.ChildNodes)
            {
                switch(d.Name) 
                {
                    case "SonderprofilVorhanden": break;
                    case "Profilart": Profilart = Convert.ToInt32(s.InnerText); break;
                    case "Profilbreite": Profilbreite = Convert.ToInt32(s.InnerText); break;
                    case "Profilhoehe": Profilhoehe = Convert.ToInt32(s.InnerText); break;
                    default: throw new NotImplementedException(d.Name);
                }
            }
            return new Profil(Profilart,Profilbreite,Profilhoehe);
        }

        private static Knoten parseStammdatenKnoten(XmlNode aktuell)
        {
            Knoten result = new Knoten();
            foreach(XmlNode s in aktuell.ChildNodes)
            {
                switch(s.Name)
                {
                    case "KnotenTyp": result.KnotenTyp = (EKnotenTyp)Convert.ToInt32(s.InnerText);break;
                    case "Anschlusspunkt": result.Anschlusspunkt = new Anschlusspunkt() { Punktkennung = s.InnerText }; break;
                    case "Schacht": result.Schacht = parseSchacht(s); break;
                }
            }
            
            return result;
        }

        private static Schacht parseSchacht(XmlNode s)
        {
            Schacht result = new Schacht();
            foreach(XmlNode n in s.ChildNodes) {
                switch(n.Name) {
                    case "SchachtFunktion": result.SchachtFunktion = (ESchachtFunktion)Convert.ToInt32(n.InnerText); break;
                    case "Schachttiefe": result.SchachtTiefe = Convert.ToDecimal(n.InnerText); break;
                }
            }
            return result;
        }
    }
}
