//http://stackoverflow.com/questions/210375/problems-reading-rss-with-c-and-net-3-5
//https://connect.microsoft.com/VisualStudio/feedback/details/325421/syndicationfeed-load-fails-to-parse-datetime-against-a-real-world-feeds-ie7-can-read

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Syndication;
using System.Threading;
using System.Xml;
using NLog;

namespace NzbDrone.Core.Providers.Indexer
{
    public class SyndicationFeedXmlReader : XmlTextReader
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        private static readonly string[] rss20DateTimeHints = { "pubDate" };
        private static readonly string[] atom10DateTimeHints = { "updated", "published", "lastBuildDate" };
        private bool _isRss2DateTime;
        private bool _isAtomDateTime;

        private static readonly MethodInfo rss20FeedFormatterMethodInfo = typeof(Rss20FeedFormatter).GetMethod("DateFromString", BindingFlags.NonPublic | BindingFlags.Static);
        private static readonly MethodInfo atom10FeedFormatterMethodInfo = typeof(Atom10FeedFormatter).GetMethod("DateFromString", BindingFlags.NonPublic | BindingFlags.Instance);

        public SyndicationFeedXmlReader(Stream stream) : base(stream) { }

        public override bool IsStartElement(string localname, string ns)
        {
            _isRss2DateTime = rss20DateTimeHints.Contains(localname);
            _isAtomDateTime = atom10DateTimeHints.Contains(localname);

            CheckForError();       

            return base.IsStartElement(localname, ns);
        }

        public override string ReadString()
        {
            var dateVal = base.ReadString();

            try
            {
                if (_isRss2DateTime)
                {
                    rss20FeedFormatterMethodInfo.Invoke(null, new object[] { dateVal, this });
                }
                if (_isAtomDateTime)
                {
                    atom10FeedFormatterMethodInfo.Invoke(new Atom10FeedFormatter(), new object[] { dateVal, this });
                }
            }
            catch (TargetInvocationException e)
            {
                DateTime parsedDate;

                if (!DateTime.TryParse(dateVal, new CultureInfo("en-US"), DateTimeStyles.None, out parsedDate))
                {
                    parsedDate = DateTime.UtcNow;
                    logger.WarnException("Unable to parse Feed date " + dateVal, e);
                }

                var currentCulture = Thread.CurrentThread.CurrentCulture;
                Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
                dateVal = parsedDate.ToString("ddd, dd MMM yyyy HH:mm:ss zzz");
                dateVal = dateVal.Remove(dateVal.LastIndexOf(':'), 1);
                Thread.CurrentThread.CurrentCulture = currentCulture;
            }

            return dateVal;
        }

        internal void CheckForError()
        {
            if (this.MoveToContent() == XmlNodeType.Element)
            {
               if (this.Name != "error")
                    return;

                var message = "Error: ";

                if (this.HasAttributes)
                {
                    while (this.MoveToNextAttribute())
                    {
                        message += String.Format(" [{0}:{1}]", this.Name, this.Value);
                    }
                }

                logger.Error("Error in RSS feed: {0}", message);
                throw new Exception(message);
            }
            
        }
    }
}