using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using RothenburgAR.Common;
using UnityEngine;


namespace RothenburgAR.Exhibition
{
    public class ExhibitionXmlPreloader : IExhibitionPreloader
    {
        public bool CanLoadExhibitionDirectory(string exhibitionDirectory)
        {
            if (!Directory.Exists(exhibitionDirectory))
                return false;

            string xmlFilePath = PathHelper.GetXmlPathFromDirectoryPath(exhibitionDirectory);

            if (!File.Exists(xmlFilePath))
                return false;
            if (!File.Exists(Path.Combine(exhibitionDirectory, "tracker.xml")))
                return false;
            if (!File.Exists(Path.Combine(exhibitionDirectory, "tracker.dat")))
                return false;

            // Todo: More validations
            return true;
        }

        public PreloadedExhibition PreloadExhibition(string exhibitionDirectory)
        {
            string xmlFilePath = PathHelper.GetXmlPathFromDirectoryPath(exhibitionDirectory);

            if (!File.Exists(xmlFilePath))
                throw new FileNotFoundException(xmlFilePath);

            XmlDocument doc = new XmlDocument();
            doc.Load(xmlFilePath);

            // Read the ID from the xml
            if (doc.DocumentElement == null || doc.DocumentElement.Attributes == null ||
                doc.DocumentElement.Attributes["id"] == null)
                throw new InvalidXMLException("Could not read doc.DocumentElement.Attributes['id] ");

            string exhibitionId = doc.DocumentElement.Attributes["id"].Value;

            PreloadedExhibition resultExhibition = new PreloadedExhibition
            {
                ContainedExhibits = new List<PreloadedExhibit>(),
                ID = exhibitionId,
                DatasetPath = exhibitionDirectory
            };

            var exhibitNodes = doc.SelectNodes("//exhibition/exhibit");
            foreach (XmlNode exhibitNode in exhibitNodes)
            {
                if (exhibitNode.Attributes == null || exhibitNode.Attributes["id"] == null)
                {
                    // Todo: Log error
                    continue;
                }

                var newExhibit = PreloadExhibit(exhibitionDirectory, exhibitNode);
                resultExhibition.ContainedExhibits.Add(newExhibit);
            }
            return resultExhibition;
        }

        private PreloadedExhibit PreloadExhibit(string exhibitionDirectory, XmlNode exhibitNode)
        {
            PreloadedExhibit newExhibit = new PreloadedExhibit();
            newExhibit.ID = exhibitNode.Attributes["id"].Value;
            newExhibit.ReferencedPoiEntries = new List<PreloadedPoiReference>();

            // Load Description Text
            XmlNodeList descrList = exhibitNode.SelectNodes("description/text");
            if (descrList != null && descrList.Count > 0)
            {
                TextElement exhibitDescr = TextElement.BuildFromXmlNode(exhibitionDirectory, descrList);
                newExhibit.Description = exhibitDescr;
            }

            // Load Title Text
            XmlNodeList titleList = exhibitNode.SelectNodes("title/text");
            if (titleList != null && titleList.Count > 0)
            {
                TextElement exhibitTitle = TextElement.BuildFromXmlNode(exhibitionDirectory, titleList);
                var fontSize = GetFloatFromXmlNode(exhibitNode.SelectSingleNode("title/font"), "size");
                var boxHeight = GetFloatFromXmlNode(exhibitNode.SelectSingleNode("title/dimensions"), "height");
                var boxWidth = GetFloatFromXmlNode(exhibitNode.SelectSingleNode("title/dimensions"), "width");

                PreloadedExhibitTitle preTitle = new PreloadedExhibitTitle
                {
                    Text = exhibitTitle,
                    Position = GetVector3FromXmlNode(exhibitNode.SelectSingleNode("title/position")),
                    Rotation = GetVector3FromXmlNode(exhibitNode.SelectSingleNode("title/rotation")),
                    FontSize = fontSize ?? 20,
                    BoxWidth = boxWidth ?? 200,
                    BoxHeight = boxHeight ?? 25
                };
                newExhibit.Title = preTitle;
            }


            // Load POI References
            XmlNodeList poiRefList = exhibitNode.SelectNodes("poiList/poi");
            if (poiRefList != null && poiRefList.Count > 0)
            {
                foreach (XmlNode poiRefEntry in poiRefList)
                {
                    if (poiRefEntry.Attributes == null || poiRefEntry.Attributes["ref-id"] == null)
                        continue;

                    PreloadedPoiReference poiReference =
                        new PreloadedPoiReference
                        {
                            ReferencedId = poiRefEntry.Attributes["ref-id"].Value,
                            Position = GetVector3FromXmlNode(poiRefEntry.SelectSingleNode("position")),
                            Rotation = GetVector3FromXmlNode(poiRefEntry.SelectSingleNode("rotation")),
                            Scale = GetVector3FromXmlNode(poiRefEntry.SelectSingleNode("scale"))
                        };

                    newExhibit.ReferencedPoiEntries.Add(poiReference);
                }
            }
            return newExhibit;
        }

        private string GetStringFromXmlNode(XmlNode node, string key)
        {
            if (node == null)
                return null;
            if (node.Attributes == null)
                return null;
            if (node.Attributes[key] == null)
                return null;

            return node.Attributes[key].Value;
        }

        private float? GetFloatFromXmlNode(XmlNode node, string key)
        {
            var str = GetStringFromXmlNode(node, key);
            if (str == null)
                return null;
            try
            {
                return float.Parse(str, CultureInfo.InvariantCulture.NumberFormat);
            }
            catch
            {
                return null;
            }
        }

        private Vector3? GetVector3FromXmlNode(XmlNode node)
        {
            if (node == null)
                return null;
            if (node.Attributes == null)
                return null;
            if (node.Attributes["x"] == null)
                return null;
            if (node.Attributes["y"] == null)
                return null;
            if (node.Attributes["z"] == null)
                return null;

            try
            {
                float posX = float.Parse(node.Attributes["x"].Value,
                    CultureInfo.InvariantCulture.NumberFormat);
                float posY = float.Parse(node.Attributes["y"].Value,
                    CultureInfo.InvariantCulture.NumberFormat);
                float posZ = float.Parse(node.Attributes["z"].Value,
                    CultureInfo.InvariantCulture.NumberFormat);

                return new Vector3(posX, posY, posZ);
            }
            catch
            {
                // TODO: log error
                return null;
            }
        }
    }
}