//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.Net;
using System.IO;
using System.Xml;
using System.Collections;
using CommunityServer.Components;
using CommunityServer.Configuration;
using Rss;

namespace CommunityServer.Reader.Components
{

	/// <summary>
	/// Summary description for FeedUpdater.
	/// </summary>
	public class FeedUpdater : IJob
	{


		#region Constructor(s)


		public FeedUpdater()
		{
			
		}


		#endregion


		#region Public Methods


		public void UpdateAllFeeds(ArrayList feeds)
		{
			// Now, go through each, grab the RSS feed and update the database.
			for (int i = 0; i < feeds.Count; i++)
			{
				Feed feed = (Feed) feeds[i];

				this.UpdateFeed(feed);

                
                EventLogs.Debug("Feed Updated " + feed.Title, "Feed Reader", 702, CSContext.Current.SettingsID);
                


			}			
		}


		public void UpdateFeed(Feed feed)
		{
			RssFeed rssFeed = null;
            FeedState state = FeedState.UpdateSuccessful;


			try
			{
                
				rssFeed = RssFeed.Read(feed.CreateRequest());	

				// RSS.NET will return cached if it's unable to load the current feed (any WebException). We
				// want to show that as cache in order to show the feed state in the reader UI.
				if (rssFeed.Cached) 
                    state = FeedState.FeedCached;

			}
			catch (WebException wexc)
			{
				if (wexc.Message.IndexOf("(404)") > 0)
					state = FeedState.FeedNotFound;
                else if (wexc.Message.IndexOf("(304)") > 0)
                {
                    state = FeedState.FeedCached;
                }
				else
                {
                	state = FeedState.UnknownError;
                }
			}
			catch (XmlException)
			{
				state = FeedState.InvalidFormat;
			}
			catch (Exception)
			{
				state = FeedState.UnknownError;
			}

			// If the feed state is anything bad, let's update the feed state only, and continue.
			// Otherwise, attempt to update the feed.
			if (state != FeedState.UpdateSuccessful)
			{
				FeedReaderDataProvider.Instance().UpdateFeedStatus(feed.FeedId, state);
			}
			else
			{
			    ProcessFeedItems(rssFeed, feed);
			}			
		}

	    public void ProcessFeedItems(RssFeed rssFeed, Feed feed)
	    {
	        // If the current feed has no channels, continue on to the next.
	        if (rssFeed.Channels == null || rssFeed.Channels.Count < 1) return;
    
	        // Grab the first channel (most RSS feeds never implement multiple channels)
	        RssChannel channel = rssFeed.Channels[0];
    
	        // Update the feed.
	        FeedReaderDataProvider.Instance().UpdateFeed(feed.FeedId, feed.Url, channel.Title, channel.Link.ToString(), channel.Language, 
	                                                     channel.Generator, feed.SubscribeDate, DateTime.Now, rssFeed.LastModified,rssFeed.ETag, (int)FeedState.UpdateSuccessful);	
    
	        try
	        {
	            // Now, let's update the feeds.
	            MemoryStream stream = new MemoryStream();
	            XmlTextWriter xmlWriter = new XmlTextWriter(stream, null);

	            xmlWriter.WriteStartDocument(true);

	            xmlWriter.WriteStartElement("feeds");
					
	            foreach (RssItem item in channel.Items)
	            {
	                xmlWriter.WriteStartElement("feed");

	                xmlWriter.WriteElementString("FeedId", feed.FeedId.ToString());
	                xmlWriter.WriteElementString("Author", HtmlScrubber.Clean(item.Author,false,true));
	                xmlWriter.WriteElementString("Title", HtmlScrubber.Clean(item.Title,false,true));
	                xmlWriter.WriteElementString("Description", HtmlScrubber.Clean(item.Description,false,true));
	                xmlWriter.WriteElementString("Source", HtmlScrubber.Clean(Convert.ToString(item.Source),false,true));

	                if (item.Guid != null)
	                {
	                    xmlWriter.WriteElementString("GuidName", item.Guid.Name);
	                }
	                else
	                {
	                    // Some feeds do not publish a GuidName property. In this case, let's try to use other options.
	
	                    // First, let's try Link.
	                    if (item.Link != null)
	                    {
	                        xmlWriter.WriteElementString("GuidName", item.Link.ToString());
	                    }
	                }

	                string guidIsPermaLink;
							
	                if (item.Guid == null || item.Guid.PermaLink.ToString() == "DBBool.Null")
	                {
	                    guidIsPermaLink = "0";
	                }
	                else
	                {
	                    guidIsPermaLink = (Convert.ToBoolean(item.Guid.PermaLink.ToString()) ? "1" : "0");	
	                }

	                xmlWriter.WriteElementString("GuidIsPermaLink", guidIsPermaLink);							
	                xmlWriter.WriteElementString("Link", item.Link.ToString());

	                if (item.PubDate == RssDefault.DateTime)
	                {
	                    // tdenham : FIX #2770. Forcing ISO date format to ensure the date can be parsed by any locale of SQL Server
	                    xmlWriter.WriteElementString("PubDate", DateTime.Now.ToString("s"));
	                }
	                else
	                {
	                    // tdenham : FIX #2770. Forcing ISO date format to ensure the date can be parsed by any locale of SQL Server
	                    xmlWriter.WriteElementString("PubDate", item.PubDate.ToString("s"));	
	                }
						

	                xmlWriter.WriteElementString("CommentsUrl", item.Comments);

	                if (item.Enclosure != null)
	                {
	                    xmlWriter.WriteElementString("EnclosureUrl", Convert.ToString(item.Enclosure.Url));
	                    xmlWriter.WriteElementString("EnclosureLength", Convert.ToString(item.Enclosure.Length));
	                    xmlWriter.WriteElementString("EnclosureType", item.Enclosure.Type);
	                }

	                xmlWriter.WriteElementString("Creator", item.Creator);
	                xmlWriter.WriteElementString("CommentApiUrl", item.CommentApiUrl);
	                xmlWriter.WriteElementString("CommentRssUrl", item.CommentRss);
	                xmlWriter.WriteElementString("CommentCount", item.CommentCount.ToString());

	                xmlWriter.WriteEndElement();
	            }
	            xmlWriter.WriteEndElement();
	            xmlWriter.WriteEndDocument();

	            xmlWriter.Flush();

	            stream.Seek(0, SeekOrigin.Begin);
	            StreamReader streamReader = new StreamReader(stream);
	            string itemText = streamReader.ReadToEnd();

	            FeedReaderDataProvider.Instance().UpdateFeedPosts(feed.FeedId, itemText);

	            streamReader.Close();
	            xmlWriter.Close();
						
	        }
	        catch (XmlException xml)
	        {
	            EventLogs.Warn(string.Format("Feed {0} ({2} - {3}) could not get updated {1}", feed.Title, xml.Message, feed.FeedId,feed.Url), "Feed Reader", 701, CSContext.Current.SettingsID);
	            FeedReaderDataProvider.Instance().UpdateFeedStatus(feed.FeedId, FeedState.InvalidFormat);
	        }
	        catch (Exception ex)
	        {
	            EventLogs.Warn(string.Format("Feed {0} ({2} - {3}) could not get updated {1}", feed.Title, ex.Message, feed.FeedId,feed.Url), "Feed Reader", 701, CSContext.Current.SettingsID);
	            FeedReaderDataProvider.Instance().UpdateFeedStatus(feed.FeedId, FeedState.UnknownError);
	        }
	    }

	    #endregion


		#region IJob Members

        public void Execute(XmlNode node)
        {
            SiteSettingsManager.IterateSiteSettings(new SiteSettingsListIterator(ExecuteReaderUpdateBySettings));

        }
		private void ExecuteReaderUpdateBySettings(int settingsID)
		{
			CSContext.Create(settingsID);

            //TODO:
            //This needs to poll for feeds by SettingsID. More than one ReaderConfiguration can exist in a single
            //AppDomain so it is not safe to user the last one on the thread.

            // First, let's get all the feeds defined in the system.
            ArrayList feeds = FeedReaderDataProvider.Instance().GetAllFeeds(ReaderConfiguration.Instance().LastModifiedInterval);

            // Call UpdateAllFeeds()
            this.UpdateAllFeeds(feeds);
		}

		#endregion


	}
}