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

using System;
using System.Collections;
using CommunityServer.Configuration;

namespace CommunityServer.Components {

    // *********************************************************************
    //  Post
    //
    /// <summary>
    /// This class contains the properties that describe a Post.
    /// </summary>
    //
    // ********************************************************************/
	[Serializable]
	public abstract class Post : ExtendedAttributes, IRateable, IContent {

        #region Private Members
        int postID = 0;				   	// the unique ID for the post
        int threadID = 0;				// the ID indicating what thread the post belongs to
        int parentID = 0;				// indicates the thread's parent - 0 if the post is an original post
        int sectionID = 0;				// indicates the forum the thread was posted to
	    int emoticonID = 0;
        int postLevel = 0;				// indicates the postlevel - i.e., how many replies deep the thread is
        int sortOrder = 0;				// indicates the order the thread is sorted
        int replies = 0;				// how many replies this posting has received
        int views = 0;                  		// Total times the post has been viewed
		int points = 0;					// total points for this post
        int prevThreadID = 0;             		// Previous thread
        int nextThreadID = 0;             		// Next thread
        String username = "";				// uniquely identifies the user who posted the post
        int indexInThread = 0;          		// The index of the post relative to the thread
        String subject = "";				// the subject of the post
        string userHostAddress = "000.000.000.000";
        string body;				    	// The body of the post, in raw format
        string formattedBody;           		// Formatted body
        string editNotes;               		// Edit notes associated with the post
        DateTime postDate;				// the date the post was made
        DateTime threadDate;				// the date of the thread (the postDate of the most recent post to the thread)
        bool approved = true;				// whether or not the post is approved
        bool islocked = false;          		// whether or not the post allows replies
        bool hasRead = false;           		// whether or not the post has been read by the user
        bool isTracked = false;         		// whether or not the post is being tracked by the user
        string attachmentFilename;      
        User user;
        private string name;
        private DateTime _userTime;
        private int _userID;
        private PostAttachmentMetaData _attachment = null;
		private int totalRatings;
		private int ratingSum;
		private IList ratings;
		private bool autoApprove = false;
        
        PostContentType postType = PostContentType.Text;
		PostMediaType postMedia = PostMediaType.Empty;

        #endregion

		public override object Copy()
		{
			Post p = base.Copy () as Post;
			p.UserHostAddress = this.UserHostAddress;
			p.EmoticonID = this.EmoticonID;
			p.AttachmentFilename = this.AttachmentFilename;

			//Should we copy user?
			p.User = this.User;
			p.IndexInThread = this.IndexInThread;
			p.PostID = this.PostID;
			p.PostType = this.PostType;
			p.IsLocked = this.IsLocked;
			p.HasRead = this.HasRead;
			p.IsTracked = this.IsTracked;
			p.Views = this.Views;
			p.Points = this.Points;
			p.ThreadID = this.ThreadID;
			p.ThreadIDNext = this.ThreadIDNext;
			p.ThreadIDPrev = this.ThreadIDPrev;
			p.ParentID  = this.ParentID;
			p.Section = this.Section;
			p.SectionID = this.SectionID;
			p.PostLevel = this.PostLevel;
			p.SortOrder = this.SortOrder;
			p.Replies = this.Replies;
			p.Username = this.Username;
			p.Subject = this.Subject;
			p.Body = this.Body;
			p.FormattedBody = this.FormattedBody;
			p.EditNotes = this.EditNotes;
			p.PostDate = this.PostDate;
			p.ThreadDate = this.ThreadDate;
			p.IsApproved = this.IsApproved;
			p.UserTime = this.UserTime;
			p.Name = this.Name;
            p.UserID = this.UserID;
            p.Attachment = this.Attachment;
			p.TotalRatings = this.TotalRatings;
			p.RatingSum = this.RatingSum;
			p.VideoUrl = this.VideoUrl;
			p.VideoImageUrl = this.VideoImageUrl;
			p.VideoDuration = this.VideoDuration;
			p.VideoHeight = this.VideoHeight;
			p.VideoWidth = this.VideoWidth;
			p.AutoApprove = this.AutoApprove;
            p.ApplicationPostTypeValue = this.ApplicationPostTypeValue;
            p.PostConfigurationValue = this.PostConfigurationValue;
			p.SpamScore = this.SpamScore;
			p.PostMedia = this.PostMedia;
			
			return p;
		}

		#region Polls

		public bool HasPoll
		{
			get
			{
				return PollItemIndexes != String.Empty;
			}
			set
			{
				if (value == false)
				{
					ClearPollItems();
					PollTitle = String.Empty;
					PollDescription = String.Empty;
					PollExpirationDate = DateTime.MinValue;
					SetPostMedia(PostMediaType.Poll, false);
				}
			}
		}

		public DateTime PollExpirationDate
		{
			get { return this.GetDateTime("PollExpirationDate", DateTime.MinValue); }
			set { this.SetExtendedAttribute("PollExpirationDate", value.ToString()); }
		}

		public string PollTitle
		{
			get { return this.GetExtendedAttribute("PollTitle"); }
			set { this.SetExtendedAttribute("PollTitle", value); }
		}

		public string PollDescription
		{
			get { return this.GetExtendedAttribute("PollDescription"); }
			set { this.SetExtendedAttribute("PollDescription", value); }
		}

		private string PollItemIndexes
		{
			get { return this.GetExtendedAttribute("PollItemIndexes"); }
			set 
			{ 
				this.SetExtendedAttribute("PollItemIndexes", value); 

				if ((PostMedia & PostMediaType.Poll) != PostMediaType.Poll)
					SetPostMedia(PostMediaType.Poll, true);
			}
		}

		public void AddPollItem(PollItem item)
		{
			if (HasPoll)
			{
				string[] indexes = PollItemIndexes.Split(',');
				item.AnswerID = (int.Parse(indexes[indexes.Length - 1]) + 1).ToString();
				PollItemIndexes += "," + item.AnswerID;
			}
			else
			{
				item.AnswerID = "1";
				PollItemIndexes = "1";
			}			

			SetExtendedAttribute("PollItem" + item.AnswerID, item.OrderNumber.ToString() + "," + item.Answer);
		}

		public void UpdatePollItem(PollItem item)
		{
			if (HasPoll)
			{
				string[] indexes = PollItemIndexes.Split(',');
				foreach (string index in indexes)
				{
					if (index == item.AnswerID)
					{
						SetExtendedAttribute("PollItem" + index, item.OrderNumber.ToString() + "," + item.Answer);
						break;
					}
				}
			}			
		}

		public PollItem RemovePollItem(PollItem item)
		{
			if (HasPoll)
			{
				System.Text.StringBuilder indexString = new System.Text.StringBuilder();
				string[] indexes = PollItemIndexes.Split(',');
				foreach (string index in indexes)
				{
					if (index == item.AnswerID)
						SetExtendedAttribute("PollItem" + index, String.Empty);
					else
					{
						if (indexString.Length > 0)
							indexString.Append(",");

						indexString.Append(index);
					}
				}

				PollItemIndexes = indexString.ToString();
			}

			return item;
		}

		public void ClearPollItems()
		{
			if (HasPoll)
			{
				string[] indexes = PollItemIndexes.Split(',');
				foreach (string index in indexes)
				{
					SetExtendedAttribute("PollItem" + index, String.Empty);
				}

				PollItemIndexes = String.Empty;
			}
		}

		public ArrayList GetPollItems()
		{
			ArrayList pollItems = new ArrayList();

			if (HasPoll)
			{
				string[] indexes = PollItemIndexes.Split(',');
				int i = 0;
				foreach (string index in indexes)
				{
					string[] pollValues = GetExtendedAttribute("PollItem" + index).Split(new char[] {','}, 2);
					if (pollValues.Length == 2)
						pollItems.Add(new PollItem(index, pollValues[1], int.Parse(pollValues[0])));
					else
					{
						pollItems.Add(new PollItem(index, pollValues[0], i));
						i++;
					}
				}

				pollItems.Sort();
			}

			return pollItems;
		}

		public void CopyPollDataFrom(Post p)
		{
			HasPoll = false;

			PollItemIndexes = p.GetExtendedAttribute("PollItemIndexes");
			PollTitle = p.PollTitle;
			PollDescription = p.PollDescription;
			PollExpirationDate = p.PollExpirationDate;

			foreach (PollItem pi in p.GetPollItems())
			{
				UpdatePollItem(pi);
			}
		}

		#endregion

		#region Video
		public bool HasVideo
		{
			get { return ( ((PostMedia & PostMediaType.Video) == PostMediaType.Video) && (VideoUrl.Trim().Length > 0)); }
		}

		public string VideoUrl
		{
			get { return this.GetExtendedAttribute("VideoUrl"); }
			set { this.SetExtendedAttribute("VideoUrl", value); }
		}

		public string VideoFileFormat
		{
			get
			{
				if (!HasVideo)
					return string.Empty;

				string extension = System.IO.Path.GetExtension(VideoUrl);
				if ((extension != null) && (extension.Trim().Length > 0))
					return extension.TrimStart('.');

				return "???";
			}
		}

		public string VideoImageUrl
		{
			get
			{
				if (HasVideo && (this.GetExtendedAttribute("VideoImageUrl").Trim().Length <= 0))
					return Globals.GetSkinPath() + "/images/video.gif";

				return this.GetExtendedAttribute("VideoImageUrl");
			}
			set { this.SetExtendedAttribute("VideoImageUrl", value); }
		}

		public string VideoDuration
		{
			get
			{
				if (HasVideo && (this.GetExtendedAttribute("VideoDuration").Trim().Length <= 0))
					return "--:--";

				return this.GetExtendedAttribute("VideoDuration");
			}
			set { this.SetExtendedAttribute("VideoDuration", value); }
		}

        public string VideoContentType
        {
            get
            {
                string vurl = this.VideoUrl;
                if(vurl == null)
                    return string.Empty;

                return AttachmentSettings.GetMapping(vurl);
            }
        }

		public int VideoHeight
		{
			get
			{
				string height = this.GetExtendedAttribute("VideoHeight");
				if (height.Length == 0)
					return -1;
				else
					return int.Parse(height);
			}
			set
			{
				if (value >= 0)
					this.SetExtendedAttribute("VideoHeight", value.ToString());
				else
					this.SetExtendedAttribute("VideoHeight", string.Empty);
			}
		}

		public int VideoWidth
		{
			get
			{
				string height = this.GetExtendedAttribute("VideoWidth");
				if (height.Length == 0)
					return -1;
				else
					return int.Parse(height);
			}
			set
			{
				if (value >= 0)
					this.SetExtendedAttribute("VideoWidth", value.ToString());
				else
					this.SetExtendedAttribute("VideoWidth", string.Empty);
			}
		}


		#endregion

        #region Spam

    	private int _spameScore = -1;
    	private PostStatus _postStatus = PostStatus.Unknown;
    	
        public int SpamScore
        {
            get
            {
            	if(_spameScore == -1)
            	{
            		_spameScore = GetInt("SpamScore", 0);
            		
            	}
            	return _spameScore;
            }
            set { _spameScore = value;}
        }
    	
    	public PostStatus PostStatus
    	{
    		get{ return _postStatus;}
    		set{ _postStatus = value;}
    	}

        public SpamStatus SpamStatus
        {
            get { return (SpamStatus)GetInt("SpamStatus",0);}
            set { SetExtendedAttribute("SpamStatus",((int)value).ToString());}
        }

        #endregion

		#region Ink
		public int InkID
		{
			get{ return GetInt("InkID",-1);}
			set { SetExtendedAttribute("InkID", value.ToString());}
		}

		public bool HasInk
		{
			get { return InkID > 0;}
		}
		#endregion

        protected internal int ApplicationPostTypeValue = 0;
        protected internal int PostConfigurationValue = 0;

        public string UserHostAddress 
        {
            get {return userHostAddress;}
            set {userHostAddress = value;}
        }

        public int UserID 
        {
            get {return _userID;}
            set {_userID = value;}
        }

        public int EmoticonID {
            get {return emoticonID;}
            set {emoticonID = value;}
        }

        public string AttachmentFilename {
            get
            {
                if(Globals.IsNullorEmpty(attachmentFilename))
                {
                    if(this.Attachment != null)
                        return Attachment.FileName;
                }
                return attachmentFilename;
            }
            set {attachmentFilename = value;}
        }

        public PostAttachmentMetaData Attachment
        {
            get{return _attachment;}
            set{_attachment = value;}
        }
 
        public bool HasAttachment {
            get {return !Globals.IsNullorEmpty(AttachmentFilename);}
        }

		
		// *********************************************************************
		//
		//  ForceExcerpt
		//
		/// <summary>
		/// Returns the Excerpt if available or creates one from the FormattedBody
		/// </summary>
		//
		// ********************************************************************/
		public string ForceExcerpt
		{
			get
			{
				if(this.HasExcerpt)
					return this.Excerpt;

				else
					return Formatter.RemoveHtml(this.FormattedBody,250);
			}
		}

		// *********************************************************************
		//
		//  Excerpt
		//
		/// <summary>
		/// Short description of post
		/// </summary>
		//
		// ********************************************************************/
		public string Excerpt
		{
			get{return this.GetExtendedAttribute("Excerpt");}
			set{this.SetExtendedAttribute("Excerpt",value);}
		}

        // *********************************************************************
        //
        //  User
        //
        /// <summary>
        /// User that created this post.   
        /// </summary>
        //
        // ********************************************************************/
        public User User 
		{
            get {
				 return user;
            }
            set {
                user = value;
            }
        }

        public int IndexInThread {
            get {
                return indexInThread;
            }
            set {
                indexInThread = value;
            }
        }

        // *********************************************************************
        //
        //  PostID
        //
        /// <summary>
        /// Specifies the ID of the Post, the unique identifier.
        /// </summary>
        //
        // ********************************************************************/
        public int PostID {
            get { 
                return postID; 
            }

            set {
                if (value < 0)
                    postID = 0;
                else
                    postID = value;
            }
        }

        public PostContentType PostType {
            get {
                return postType;
            }
            set {
                postType = value;
            }
        }

		public virtual PostMediaType PostMedia
		{
			get 
			{
				return postMedia;
			}
			set 
			{
				postMedia = value;
			}
		}

		
		/// <summary>
		/// Helper to set the PostMedia value
		/// </summary>
		public void SetPostMedia(PostMediaType pm, bool isSelected)
		{
			if(isSelected)
				PostMedia = PostMedia | pm;
			else
				PostMedia = PostMedia & ~pm;
		}



		// *********************************************************************
        //
        //  IsLocked
        //
        /// <summary>
        /// Whether or not this post allows replies
        /// </summary>
        //
        // ********************************************************************/
        public virtual bool IsLocked 
		{
            get { return islocked; }
            
            set {
                islocked = value;
            }
        }

        // *********************************************************************
        //
        //  HasRead
        //
        /// <summary>
        /// Whether or not this post allows replies
        /// </summary>
        //
        // ********************************************************************/
        public bool HasRead {
            get { return hasRead; }
            
            set {
                hasRead = value;
            }
        }
		
		// *********************************************************************
		//
		//  HasExcerpt
		//
		/// <summary>
		/// Does this post have an excerpt
		/// </summary>
		//
		// ********************************************************************/
		public bool HasExcerpt
		{
			get{return (Excerpt != null && Excerpt.Length > 0);}
		}



        // *********************************************************************
        //
        //  IsTracked
        //
        /// <summary>
        /// Whether or not this post allows replies
        /// </summary>
        //
        // ********************************************************************/
        public virtual bool IsTracked 
		{
            get { return isTracked; }
            
            set {
                isTracked = value;
            }
        }


        // *********************************************************************
        //
        //  Views
        //
        /// <summary>
        /// Total number of views for a given post
        /// </summary>
        //
        // ********************************************************************/
        public int Views {
            get { return this.views; }
            
            set {
                views = value;
            }
        }

		// *********************************************************************
		//
		//  Points
		//
		/// <summary>
		/// Total number of points for a given post
		/// </summary>
		//
		// ********************************************************************/
		public int Points
		{
			get { return this.points; }
			set { points = value; }
		}

        // *********************************************************************
        //
        //  ThreadID
        //
        /// <summary>
        /// Indicates what thread the Post belongs to.
        /// </summary>
        //
        // ********************************************************************/
        public int ThreadID {
            get { return threadID; }
            set {
                if (value < 0)
                    threadID = 0;
                else
                    threadID = value;
            }
        }

        // *********************************************************************
        //
        //  ThreadIDNext
        //
        /// <summary>
        /// Indicates what thread the Post belongs to.
        /// </summary>
        //
        // ********************************************************************/
        public int ThreadIDNext {
            get { return nextThreadID; }
            set {
                if (value < 0)
                    nextThreadID = 0;
                else
                    nextThreadID = value;
            }
        }

        // *********************************************************************
        //
        //  ThreadIDPrev
        //
        /// <summary>
        /// Indicates what thread the Post belongs to.
        /// </summary>
        //
        // ********************************************************************/
        public int ThreadIDPrev {
            get { return prevThreadID; }
            set {
                if (value < 0)
                    prevThreadID = 0;
                else
                    prevThreadID = value;
            }
        }

        // *********************************************************************
        //
        //  ParentID
        //
        /// <summary>
        /// Specifies the thread's parent ID.  (That is, the PostID of the replied-to post.)
        /// </summary>
        //
        // ********************************************************************/
        public int ParentID {
            get { return parentID; }
            set {
                if (value < 0)
                    parentID = 0;
                else
                    parentID = value;
            }
        }

		// *********************************************************************
		//
		//  Section
		//
		/// <summary>
		/// Defines the parent of the current post
		/// </summary>
		//
		// ********************************************************************/
        public abstract Section Section
        {
            get;
            set;
        }

        // *********************************************************************
        //
        //  ForumID
        //
        /// <summary>
        /// Specifies the ID of the Forumt the post belongs to.
        /// </summary>
        //
        // ********************************************************************/
        public int SectionID {
            get { return sectionID; }
            set {
                if (value < 0)
                    sectionID = 0;
                else
                    sectionID = value;
            }
        }

        // *********************************************************************
        //
        //  PostLevel
        //
        /// <summary>
        /// Specifies the level of the post.
        /// </summary>
        /// <remarks>
        /// Each reply-level has an incrementing PostLevel.  That is, the first message in
        /// a thread has a PostLevel of 0, while any replies to the first message in a thread have a
        /// PostLevel of 1, and any replies to any posts with a PostLevel of 1, have a PostLevel of 2,
        /// and so on...
        /// </remarks>
        //
        // ********************************************************************/
        public int PostLevel {
            get { return postLevel; }
            set {
                if (value < 0)
                    postLevel = 0;
                else
                    postLevel = value;
            }
        }

        // *********************************************************************
        //
        //  SortOrder
        //
        /// <summary>
        /// Specifies the SortOrder of the posts.
        /// </summary>
        /// <remarks>
        /// The property is used to sort the posts in descending order, starting with the
        /// most recent post.
        /// </remarks>
        //
        // ********************************************************************/
        public int SortOrder {
            get { return sortOrder; }
            set {
                if (value < 0)
                    sortOrder = 0;
                else
                    sortOrder = value;
            }
        }

        // *********************************************************************
        //
        //  Replies
        //
        /// <summary>
        /// Specifies how many replies have been made to this post.
        /// </summary>
        /// <remarks>
        /// This property is only populated when viewing all of the posts for a particular
        /// forum, and only contains a valid value when the user is viewing the forum posts in Flat or Mixed
        /// mode.
        /// </remarks>
        //
        // ********************************************************************/
        public int Replies {
            get { return replies; }
            set {
                if (value < 0)
                    replies = 0;
                else
                    replies = value;
            }
        }


        // *********************************************************************
        //
        //  Username
        //
        /// <summary>
        /// Returns the Username of the user who made the post.
        /// </summary>
        //
        // ********************************************************************/
        public virtual String Username {
			get { return username; }
            set { username = value; }			
        }


        // *********************************************************************
        //
        //  Subject
        //
        /// <summary>
        /// Returns the subject of the post.
        /// </summary>
        //
        // ********************************************************************/
        public virtual String Subject {
            get { return subject; }
            set { subject = value; }
        }

        // *********************************************************************
        //
        //  Body
        //
        /// <summary>
        /// Returns the body of the post.
        /// </summary>
        /// <remarks>
        /// The body of the post is stored in a raw format in the database.
        /// </remarks>
        //
        // ********************************************************************/
        public virtual String Body {
            get { return body; }
            set { body = value; }
        }

        public virtual string EditNotes {
            get {return editNotes;}
            set {editNotes = value;}
        }

        // *********************************************************************
        //
        //  FormattedBody
        //
        /// <summary>
        /// Returns a pre-formatted version of the body of the post.
        /// </summary>
        /// <remarks>
        /// The FormattedBody of the post is stored in a pre-formatted HTML.
        /// </remarks>
        //
        // ********************************************************************/
        public virtual String FormattedBody {
            get { return formattedBody; }
            set { formattedBody = value; }
        }

        // *********************************************************************
        //
        //  PostDate
        //
        /// <summary>
        /// Specifies the date/time the post was made, relative to the database's timezone.
        /// </summary>
        //
        // ********************************************************************/
        public DateTime PostDate 
        {
            get { return postDate; }
            set { postDate = value; }
        }

        // *********************************************************************
        //
        //  ThreadDate
        //
        /// <summary>
        /// Specifies the date/time of the most recent post in the thread, relative to the database's
        /// time zone.
        /// </summary>
        //
        // ********************************************************************/
        public DateTime ThreadDate {
            get { return threadDate; }
            set { threadDate = value; }
        }

        // *********************************************************************
        //
        //  IsApproved
        //
        /// <summary>
        /// Indicates if the post has been approved or not.  Non-approved posts are posts that are
        /// still awaiting moderation.
        /// </summary>
        //
        // ********************************************************************/
        public bool IsApproved {
            get { return approved; }
            set { approved = value; }
        }
        
  

        /// <summary>
        /// Timezone refactored date. Converts date of post to the local time of the user
        /// </summary>
        public DateTime UserTime
        {
            get {  return this._userTime; }
            set {  this._userTime = value; }
        }

        /// <summary>
        /// Name of the post.
        /// </summary>
        public string Name
        {
            get{return name;}
            set{name = value;}
        }
        
		public int TotalRatings
		{
			get { return totalRatings; }
			set { totalRatings = value; }
		}

		public int RatingSum
		{
			get { return ratingSum; }
			set { ratingSum = value; }
		}

		public double RatingAverage
		{
			get
			{
				if (TotalRatings == 0)
					return 0;

				return ((double)RatingSum / (double)TotalRatings);
			}
		}

		public IList Ratings
		{
			get
			{
				if ((ratings == null) && (TotalRatings > 0))
					ratings = CommunityServer.Components.Ratings.GetRatings(RatingType, ItemID);

				return ratings;
			}
		}

		protected virtual RatingType RatingType
		{
			get { return RatingType.Post; }
		}

		protected virtual int ItemID
		{
			get { return PostID; }
		}

		public bool AutoApprove
		{
			get { return autoApprove; }
			set { autoApprove = value; }
		}

		// *********************************************************************
		//
		//  RenderedBody
		//
		/// <summary>
		/// Returns a processed version of the formatted body of the post.
		/// </summary>
		/// <remarks>
		/// The RenderedBody of the post is returned in a pre-formatted HTML.
		/// </remarks>
		//
		// ********************************************************************/
		public virtual String RenderedBody(PostTarget target)
		{
			Post p = this.Copy() as Post;

			if (p.Section != null)
				CSEvents.PreRenderPost(p,p.Section.ApplicationType,target);
			else
				CSEvents.PreRenderPost(p,ApplicationType.Unknown,target);

			return p.FormattedBody;
		}  	


	}
}
