using System;
using System.Collections.Generic;
using System.Threading;

using Bitrix.Components;
using Bitrix.DataLayer;
using Bitrix.DataTypes;
using Bitrix.IO;
using Bitrix.Security;
using Bitrix.Services;
using Bitrix.Services.Text;
using Bitrix.UI;
using System.Web.UI;
using Bitrix.CommunicationUtility;
using System.Globalization;
using Bitrix.Components.Editor;

namespace Bitrix.Blog.Components
{
	public partial class BlogPostsComponent : BXComponent
	{
		private List<PostInfo> posts = new List<PostInfo>();
		private bool templateIncluded;
		private ErrorCode fatalError = ErrorCode.FatalComponentNotExecuted;
		private Exception fatalException;
		private int maxWordLength = -1;
		private BXBlogPostChain processor;
		private Sorting sortBy;
		private int periodDays = -1;

		public Sorting SortBy
		{
			get
			{
				if (sortBy == 0)
				{
					sortBy = Sorting.ByDate;
					string paramSort = Parameters.GetString("SortBy", String.Empty);
					if (!String.IsNullOrEmpty(paramSort))
					{
						try
						{
							sortBy = (Sorting)Enum.Parse(typeof(Sorting), paramSort);
						}
						catch { }
					}
				}

				return sortBy;
			}
		}
		public List<PostInfo> Posts
		{
			get { return posts; }
		}
		public ErrorCode FatalError
		{
			get
			{
				return fatalError;
			}
		}
		public Exception FatalException
		{
			get
			{
				return fatalException;
			}
		}
		public string BlogUrlTemplate
		{
			get
			{
				return Parameters.GetString("BlogUrlTemplate");
			}
			set
			{
				Parameters["BlogUrlTemplate"] = value;
			}
		}
		public string UserProfileUrlTemplate
		{
			get
			{
				return Parameters.GetString("UserProfileUrlTemplate");
			}
			set
			{
				Parameters["UserProfileUrlTemplate"] = value;
			}
		}
		public string PostViewUrlTemplate
		{
			get
			{
				return Parameters.GetString("PostViewUrlTemplate");
			}
			set
			{
				Parameters["PostViewUrlTemplate"] = value;
			}
		}
		public int MaxWordLength
		{
			get
			{
				return (maxWordLength != -1) ? maxWordLength : (maxWordLength = Math.Max(0, Parameters.GetInt("MaxWordLength", 15)));
			}
			set
			{
				maxWordLength = Math.Max(0, value);
				Parameters["MaxWordLength"] = maxWordLength.ToString();
			}
		}
		public int PeriodDays
		{
			get
			{
				return (periodDays != -1) ? periodDays : (periodDays = Math.Max(0, Parameters.GetInt("PeriodDays", 30)));
			}
			set
			{
				periodDays = Math.Max(0, value);
				Parameters["PeriodDays"] = periodDays.ToString();
			}
		}
		public BXBlogPostChain Processor
		{
			get { return processor; }
		}

		public event EventHandler<BXBlogCutTagEventArgs> RenderHideCut
		{
			add
			{
				Processor.RenderHideCut += value;
			}
			remove
			{
				Processor.RenderHideCut -= value;
			}
		}

		protected void Page_Load(object sender, EventArgs e)
		{
			fatalError = ErrorCode.None;

			try
			{
				if (!BXPrincipal.Current.IsCanOperate(BXBlog.Operations.BlogPublicView))
				{
					Fatal(ErrorCode.UnauthorizedViewBlog);
					return;
				}


                DateTime dt = DateTime.Now;
                dt = dt.AddMinutes(-(dt.Minute % 5)).AddSeconds(-dt.Second).AddMilliseconds(-dt.Millisecond);

				BXFilter postFilter = new BXFilter(
					new BXFilterItem(BXBlogPost.Fields.IsPublished, BXSqlFilterOperators.Equal, true),
					new BXFilterItem(BXBlogPost.Fields.DatePublished, BXSqlFilterOperators.LessOrEqual, dt),
					new BXFilterItem(BXBlogPost.Fields.Blog.Active, BXSqlFilterOperators.Equal, true),
					new BXFilterItem(BXBlogPost.Fields.Blog.Categories.Category.Sites.SiteId, BXSqlFilterOperators.Equal, DesignerSite)
				);

				if (PeriodDays > 0)
					postFilter.Add(new BXFilterItem(BXBlogPost.Fields.DatePublished, BXSqlFilterOperators.GreaterOrEqual, dt.AddDays(-PeriodDays)));

				if (SortBy == Sorting.ByViews)
					postFilter.Add(new BXFilterItem(BXBlogPost.Fields.ViewCount, BXSqlFilterOperators.Greater, 0));
				else if (SortBy == Sorting.ByComments)
					postFilter.Add(new BXFilterItem(BXBlogPost.Fields.CommentCount, BXSqlFilterOperators.Greater, 0));
				
				bool legal;
				BXPagingParams pagingParams = PreparePagingParams();
				BXQueryParams queryParams = PreparePaging(
					pagingParams,
					delegate()
					{
						return BXBlogPost.Count(postFilter);
					},
					new BXParamsBag<object>(),
					out legal
				);

				if (!legal)
				{
					Fatal(ErrorCode.FatalInvalidPage);
					return;
				}

				BXOrderBy postOrderBy = new BXOrderBy();
				if (SortBy == Sorting.ByViews)
					postOrderBy.Add(BXBlogPost.Fields.ViewCount, BXOrderByDirection.Desc);
				else if (SortBy == Sorting.ByComments)
					postOrderBy.Add(BXBlogPost.Fields.CommentCount, BXOrderByDirection.Desc);

				postOrderBy.Add(BXBlogPost.Fields.DatePublished, BXOrderByDirection.Desc);
				postOrderBy.Add(BXBlogPost.Fields.Id, BXOrderByDirection.Desc);

				BXBlogPostCollection postCollection = BXBlogPost.GetList(
					postFilter, 
					postOrderBy,
					new BXSelectAdd(BXBlogPost.Fields.Blog, BXBlogPost.Fields.Author, BXBlogPost.Fields.Author.User, BXBlogPost.Fields.Author.User.Image), 
					queryParams
				);

				BXParamsBag<object> replace = new BXParamsBag<object>();

				processor = new BXBlogPostChain();
				processor.MaxWordLength = MaxWordLength;

				foreach (BXBlogPost post in postCollection)
				{
					replace["PostYear"] = post.DatePublished.Year.ToString();
					replace["PostMonth"] = post.DatePublished.Month.ToString();
					replace["PostDay"] = post.DatePublished.Day.ToString();
					replace["PostId"] = post.Id;
					replace["UserId"] = post.AuthorId;
					replace["BlogId"] = post.Blog.Id;
					replace["BlogSlug"] = post.Blog.Slug;

					PostInfo postInfo = new PostInfo(this);
					postInfo.Post = post;
					postInfo.Auth = new BXBlogAuthorization(post.Blog, post);
					postInfo.AuthorNameHtml = BXWordBreakingProcessor.Break(post.TextEncoder.Decode(post.AuthorName), MaxWordLength, true);
					postInfo.TitleHtml = BXWordBreakingProcessor.Break(post.TextEncoder.Decode(post.Title), MaxWordLength, true);
					postInfo.BlogHref = Encode(ResolveTemplateUrl(BlogUrlTemplate, replace));
					postInfo.PostViewHref = Encode(ResolveTemplateUrl(PostViewUrlTemplate, replace));
					postInfo.UserProfileHref = Encode(ResolveTemplateUrl(UserProfileUrlTemplate, replace));
					posts.Add(postInfo);
				}

				if (!templateIncluded)
				{
					templateIncluded = true;
					IncludeComponentTemplate();
				}

				BXPublicPage bitrixPage = Page as BXPublicPage;
				if (bitrixPage != null && !IsComponentDesignMode && Parameters.Get<bool>("SetPageTitle", true))
				{
					string title;
					if (SortBy == Sorting.ByViews)
						title = GetMessageRaw("PageTitle.MostViewed");
					else if (SortBy == Sorting.ByComments)
						title = GetMessageRaw("PageTitle.MostCommented");
					else
						title = GetMessageRaw("PageTitle.NewPosts");

					bitrixPage.BXTitle = title;
				}
			}
			catch (Exception ex)
			{
				Fatal(ex);
			}
		}
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);

			foreach (string param in new string[] { Parameters.GetString("ThemeCssFilePath"), Parameters.GetString("ColorCssFilePath") })
			{
				if (BXStringUtility.IsNullOrTrimEmpty(param))
					continue;
				string path = param;
				try
				{
					path = BXPath.ToVirtualRelativePath(path);
					if (BXSecureIO.FileExists(path))
						BXPage.RegisterStyle(path);
				}
				catch
				{
				}
			}
		}
		protected override void PreLoadComponentDefinition()
		{
			Title = GetMessageRaw("Title");
			Description = GetMessageRaw("Description");
			Group = BXBlogModuleConfiguration.GetComponentGroup();

			BXCategory mainCategory = BXCategory.Main;
			BXCategory urlCategory = BXCategory.UrlSettings;
			BXCategory additionalSettingsCategory = BXCategory.AdditionalSettings;

			ParamsDefinition["ThemeCssFilePath"] = new BXParamText(GetMessageRaw("Param.ThemeCssFilePath"), "~/bitrix/components/bitrix/blog/templates/.default/style.css", mainCategory);
			ParamsDefinition["ColorCssFilePath"] = new BXParamText(GetMessageRaw("Param.ColorCssFilePath"), "~/bitrix/components/bitrix/blog/templates/.default/themes/default/style.css", mainCategory);
			ParamsDefinition["SortBy"] = new BXParamSingleSelection(GetMessageRaw("Param.SortBy"), Sorting.ByDate.ToString(), mainCategory);
			ParamsDefinition["PeriodDays"] = new BXParamText(GetMessageRaw("Param.PeriodDays"), "30", mainCategory);

			ParamsDefinition["BlogUrlTemplate"] = new BXParamText(GetMessageRaw("Param.BlogUrlTemplate"), "Blog.aspx?blog=#blogSlug#", urlCategory);
			ParamsDefinition["UserProfileUrlTemplate"] = new BXParamText(GetMessageRaw("Param.UserProfileUrlTemplate"), "User.aspx?user=#userId#", urlCategory);
			ParamsDefinition["PostViewUrlTemplate"] = new BXParamText(GetMessageRaw("Param.PostViewUrlTemplate"), "Post.aspx?post=#postId#", urlCategory);

			ParamsDefinition.Add("MaxWordLength", new BXParamText(GetMessageRaw("Param.MaxWordLength"), "15", additionalSettingsCategory));
			ParamsDefinition["SetPageTitle"] = new BXParamYesNo(GetMessageRaw("Param.SetPageTitle"), true, additionalSettingsCategory);

			BXParametersDefinition.SetPaging(ParamsDefinition, ClientID, BXParametersDefinition.PagingParamsOptions.DisableLoad | BXParametersDefinition.PagingParamsOptions.DisableShowAll);
			BXParametersDefinition.SetPagingUrl(ParamsDefinition, ClientID, BXParametersDefinition.PagingParamsOptions.DisableLoad | BXParametersDefinition.PagingParamsOptions.DisableShowAll);
		}
		protected override void LoadComponentDefinition()
		{
			ParamsDefinition["SortBy"].Values.Add(new BXParamValue(GetMessageRaw("Param.SortBy.Date"), Sorting.ByDate.ToString()));
			ParamsDefinition["SortBy"].Values.Add(new BXParamValue(GetMessageRaw("Param.SortBy.Views"), Sorting.ByViews.ToString()));
			ParamsDefinition["SortBy"].Values.Add(new BXParamValue(GetMessageRaw("Param.SortBy.Comments"), Sorting.ByComments.ToString()));

			BXParametersDefinition.SetPaging(ParamsDefinition, ClientID, BXParametersDefinition.PagingParamsOptions.DisablePreLoad | BXParametersDefinition.PagingParamsOptions.DisableShowAll);
			BXParametersDefinition.SetPagingUrl(ParamsDefinition, ClientID, BXParametersDefinition.PagingParamsOptions.DisablePreLoad | BXParametersDefinition.PagingParamsOptions.DisableShowAll);
		}

		private void Fatal(ErrorCode code)
		{
			if (code == ErrorCode.FatalException)
				throw new InvalidOperationException("Use method with Exception argument");
			fatalError = code;
			if (!templateIncluded)
			{
				templateIncluded = true;
				IncludeComponentTemplate();
			}
		}
		private void Fatal(Exception ex)
		{
			if (ex == null)
				throw new ArgumentNullException("ex");

			fatalError = ErrorCode.FatalException;
			fatalException = ex;
			if (!templateIncluded)
			{
				templateIncluded = true;
				IncludeComponentTemplate();
			}
		}

		public string GetErrorHtml(ErrorCode code)
		{
			switch (code)
			{
				case ErrorCode.FatalException:
					return BXPrincipal.Current.IsCanOperate(BXRoleOperation.Operations.SystemMaintenance) ? ("<pre>" + Encode(fatalException.ToString()) + "</pre>") : GetMessage("Error.Unknown");
				case ErrorCode.UnauthorizedViewBlog:
					return GetMessage("Error.UnauthorizedViewBlog");
				case ErrorCode.FatalInvalidPage:
					return GetMessage("Error.FatalInvalidPage");
				default:
					return GetMessage("Error.Unknown");
			}
		}

		//NESTED CLASSES
		public enum Sorting
		{
			ByDate = 1,
			ByViews,
			ByComments
		}
		public class PostInfo
		{
			private BXBlogPost post;
			private string postViewHref;
			private string userProfileHref;
			private string blogHref;
			private string authorNameHtml;
			private string titleHtml;
			private BlogPostsComponent component;
			private BXBlogAuthorization auth;

			public BXBlogPost Post
			{
				get
				{
					return post;
				}
				internal set
				{
					post = value;
				}
			}
			public string PostViewHref
			{
				get { return postViewHref; }
				internal set { postViewHref = value; }
			}
			public string UserProfileHref
			{
				get { return userProfileHref; }
				internal set { userProfileHref = value; }
			}
			public string BlogHref
			{
				get { return blogHref; }
				internal set { blogHref = value; }
			}
			public string AuthorNameHtml
			{
				get { return authorNameHtml; }
				internal set { authorNameHtml = value; }
			}
			public string TitleHtml
			{
				get { return titleHtml; }
				internal set { titleHtml = value; }
			}
			public string GetContentHtml()
			{
				return component.Processor.Process(post.Content);
			}
			public string GetPreviewHtml(int length)
			{
				string preview = component.Processor.StripBBCode(post.Content);
				preview = BXStringUtility.StripOffSimpleTags(preview);

				if (length > 0 && preview.Length > length)
					preview = preview.Substring(0, length) + " ...";

				return BXWordBreakingProcessor.Break(preview, component.MaxWordLength, true);
			}

			public BXBlogAuthorization Auth
			{
				get { return auth; }
				internal set { auth = value; }
			}

			internal PostInfo(BlogPostsComponent component)
			{
				this.component = component;
			}
		}
		public enum ErrorCode
		{
			None = 0,
			Fatal = 1,
			Unauthorized = 2,

			FatalInvalidPage = Fatal | 4,
			FatalException = Fatal | 8,
			FatalComponentNotExecuted = Fatal | 16,

			UnauthorizedViewBlog = Unauthorized | 4,
		}
	}

	public class BlogPostsTemplate : BXComponentTemplate<BlogPostsComponent>
	{
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);
			if ((Component.FatalError & BlogPostsComponent.ErrorCode.Fatal) != 0)
				BXError404Manager.Set404Status(Response);
		}
		protected override void Render(HtmlTextWriter writer)
		{
			StartWidth = "100%";
			base.Render(writer);
		}
	}
}
