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 BlogPostListComponent : BXComponent
	{
		private BXBlogAuthorization auth;
		private BXBlog blog;
		private string blogSlug;
		private List<PostInfo> posts = new List<PostInfo>();
		private bool templateIncluded;
		private PublishMode publishMode;
		private ErrorCode fatalError = ErrorCode.FatalComponentNotExecuted;
		private Exception fatalException;
		private BXParamsBag<object> replace;
		private int maxWordLength = -1;
		private BXBlogPostChain processor;
		private bool isFilterSet = false;

		public BXBlogAuthorization Auth
		{
			get { return auth; }
		}
		public BXBlog Blog
		{
			get { return blog; }
		}
		public string BlogSlug
		{
			get
			{
				return (blogSlug ?? (blogSlug = Parameters.GetString("BlogSlug")));
			}
		}
		public List<PostInfo> Posts
		{
			get { return posts; }
		}
		public PublishMode PostPublishMode
		{
			get 
			{
				if (publishMode == 0)
				{
					publishMode = PublishMode.Published;
					string paramMode = Parameters.GetString("PublishMode", String.Empty);
					if (!String.IsNullOrEmpty(paramMode))
					{
						try
						{
							publishMode = (PublishMode)Enum.Parse(typeof(PublishMode), paramMode);
						}
						catch { }
					}
				}

				return publishMode; 
			}
		}
		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 string PostEditUrlTemplate
		{
			get
			{
				return Parameters.GetString("PostEditUrlTemplate");
			}
			set
			{
				Parameters["PostEditUrlTemplate"] = 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 bool IsOwner
		{
			get { return blog != null && blog.OwnerId == BXIdentity.Current.Id; }
		}
		public bool IsFilterSet
		{
			get { return isFilterSet; }
		}
		public int FilterYear
		{
			get 
			{
				return Parameters.GetInt("FilterByYear", 0);
			}
		}
		public int FilterMonth
		{
			get 
			{
				return Parameters.GetInt("FilterByMonth", 0);
			}
		}
		public int FilterDay
		{
			get 
			{
				return Parameters.GetInt("FilterByDay", 0);
			}
		}
		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 (BXStringUtility.IsNullOrTrimEmpty(BlogSlug) || !BXBlog.SlugRegex.IsMatch(BlogSlug))
				{
					Fatal(ErrorCode.FatalBlogNotFound);
					return;
				}

				BXFilter blogFilter = new BXFilter(
					new BXFilterItem(BXBlog.Fields.Slug, BXSqlFilterOperators.Equal, BlogSlug),
					new BXFilterItem(BXBlog.Fields.Categories.Category.Sites.SiteId, BXSqlFilterOperators.Equal, DesignerSite)
				);

				BXBlogCollection blogCollection = BXBlog.GetList(blogFilter, null, new BXSelectAdd(BXBlog.Fields.Owner.User, BXBlog.Fields.Owner.User.Image), null);
				if (blogCollection.Count == 0)
				{
					Fatal(ErrorCode.FatalBlogNotFound);
					return;
				}

				blog = blogCollection[0];
				auth = new BXBlogAuthorization(blog);

				if (!auth.CanReadThisBlog)
				{
					Fatal(ErrorCode.UnauthorizedViewBlog);
					return;
				}

				replace = new BXParamsBag<object>();
				replace["BlogId"] = blog.Id;
				replace["BlogSlug"] = blog.Slug;

				processor = new BXBlogPostChain(blog);
				processor.MaxWordLength = MaxWordLength;
				processor.HideCut = true;

				BXFilter postFilter = new BXFilter(new BXFilterItem(BXBlogPost.Fields.Blog.Id, BXSqlFilterOperators.Equal, blog.Id));
				
				DateTime from = new DateTime();
				DateTime to = new DateTime();
				if (Parameters.GetBool("FilterByDate", false))
				{
					try
					{
						if (FilterYear > 0 && FilterMonth > 0 && FilterDay > 0)
						{
							from = new DateTime(FilterYear, FilterMonth, FilterDay);
							to = from.AddDays(1);
							isFilterSet = true;
						}
						else if (FilterYear > 0 && FilterMonth > 0)
						{
							from = new DateTime(FilterYear, FilterMonth, 1);
							to = from.AddMonths(1);
							isFilterSet = true;
						}
						else if (FilterYear > 0)
						{
							from = new DateTime(FilterYear, 1, 1);
							to = from.AddYears(1);
							isFilterSet = true;
						}

						if (to > DateTime.Now && PostPublishMode == PublishMode.Published)
							to = DateTime.Now;
					}
					catch
					{
					}
				}

				if (isFilterSet)
				{
					postFilter.Add(new BXFilterItem(BXBlogPost.Fields.DatePublished, BXSqlFilterOperators.GreaterOrEqual, from));
					postFilter.Add(new BXFilterItem(BXBlogPost.Fields.DatePublished, BXSqlFilterOperators.Less, to));
				}

				if (PostPublishMode == PublishMode.Draft)
					postFilter.Add(new BXFilterOr(
						new BXFilterItem(BXBlogPost.Fields.IsPublished, BXSqlFilterOperators.Equal, false),
						new BXFilterItem(BXBlogPost.Fields.DatePublished, BXSqlFilterOperators.Greater, DateTime.Now)
					));
				else if (PostPublishMode == PublishMode.Published)
				{
					postFilter.Add(new BXFilterItem(BXBlogPost.Fields.IsPublished, BXSqlFilterOperators.Equal, true));
					if (!isFilterSet)
						postFilter.Add(new BXFilterItem(BXBlogPost.Fields.DatePublished, BXSqlFilterOperators.LessOrEqual, DateTime.Now));
				}

				bool legal;
				BXPagingParams pagingParams = PreparePagingParams();
				BXQueryParams queryParams = PreparePaging(
					pagingParams,
					delegate()
					{
						return BXBlogPost.Count(postFilter);
					},
					replace,
					out legal
				);

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

				BXOrderBy postOrderBy = new BXOrderBy(
					new BXOrderByPair(BXBlogPost.Fields.DatePublished, BXOrderByDirection.Desc),
					new BXOrderByPair(BXBlogPost.Fields.Id, BXOrderByDirection.Desc)
				);

				BXBlogPostCollection postCollection = BXBlogPost.GetList(
					postFilter, 
					postOrderBy,
					new BXSelectAdd(BXBlogPost.Fields.Author, BXBlogPost.Fields.Author.User, BXBlogPost.Fields.Author.User.Image), 
					queryParams
				);
				foreach (BXBlogPost post in postCollection)
				{
					PostInfo postInfo = new PostInfo(this);

					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;

					postInfo.Post = post;

					BXBlogAuthorization postAuth = new BXBlogAuthorization(blog, post);
					postInfo.Auth = postAuth;
					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));
					
					if (postAuth.CanEditThisPost)
						postInfo.PostEditHref = Encode(ResolveTemplateUrl(PostEditUrlTemplate, 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))
				{
					if (PostPublishMode == PublishMode.Draft)
					{
						bitrixPage.Title = GetMessage("PageTitle.Drafts");
					}
					else
					{
						string title = blog.TextEncoder.Decode(blog.Name);
						bitrixPage.MasterTitleHtml = BXWordBreakingProcessor.Break(title, MaxWordLength, true);
						bitrixPage.Title = Encode(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["BlogSlug"] = new BXParamText(GetMessageRaw("Param.BlogSlug"), "", mainCategory);
			ParamsDefinition["PublishMode"] = new BXParamSingleSelection(GetMessageRaw("Param.PublishMode"), PublishMode.Published.ToString(), mainCategory);

			string clientSideActionGroupViewId = ClientID;
			ParamsDefinition["FilterByDate"] = new BXParamYesNo(GetMessageRaw("Param.FilterByDate"), false, mainCategory, new ParamClientSideActionGroupViewSwitch(clientSideActionGroupViewId, "FilterByDate", "Filter", "NonFilter"));
			ParamsDefinition["FilterByYear"] = new BXParamSingleSelection(GetMessageRaw("Param.FilterByDate.Year"), String.Empty, mainCategory, null, new ParamClientSideActionGroupViewMember(clientSideActionGroupViewId, "FilterByYear", new string[] { "Filter" }));
			ParamsDefinition["FilterByMonth"] = new BXParamSingleSelection(GetMessageRaw("Param.FilterByDate.Month"), String.Empty, mainCategory, null, new ParamClientSideActionGroupViewMember(clientSideActionGroupViewId, "FilterByMonth", new string[] { "Filter" }));
			ParamsDefinition["FilterByDay"] = new BXParamSingleSelection(GetMessageRaw("Param.FilterByDate.Day"), String.Empty, mainCategory, null, new ParamClientSideActionGroupViewMember(clientSideActionGroupViewId, "FilterByDay", new string[] { "Filter" }));

			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["PostEditUrlTemplate"] = new BXParamText(GetMessageRaw("Param.PostEditUrlTemplate"), "PostEdit.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["PublishMode"].Values.Add(new BXParamValue(GetMessageRaw("Param.PublishMode.Published"), PublishMode.Published.ToString()));
			ParamsDefinition["PublishMode"].Values.Add(new BXParamValue(GetMessageRaw("Param.PublishMode.Draft"), PublishMode.Draft.ToString()));
			ParamsDefinition["PublishMode"].Values.Add(new BXParamValue(GetMessageRaw("Param.PublishMode.All"), PublishMode.All.ToString()));

			ParamsDefinition["FilterByYear"].Values.Add(new BXParamValue(GetMessageRaw("Option.SelectYear"), String.Empty));
			for (int i = 2008; i <= DateTime.Now.Year + 1; i++)
				ParamsDefinition["FilterByYear"].Values.Add(new BXParamValue(i.ToString(), i.ToString()));


			CultureInfo ci = CultureInfo.CurrentCulture;
			DateTimeFormatInfo dtf = ci.DateTimeFormat;
			ParamsDefinition["FilterByMonth"].Values.Add(new BXParamValue(GetMessageRaw("Option.NotSelected"), String.Empty));
			for (int i = 0; i < 12; i++)
				ParamsDefinition["FilterByMonth"].Values.Add(new BXParamValue(dtf.MonthNames[i], (i+1).ToString()));

			ParamsDefinition["FilterByDay"].Values.Add(new BXParamValue(GetMessageRaw("Option.NotSelected"), String.Empty));
			for (int i = 1; i <= 31; i++)
				ParamsDefinition["FilterByDay"].Values.Add(new BXParamValue(i.ToString(), i.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.FatalBlogNotFound:
					return GetMessage("Error.FatalBlogNotFound");
				case ErrorCode.FatalInvalidPage:
					return GetMessage("Error.FatalInvalidPage");
				case ErrorCode.UnauthorizedViewBlog:
					return GetMessage("Error.UnauthorizedViewBlog");
				case ErrorCode.UnauthorizedDeletePost:
					return GetMessage("Error.UnauthorizedDeletePost");
				default:
					return GetMessage("Error.Unknown");
			}
		}
		public void DeletePost(IEnumerable<int> ids)
		{
			if (fatalError != ErrorCode.None)
				return;

			try
			{
				BXPrincipal user = BXPrincipal.Current;
				int userId = BXIdentity.Current.Id;

				BXFilter filter = new BXFilter();
				filter.Add(new BXFilterItem(BXBlogPost.Fields.Id, BXSqlFilterOperators.In, ids));
				filter.Add(new BXFilterItem(BXBlogPost.Fields.Blog.Id, BXSqlFilterOperators.Equal, blog.Id));

				BXBlogPostCollection posts = BXBlogPost.GetList(filter, null);
				foreach (BXBlogPost post in posts)
				{
					if (!new BXBlogAuthorization(blog, post).CanDeleteThisPost)
					{
						Fatal(ErrorCode.UnauthorizedDeletePost);
						return;
					}
					post.Delete();
				}
			}
			catch (ThreadAbortException)
			{
				return;
			}
			catch (Exception ex)
			{
				Fatal(ex);
				return;
			}

			RedirectTemplateUrl(BlogUrlTemplate, replace);
		}

		//NESTED CLASSES
		public class PostInfo
		{
			private BXBlogPost post;
			private string postViewHref;
			private string postEditHref;
			private string userProfileHref;
			private string blogHref;
			private string authorNameHtml;
			private string titleHtml;
			private BlogPostListComponent 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 PostEditHref
			{
				get { return postEditHref; }
				internal set { postEditHref = 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 BXBlogAuthorization Auth
			{
				get { return auth; }
				internal set { auth = value; }
			}

			public string GetContentHtml()
			{
				return component.Processor.Process(post.Content);
			}

			internal PostInfo(BlogPostListComponent component)
			{
				this.component = component;
			}
		}
		public enum PublishMode
		{
			All = 1,
			Draft,
			Published
		}
		public enum ErrorCode
		{
			None = 0,
			Fatal = 1,
			Unauthorized = 2,

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

			UnauthorizedViewBlog = Unauthorized | 4,
			UnauthorizedDeletePost = Unauthorized | 8
		}
	}

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