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

using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.Mail;
using CommunityServer.Configuration;

namespace CommunityServer.Components {

	/// <summary>
	/// Used to iterate over a of possible email subscribers and optionally send them a message
	/// </summary>
	public delegate MailMessage EnquePostEmails(Post p, User subscriber, FormatList formats);

    // *********************************************************************
    //  Emails
    //
    /// <summary>
    /// This class is responsible for sending out emails to users when certain events occur.  For example,
    /// when a user requests to be emailed their password, a method of this class is called to send the
    /// correct email template populated with the correct data to the correct user.
    /// </summary>
    /// <remarks>There are a number of email templates.  These templates can be viewed/edited via the Email
    /// Administration Web page.  The EmailType enumeration contains a member for each of the potential
    /// email templates.</remarks>
    /// 
    // ********************************************************************/
    public class Emails {

        #region Contact Request

        public static void QueueContactRequest(string to, string from, string subject, string body, string appName, string link, string submittedName)
        {
            FormatList fl = new FormatList();
            fl.Add("AppTitle", appName);
            fl.Add("Body", body);
            fl.Add("link", link);
            fl.Add("useremail", from);
            fl.Add("Subject", subject);
			fl.Add("Name", submittedName);

            MailMessage email = GenericEmail("ContactForm",null,null,null,false,false);

			if(email == null)
				return;

            email.To = to;
            email.From = from;
            email.Body = fl.Format(email.Body);
            email.Subject = fl.Format(email.Subject);

			EmailQueueProvider.Instance().QueueEmail(email);
        }

        #endregion

		#region SectionTracking
		/// <summary>
		/// Based on the postID, this method will retrieve subscribers to the post and section.
		/// It will then create a unique list between them and call the method supplied vai the 
		/// EnquePostEmails delegate
		/// </summary>
		/// <param name="post">Post to send</param>
		/// <param name="epm">Method used to Enque the email</param>
		protected static void SectionTracking(Post post, EnquePostEmails epm, FormatList formats)
		{
			if (post == null)
				return;

			Hashtable threadSubscribers = GetEmailsTrackingThread(post.PostID);
			Hashtable sectionSubscribers = GetEmailsTrackingSectionByPostID(post.PostID);

            

			foreach(string email in sectionSubscribers.Keys)
			{
				User threadUser = threadSubscribers[email] as User;
				if(threadUser != null)
					threadSubscribers.Remove(email);
			}

			foreach(string email in threadSubscribers.Keys)
			{
				sectionSubscribers.Add(email,threadSubscribers[email] as User);
			}
			threadSubscribers.Clear();

            User currentUser = CSContext.Current.User;

			foreach (string email in sectionSubscribers.Keys) 
			{
				User subscriber = sectionSubscribers[email] as User;
				// Make sure we don't send an email to the user that posted the message
				//			
				if (subscriber.Email != currentUser.Email) 
				{
					EnqueuEmail(epm(post,subscriber,formats));
				}
			}

		}
		#endregion

		#region EmailsInQueue
		// *********************************************************************
		//  EmailsInQueue
		//
		/// <summary>
		/// This method returns an ArrayList of all emails in the
		/// database, ready to send.
		/// </summary>
		/// 
		// ********************************************************************/
		public static ArrayList EmailsInQueue (int settingsID) 
		{
			return EmailQueueProvider.Instance().DequeueEmail(settingsID);
		}
		#endregion

        #region Enque email
		protected static void EnqueuEmail (MailMessage email) 
		{
			// don't enqueue the email if the user has a blank
			// email address.
			//
			if(email != null && email.To != null && email.To.Trim().Length > 0)
				EmailQueueProvider.Instance().QueueEmail(email);
        }
        #endregion

        #region SendQueuedEmails
        // *********************************************************************
        //  SendQueuedEmails
        //
        /// <summary>
        ///  The SendQueuedEmails method sends all queued emails for a specified Community Server site. 
        /// </summary>
        /// <param name="failureInterval">
        ///  If a specific email fails to send, the interval to wait before re-sending. An integer value. 
        ///  The actual interval calculation is done by the cs_EmailQueue_Failure stored procedure and the default 
        ///  is to multiply this interval value time the current number of attempts that have been made to 
        ///  send this specific email (assuming the number of attempts is less than the minimum) and adding that 
        ///  value in minutes to the NextTryTime value for the email. 
        /// </param>
        /// <param name="maxNumberOfTries">
        ///   The number of times the system should attempt to send the queue email before deleting it 
        ///   as undeliverable.
        /// </param>
        /// <param name="site">This site for which queued emails should be sent.</param>
        /// 
        // ********************************************************************/
        public static void SendQueuedEmails(int failureInterval, int maxNumberOfTries, SiteSettings site) 
        {

            CommonDataProvider dp = CommonDataProvider.Instance();
        	CSConfiguration csConfig = CSConfiguration.GetConfig();

			// Test to see if this server or the site has email disabled.  If either the server or the site
            // has disabled email, or if the site specified is null, exit the method without attempting 
            // further work.
			//
			if (csConfig.IsEmailDisabled  || site == null || !site.EnableEmail)
				return;

		    ArrayList emails = EmailQueueProvider.Instance().DequeueEmail(site.SettingsID);
			ArrayList failure = new ArrayList();

            SmtpMail.SmtpServer = site.SmtpServer;
            string username = site.SmtpServerUserName;
            string password = site.SmtpServerPassword;
			string portnumber = site.SmtpPortNumber;

            int sentCount	= 0;
			int totalSent	= 0;
            short connectionLimit = csConfig.SmtpServerConnectionLimit;
            foreach (EmailTemplate m in emails) 
            {
				try 
                {
					//for SMTP Authentication
					if (site.SmtpServerUsingNtlm)
					{
						m.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "2" ); //ntlm authentication
					}
					else if (site.SmtpServerRequiredLogin)
                    {
                        m.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1" ); //basic authentication
                        m.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", username ); //set your username here
                        m.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", password ); //set your password here
                    }

					// Add Port Number if one was configured
					if (!Globals.IsNullorEmpty(portnumber))
						m.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpserverport", portnumber ); //set your port number here
					
					// For Html emails, this resolves the complete paths for images and links.
                    // Should not effect non-Html emails via RFC-2110.
                    //
                    // EAD 1/14/2005: Commented out until we can access the forumContext from a background thread.
                    //m.Fields.Add("Content-Base", Globals.FullPath(Globals.GetSiteUrls().Home) );
                    //m.Fields.Add("Content-Location", Globals.FullPath(Globals.GetSiteUrls().Home) );
					m.Headers.Add("X-CS-EmailID", m.EmailID.ToString());
                    m.Headers.Add("X-CS-Attempts", (m.NumberOfTries + 1).ToString());
                    m.Headers.Add("X-CS-AppDomain", AppDomain.CurrentDomain.FriendlyName);

					// Set the body encoding
					try { m.BodyEncoding = Encoding.GetEncoding(CSContext.Current.SiteSettings.EmailEncoding); }
					catch { m.BodyEncoding = Encoding.UTF8; }

					// Replace any LF characters with CR LF
					m.Body = m.Body.Replace("\r", string.Empty);
					m.Body = m.Body.Replace("\n", "\r\n");

					// Send it
                    SmtpMail.Send(m);

					// (Ken) Moved the delete command to here
					//       If a number of emails were sent, the command to delete them could timeout and duplicates would be sent on the next run
					EmailQueueProvider.Instance().DeleteQueuedEmail(m.EmailID);

                    if(		connectionLimit != -1 &&	++sentCount >= connectionLimit ) 
                    {
                        Thread.Sleep( new TimeSpan( 0, 0, 0, 15, 0 ) );
                        sentCount = 0;
                    }
					// on error, loop so to continue sending other email.
				} 
                catch( Exception e ) 
                {
                    Debug.WriteLine( e.Message + " : " + ( e.InnerException != null ? e.InnerException.Message : String.Empty ) );
                    CSException fe = new CSException( CSExceptionType.EmailUnableToSend, "SendQueuedEmails Failed To: " + m.To, ( e.InnerException != null ? e.InnerException : e ) );
                    fe.Log(site.SettingsID);

					// Add it to the failure list
					failure.Add(m.EmailID);
                }

                ++totalSent;

				if( site.EmailThrottle > 0 && totalSent >= site.EmailThrottle ) {
					break;
				}
			}

			if(failure.Count > 0)
				EmailQueueProvider.Instance().QueueSendingFailure(failure, failureInterval, maxNumberOfTries);

            
            if(totalSent > 0 || failure.Count > 0)
            {
                EventLogEntry entry = new EventLogEntry();
                entry.Message = string.Format("Email Queue:\n Messages Sent: {0}\nMessages Failed: {1}", totalSent,failure.Count);
                entry.Category = "Job Status";
                entry.EventID = 601;
                entry.SettingsID = site.SettingsID;
                EventLogs.Write(entry);
            }
             
        }
        #endregion

        #region User Management Emails

        public static MailMessage User (string emailType, User user, string password) {
            MailMessage email;

			// Do we have an email address?
			//
			if(user.Email == null)
				return null;

            // Do we have a password?
            //
            if (password != null)
                user.Password = password;

            // Get the email template we're going to use
            //
            email = GenericEmail(emailType, user, null, null, true, user.EnableHtmlEmail);

			if(email == null)
				return null;

            email.From = GenericEmailFormatter(email.From, user, null);
            email.Subject= GenericEmailFormatter(email.Subject, user, null);
			email.Body = GenericEmailFormatter(email.Body, user, null, user.EnableHtmlEmail);

            return email;
        }

		public static MailMessage UserInvitation (string emailType, UserInvitation userInvitation)
		{
			CSContext cntx = CSContext.Current;
			User user = cntx.User;

			// Do we have an email address?
			//
			if(userInvitation.Email == null)
				return null;

			// Get the email template we're going to use
			//
			MailMessage email = EmailTemplateProvider.Instance().GetTemplate(emailType, user);
			if(email == null)
				return null;
			
			EmailTemplateProvider.PopulateHeaders(email, userInvitation.Email, null, null, true);

			email.From = GenericEmailFormatter(email.From, user, null);
			email.Subject= GenericEmailFormatter(email.Subject, user, null);
			email.Body = GenericEmailFormatter(email.Body, user, null, user.EnableHtmlEmail);

			email.Body = Regex.Replace(email.Body, "\\[invitationmessage\\]", userInvitation.Message, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			email.Body = Regex.Replace(email.Body, "\\[invitationemail\\]", userInvitation.Email, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			email.Body = Regex.Replace(email.Body, "\\[invitationkey\\]", userInvitation.InvitationKey.ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled);

			if(cntx.IsWebRequest)
				email.Body = Regex.Replace(email.Body, "\\[invitationaccepturl\\]", Globals.HostPath(cntx.Context.Request.Url) + SiteUrls.Instance().AcceptUserInvitation(userInvitation.InvitationKey), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			else
				email.Body = Regex.Replace(email.Body, "\\[invitationaccepturl\\]", Globals.HostPath(new Uri(cntx.RawUrl)) + SiteUrls.Instance().AcceptUserInvitation(userInvitation.InvitationKey), RegexOptions.IgnoreCase | RegexOptions.Compiled);

			return email;
		}

        public static void UserPasswordForgotten (User user, string password) {
            if (user == null)
                return;

            EnqueuEmail(  User(EmailType.ForgottenPassword, user, password) );
        }

        public static void UserPasswordForgottenUID (User user) 
        {
            if (user == null)
                return;

            CSContext context = CSContext.Current;

            FormatList formats = new FormatList();

            string url = Globals.FullPath( SiteUrls.FormatUrlWithUserNameToken(Globals.GetSiteUrls().UserChangePassword,user,false));

            formats.Add("passwordchange",url);
            formats.Add("ip", context.Context.Request.UserHostAddress);
            formats.Add("name",user.DisplayName);
			formats.Add("sitename", Globals.HtmlDecode(Transforms.StripHtmlXmlTags(context.SiteSettings.SiteName)).Trim());
			formats.Add("adminemailfrom", string.Format(ResourceManager.GetString("AutomatedEmail").Trim(), Globals.HtmlDecode(Transforms.StripHtmlXmlTags(context.SiteSettings.SiteName)).Trim(), context.SiteSettings.AdminEmailAddress));

            MailMessage email = GenericEmail(EmailType.ForgottenPasswordWithToken,user,null,null,false,false);

            email.From = formats.Format(email.From);
            email.Body = formats.Format(email.Body);
            email.Subject = formats.Format(email.Subject);
            email.To = user.Email;


            EnqueuEmail(email);

        }

        public static void UserPasswordChanged (User user, string password) {
            if (user == null)
                return;

            EnqueuEmail(  User(EmailType.ChangedPassword, user, password) );
        }

        public static void UserCreate (User user, string password) {
            if (user == null)
                return;

            EnqueuEmail( User(EmailType.NewUserAccountCreated, user, password) );
        }

        public static void UserAccountPending (User user) {
            if (user == null)
                return;

            EnqueuEmail( User(EmailType.NewUserAccountPending, user, null) );
        }

        public static void UserAccountRejected (User user, User moderatedBy) {
            if (user == null)
                return;

            EnqueuEmail( User(EmailType.NewUserAccountRejected, user, null) );
        }

        public static void UserAccountApproved (User user) {
            if (user == null)
                return;

            EnqueuEmail( User(EmailType.NewUserAccountApproved, user, null) );
        }

		public static void UserInvitation(UserInvitation invite)
		{
			if (invite == null)
				return;

			EnqueuEmail(UserInvitation(EmailType.UserInvitation, invite));
		}

        #endregion

		#region Private Message Notification
		public static void PrivateMessageNotifications(Post post, ArrayList users) {

			foreach (User user in users)
			{
				// Only send PM notifications to recipients (not author)
				// if they've opted in to email notifications
				if ((user.EnableEmail) && (post.User.UserID != user.UserID))
					EnqueuEmail( CreatePost(post, EmailType.PrivateMessageNotification, user, null, null, false));
			}


		}
		#endregion

		#region User to User emails
		public static void UsersInRole(Guid roleID, Post post) {
			// Get Role
			Role role = Roles.GetRole(roleID);
			
			//UserSet countSet;
			UserSet emailSet = null;

            UserQuery query = new UserQuery();

			// special case for Forums-Everyone (roleID == 0)
			if (roleID == Guid.Empty) {

                query.PageSize = Int32.MaxValue;

				// find total users
				//countSet = Users.GetUsers(0,1,true,false);

				// get all users
				emailSet = Users.GetUsers(query,true);

			} else {
				// find total users in role
				//countSet = Roles.UsersInRole(0,1,SortUsersBy.Username,SortOrder.Ascending,roleID);

				// get all users in role
				emailSet = Roles.UsersInRole(0,int.MaxValue,SortUsersBy.Username,SortOrder.Ascending,roleID);	
			}

			foreach (User user in emailSet.Users) {
				MailMessage email;

				email = GenericEmail(EmailType.RoleEmail, user, null, null, true, user.EnableHtmlEmail);

				if(email == null)
					continue;
				
				email.From = GenericEmailFormatter(email.From, user, post);
				email.Subject = GenericEmailFormatter(email.Subject, user, post);
				email.Body = GenericEmailFormatter(email.Body, user, post, user.EnableHtmlEmail, false).Replace("[RoleName]",role.Name);

				Emails.EnqueuEmail(email);
			}

		}
		public static void UserToUser(User fromUser, User toUser, Post post) {
		
			MailMessage email;

			email = GenericEmail(EmailType.SendEmail, toUser, null, null, true, toUser.EnableHtmlEmail);

			if(email == null)
				return;
			
			email.From = GenericEmailFormatter(email.From, fromUser, post);
			email.Subject = GenericEmailFormatter(email.Subject, toUser, post);
			email.Body = GenericEmailFormatter(email.Body, toUser, post, toUser.EnableHtmlEmail);

			Emails.EnqueuEmail(email);

		}
		#endregion

        #region Email formatter

        // *********************************************************************
        //  FormatEmail
        //
        /// <summary>
        /// This method formats a given string doing search/replace for markup
        /// </summary>
        /// <param name="messageToFormat">Message to apply formatting to</param>
        /// <param name="user">User the message is being sent to</param>
        /// <param name="timezoneOffset">User's timezone offset</param>
        /// <param name="dbTimezoneOffset">Database's timezone offset</param>
        /// <param name="postID">ID of the post the message is about</param>
        /// <param name="html">If false HTML will stripped out of messages</param>
        /// 
        // ********************************************************************/


		protected static string GenericEmailFormatter (string stringToFormat, User user, Post post) {
			return GenericEmailFormatter (stringToFormat, user, post, false, false);
		}

		protected static string GenericEmailFormatter (string stringToFormat, User user, Post post, bool html) {
			return GenericEmailFormatter (stringToFormat, user, post, html, false);
		}

        protected static string GenericEmailFormatter (string stringToFormat, User user, Post post, bool html, bool truncateMessage ) {

//            string timeSend = string.Format(ResourceManager.GetString("Utility_CurrentTime_formatGMT"), DateTime.Now.ToString(ResourceManager.GetString("Utility_CurrentTime_dateFormat")));
			DateTime time = DateTime.Now;
        	CSContext cntx = CSContext.Current;
			SiteSettings settings = cntx.SiteSettings;
			SiteUrls urls = Globals.GetSiteUrls();

        	// set the timesent and sitename
            stringToFormat = Regex.Replace(stringToFormat, "\\[timesent\\]", time.ToString(ResourceManager.GetString("Utility_CurrentTime_dateFormat")) + " " + string.Format( ResourceManager.GetString("Utility_CurrentTime_formatGMT"), settings.TimezoneOffset.ToString() ), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = Regex.Replace(stringToFormat, "\\[sitename\\]", Globals.HtmlDecode(Transforms.StripHtmlXmlTags(settings.SiteName)).Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
            stringToFormat = Regex.Replace(stringToFormat, "\\[adminemail\\]", (settings.AdminEmailAddress.Trim() != "" ) ? settings.AdminEmailAddress.Trim() : "notset@localhost.com", RegexOptions.IgnoreCase | RegexOptions.Compiled);

			if(cntx.IsWebRequest)
			{
				string baseUrl = Globals.HostPath(cntx.Context.Request.Url);
				stringToFormat = Regex.Replace(stringToFormat, "\\[loginurl\\]", baseUrl + urls.LoginReturnHome, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				stringToFormat = Regex.Replace(stringToFormat, "\\[passwordchange\\]", baseUrl + urls.UserChangePassword, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				stringToFormat = Regex.Replace(stringToFormat, "\\[moderatorl\\]", baseUrl + urls.Moderate, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				stringToFormat = Regex.Replace(stringToFormat, "\\[websiteurl\\]", baseUrl + urls.Home, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			}
			else
			{
				string baseUrl = Globals.HostPath(new Uri(cntx.RawUrl));
				stringToFormat = Regex.Replace(stringToFormat, "\\[loginurl\\]", baseUrl + urls.LoginReturnHome, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				stringToFormat = Regex.Replace(stringToFormat, "\\[passwordchange\\]", baseUrl + urls.UserChangePassword, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				stringToFormat = Regex.Replace(stringToFormat, "\\[moderatorl\\]", baseUrl + urls.Moderate, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				stringToFormat = Regex.Replace(stringToFormat, "\\[websiteurl\\]", baseUrl + urls.Home, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			}
			
			// return a generic email address if it isn't set.
			//
			string adminEmailAddress = (settings.AdminEmailAddress.Trim() != "" ) ? settings.AdminEmailAddress.Trim() : "notset@localhost.com";
			string siteName = Globals.HtmlDecode(Transforms.StripHtmlXmlTags(settings.SiteName)).Trim();
            stringToFormat = Regex.Replace(stringToFormat, "\\[adminemailfrom\\]", string.Format( ResourceManager.GetString("AutomatedEmail").Trim(), siteName, adminEmailAddress), RegexOptions.IgnoreCase | RegexOptions.Compiled);

            // Specific to a user
            //
            if (user != null) {
                stringToFormat = Regex.Replace(stringToFormat, "\\[username\\]", user.Username.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
                stringToFormat = Regex.Replace(stringToFormat, "\\[email\\]", user.Email.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
                stringToFormat = Regex.Replace(stringToFormat, "\\[publicemail\\]", user.Profile.PublicEmail.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
                stringToFormat = Regex.Replace(stringToFormat, "\\[datecreated\\]", user.GetTimezone(user.DateCreated).ToString(user.Profile.DateFormat), RegexOptions.IgnoreCase | RegexOptions.Compiled);
                stringToFormat = Regex.Replace(stringToFormat, "\\[lastlogin\\]", user.GetTimezone(user.LastLogin).ToString(user.Profile.DateFormat), RegexOptions.IgnoreCase | RegexOptions.Compiled);

				if (cntx.IsWebRequest)
					stringToFormat = Regex.Replace(stringToFormat, "\\[profileurl\\]", Globals.HostPath(cntx.Context.Request.Url) +  urls.UserEditProfile, RegexOptions.IgnoreCase | RegexOptions.Compiled);
				else
					stringToFormat = Regex.Replace(stringToFormat, "\\[profileurl\\]", Globals.HostPath(new Uri(cntx.RawUrl)) +  urls.UserEditProfile, RegexOptions.IgnoreCase | RegexOptions.Compiled);

				if (user.Password != null)
					stringToFormat = Regex.Replace(stringToFormat, "\\[password\\]", user.Password.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);

            }

			// make urls clickable, don't do it if we have a post, 
			// because we're going to do it again before adding the post contents
			if (html && post == null) stringToFormat = Regex.Replace(stringToFormat, @"(http|ftp|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:\$/~\+#]*[\w\-\@?^=%&/~\+#])?", "<a href=\"$0\">$0</a>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

            if (post != null) {
                stringToFormat = Regex.Replace(stringToFormat, "\\[postedby\\]", post.Username.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
                stringToFormat = Regex.Replace(stringToFormat, "\\[subject\\]", post.Subject.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
                stringToFormat = Regex.Replace(stringToFormat, "\\[postdate\\]", post.User.GetTimezone(post.PostDate).ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled);

				if(cntx.IsWebRequest)
				{
					string baseUrl = Globals.HostPath(cntx.Context.Request.Url);
					stringToFormat = Regex.Replace(stringToFormat, "\\[posturl\\]", baseUrl + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
					stringToFormat = Regex.Replace(stringToFormat, "\\[replyurl\\]", baseUrl + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
					stringToFormat = Regex.Replace(stringToFormat, "\\[moderatePostUrl\\]", baseUrl + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
				}
				else
				{
					string baseUrl = Globals.HostPath(new Uri(cntx.RawUrl));
					stringToFormat = Regex.Replace(stringToFormat, "\\[posturl\\]", baseUrl + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
					stringToFormat = Regex.Replace(stringToFormat, "\\[replyurl\\]", baseUrl + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
					stringToFormat = Regex.Replace(stringToFormat, "\\[moderatePostUrl\\]", baseUrl + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
				}

				if(post.Section != null)
					stringToFormat = Regex.Replace(stringToFormat, "\\[forumname\\]", post.Section.Name.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);

                //stringToFormat = Regex.Replace(stringToFormat, "\\[forumUrl\\]", Globals.HostPath(CSContext.Current.Context.Request.Url) + ForumUrls.Instance().Forum(post.SectionID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
				
				// make urls clickable before adding post HTML
				if (html) stringToFormat = Regex.Replace(stringToFormat,@"(http|ftp|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:\$/~\+#]*[\w\-\@?^=%&/~\+#])?","<a href=\"$0\">$0</a>",RegexOptions.IgnoreCase | RegexOptions.Compiled);

				// strip html from post if necessary
				string postbody = Formatter.ConvertLocalUrls(post.RenderedBody(PostTarget.Other), (cntx.IsWebRequest) ? Globals.HostPath(cntx.Context.Request.Url) : Globals.HostPath(new Uri(cntx.RawUrl)));

				// if the user doesn't want HTML and the post is HTML, then strip it
				if (!html && post.PostType == PostContentType.HTML) 
					postbody = Emails.FormatHtmlAsPlainText(postbody);
				
					// if the user wants HTML and the post is PlainText, then add HTML to it
				else if (html &&  post.PostType == PostContentType.Text) 
					postbody = Emails.FormatPlainTextAsHtml(postbody);

				// Finally, trim this post so the user doesn't get a huge email
				//
				postbody.Trim();

				if (truncateMessage) {
					// if we throw an error, the post was too short to cut anyhow
					// 
					try {
						postbody = Formatter.CheckStringLength(postbody, 300);						
					}
					catch {}
				}

				stringToFormat = Regex.Replace(stringToFormat, "\\[postbody\\]", postbody, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            }

            return stringToFormat;

        }


		// *********************************************************************
		//  FormatHtmlMessageAsPlainText
		//
		/// <summary>
		/// This method removes HTML from a string
		/// </summary>
		/// <param name="messageToFormat">Message to apply formatting to</param>
		/// 
		// ********************************************************************/

		protected static string FormatHtmlAsPlainText (string stringToFormat) {
			if (stringToFormat == null || stringToFormat == string.Empty) return "";
			
			// get rid of extra line breaks
			stringToFormat = Regex.Replace(stringToFormat,"\n"," ",RegexOptions.IgnoreCase | RegexOptions.Compiled);
			
			// add linebreaks from HTML for <br>, <p>, <li>, and <blockquote> tags
			stringToFormat = Regex.Replace(stringToFormat,@"</?(br|p|li|blockquote)(\s/)?>","\n",RegexOptions.IgnoreCase | RegexOptions.Compiled);
			
			// strip all remaining HTML
			stringToFormat = Regex.Replace(stringToFormat,@"</?(\w+)(\s*\w*\s*=\s*(""[^""]*""|'[^']'|[^>]*))*|/?>","",RegexOptions.IgnoreCase | RegexOptions.Compiled);

			// replace special characters
			stringToFormat = stringToFormat.Replace("&nbsp;", " ");
			stringToFormat = stringToFormat.Replace("&lt;", "<");
			stringToFormat = stringToFormat.Replace("&gt;", ">");
			stringToFormat = stringToFormat.Replace("&amp;", "&");
			stringToFormat = stringToFormat.Replace("&quot;", "\"");

			return stringToFormat;

		}

		// *********************************************************************
		//  FormatPlainTextAsHtml
		//
		/// <summary>
		/// This method formats a plain text message as HTML
		/// </summary>
		/// <param name="stringToFormat">Message to apply formatting to</param>
		/// 
		// ********************************************************************/

		protected static string FormatPlainTextAsHtml (string stringToFormat) {
			if (stringToFormat == null || stringToFormat == string.Empty) return "";
			
			// line breaks
			stringToFormat = Regex.Replace(stringToFormat,"\n","<br />",RegexOptions.IgnoreCase | RegexOptions.Compiled);

			// make urls clickable
			stringToFormat = Regex.Replace(stringToFormat,@"(http|ftp|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:/~\+#\$]*[\w\-\@?^=%&/~\+#])?","<a href=\"$0\">$0</a>",RegexOptions.IgnoreCase | RegexOptions.Compiled);

			return stringToFormat;
		}
		#endregion

        #region Post Emails

        protected static MailMessage CreatePost (Post post, string emailType) {
            return CreatePost(post, emailType, null, null, null, true);
        }

		protected static MailMessage CreatePost (Post post, string emailType, User userTo, string[] cc, string[] bcc, bool sendToUser) {
			return CreatePost(post, emailType, userTo, cc, bcc, true, false);
		}

        protected static MailMessage CreatePost (Post post, string emailType, User userTo, string[] cc, string[] bcc, bool sendToUser, bool html) {
            MailMessage email;

			if ((emailType == "NewMessagePostedToForum") && (post.ParentID != post.PostID))
				emailType = "NewMessagePostedToThread";

            // Get the email template we're going to use
            //
            email = GenericEmail(emailType, userTo, cc, bcc, sendToUser, html);

			if(email == null)
				return null;

            if (userTo != null)
                email.To = userTo.Email;

            email.From = GenericEmailFormatter(email.From, null, post);
            email.Body = GenericEmailFormatter(email.Body, null, post, html);
            email.Subject = GenericEmailFormatter(email.Subject, null, post);

            return email;
        }

        // *********************************************************************
        //  PostApproved
        //
        /// <summary>
        /// This method sends an email to the user whose post has just been approved.
        /// </summary>
        /// <param name="PostID">Specifies the ID of the Post that was just approved.</param>
        /// 
        // ********************************************************************/
        public static void PostApproved (Post post) {
            if (post == null)
                return;

            // Make sure we can send the email
            //
            if (!CanSend(post.User))
                return;

            EnqueuEmail(CreatePost(post, EmailType.MessageApproved, post.User, null, null, true));
        }

        // *********************************************************************
        //  PostMoved
        //
        /// <summary>
        /// This method sends an email to the user whose approved post has just been moved.
        /// </summary>
        /// <param name="postID">The post to move</param>
        // ********************************************************************/        
        public static void PostMoved (Post post) {
            if (post == null)
                return;

            // Make sure we can send the email
            //
            if (!CanSend(post.User))
                return;

            EnqueuEmail(CreatePost(post, EmailType.MessageMoved, post.User, null, null, true));
        }

        // *********************************************************************
        //  PostMovedAndApproved
        //
        /// <summary>
        /// This method sends an email to the user whose post has just been moved AND approved.
        /// </summary>
        /// <param name="postID">Specifies the ID of the Post that was just approved.</param>
        /// 
        // ********************************************************************/
        public static void PostMovedAndApproved (Post post) {
            if (post == null)
                return;

            // Make sure we can send the email
            //
            if (!CanSend(post.User))
                return;

            EnqueuEmail(CreatePost(post, EmailType.MessageMovedAndApproved, post.User, null, null, true));
        }

        
        public static void ThreadJoined (Post parent, Post child) {
        }

        // *********************************************************************
        //  PostRemoved
        //
        /// <summary>
        /// Email sent when a post is removed.
        /// </summary>
        // ********************************************************************/
        public static void PostRemoved(Post post, User moderatedBy, string reason) {
            if (post == null)
                return;

            // Make sure we can send the email
            //
            if (!CanSend(post.User))
                return;

            MailMessage email = CreatePost(post, EmailType.MessageDeleted, post.User, null, null, true);

            email.Body = email.Body.Replace("[DeleteReasons]", reason);
            email.Body = email.Body.Replace("[DeletedByID]", moderatedBy.UserID.ToString());
            email.Body = email.Body.Replace("[Moderator]", moderatedBy.Username);

            EnqueuEmail(email);
        }

        #endregion

		#region GetEmailsTracking

		protected static Hashtable GetEmailsTrackingSectionByPostID(int postID) {
			// Create Instance of the CommonDataProvider
		    CommonDataProvider dp = CommonDataProvider.Instance();
			
			return dp.GetEmailsTrackingSectionByPostID(postID);
		}

        // *********************************************************************
        //  GetEmailsTrackingThread
        //
        /// <summary>
        /// Retrieves a list of email addresses from the users who are tracking a particular thread.
        /// </summary>
        /// <param name="postID">The PostID of the new message.  We really aren't interested in this
        /// Post, specifically, but the thread it belongs to.</param>
        /// <returns>A ArrayList with the email addresses of those who want to receive
        /// notification when a message in this thread is replied to.</returns>
        /// 
        // ********************************************************************/
        protected static Hashtable GetEmailsTrackingThread(int postID) {
            // Create Instance of the CommonDataProvider
            CommonDataProvider dp = CommonDataProvider.Instance();

            return dp.GetEmailsTrackingThread(postID);
        }		
		#endregion

        #region Protected helper methods
        protected static bool CanSend (User user) {

            if ((user == null) || (!user.EnableEmail))
                return false;

            return true;
        }

        protected static MailMessage GenericEmail (string emailType, User user, string[] cc, string[] bcc) {
            return GenericEmail (emailType, user, cc, bcc, true, false);
        }

		protected static MailMessage GenericEmail (string emailType, User user, string[] cc, string[] bcc, bool sendToUser) {
			return GenericEmail (emailType, user, cc, bcc, sendToUser, false);
		}

		protected static MailMessage GenericEmail (string emailType, User user, string[] cc, string[] bcc, bool sendToUser, bool html) 
		{
			MailMessage email = EmailTemplateProvider.Instance().GetTemplate(emailType, user);

			if(email == null)
				return null;
			
			if(sendToUser)
				EmailTemplateProvider.PopulateHeaders(email, user.Email, cc, bcc, html);
				else
				EmailTemplateProvider.PopulateHeaders(email, null, cc, bcc, html);

			return email;
        }
        #endregion

    }
}
