using System;
using System.Collections.Generic;
using System.IO;
using System.Web.UI;

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;

namespace Bitrix.Blog.Components
{
	public partial class BlogPostFormComponent : BXComponent
	{
		private string blogSlug;
		private int? postId;
		private Mode componentMode;
		private readonly List<ValidationMessageCode> validationErrors = new List<ValidationMessageCode>();
		private ErrorCode fatalError = ErrorCode.FatalComponentNotExecuted;
		private Exception fatalException;
		private bool templateIncluded;
		private BXBlog blog;
		private BXBlogPost post;
		private BXBlogPostChain processor;
		private ComponentData data;
		private int maxWordLength = -1;
		private BXBlogAuthorization auth;

		public ErrorCode FatalError
		{
			get
			{
				return fatalError;
			}
		}
		public Exception FatalException
		{
			get
			{
				return fatalException;
			}
		}
		public string BlogSlug
		{
			get
			{
				return blogSlug ?? (blogSlug = Parameters.GetString("BlogSlug"));
			}
		}
		public int PostId
		{
			get
			{
				return (postId ?? (postId = Parameters.GetInt("PostId"))).Value;
			}
		}
		public BXBlog Blog
		{
			get
			{
				return blog;
			}
		}
		public BXBlogPost Post
		{
			get
			{
				return post;
			}
		}
		public ComponentData Data
		{
			get
			{
				return data;
			}
		}
		public List<ValidationMessageCode> ValidationErrors
		{
			get
			{
				return validationErrors;
			}
		}
		internal BXBlogPostChain Processor
		{
			get
			{
				if (processor == null)
				{
					processor = new BXBlogPostChain(blog);
					processor.MaxWordLength = MaxWordLength;
					processor.HideCut = false;
				}
				return processor;
			}
		}
		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 SetPageTitle
		{
			get
			{
				return Parameters.GetBool("SetPageTitle", true);
			}
			set
			{
				Parameters["SetPageTitle"] = value.ToString();
			}
		}
		public BXBlogAuthorization Auth
		{
			get
			{
				return auth;
			}
		}
		public Mode ComponentMode
		{
			get
			{
				return componentMode;
			}
		}


		public bool Validate()
		{
			if (fatalError != ErrorCode.None)
				return false;

			data.Title = data.Title != null ? data.Title.Trim() : null;
			if (string.IsNullOrEmpty(data.Title))
				validationErrors.Add(ValidationMessageCode.TitleIsRequired);

			data.Content = Processor.CorrectBBCode(data.Content ?? "");

			return validationErrors.Count == 0;
		}
		public void Save()
		{
			if (fatalError != ErrorCode.None)
				return;

			try
			{
				if (data.IsNew)
				{
					post = new BXBlogPost(BXTextEncoder.EmptyTextEncoder);
					post.BlogId = blog.Id;
					post.DateCreated = DateTime.Now;
					post.AuthorId = BXIdentity.Current.Id;
					post.AuthorIP = Request.UserHostAddress;
				}
				post.Title = data.Title;
				post.Content = data.Content;
				post.IsPublished = data.IsPublished;
				post.DatePublished = (data.DatePublished != DateTime.MinValue || !data.IsPublished) ? data.DatePublished : DateTime.Now;

				post.Save();

				data = new ComponentData(post);
			}
			catch (Exception ex)
			{
				Fatal(ex);
				return;
			}

			string redirectUrl = Parameters.GetString(data.IsPublished ? "PublishUrlTemplate" : "DraftUrlTemplate");
			if (BXStringUtility.IsNullOrTrimEmpty(redirectUrl))
				redirectUrl = Parameters.GetString("RedirectUrlTemplate");
			if (BXStringUtility.IsNullOrTrimEmpty(redirectUrl))
				Response.Redirect(BXSefUrlManager.CurrentUrl.ToString());
			RedirectTemplateUrl(redirectUrl, GetReplaceParameters());
		}
		public void Preview(string input, HtmlTextWriter writer)
		{
			Processor.Process(input, writer);
		}
		public string Preview(string input)
		{
			using (StringWriter s = new StringWriter())
			{
				Processor.Process(input, s);
				return s.ToString();
			}
		}
		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.FatalPostNotFound:
					return GetMessage("Error.PostNotFound");
				case ErrorCode.FatalBlogNotFound:
					return GetMessage("Error.BlogNotFound");
				case ErrorCode.FatalBlogIsNotActive:
					return GetMessage("Error.BlogIsDisabled");
				case ErrorCode.UnauthorizedCreatePost:
					return GetMessage("Error.UnauthorizedCreate");
				case ErrorCode.UnauthorizedEditPost:
					return GetMessage("Blog.UnauthorizedEdit");
				default:
					return GetMessage("Error.Unknown");
			}
		}
		public string GetValidationMessageHtml(ValidationMessageCode code)
		{
			switch (code)
			{
				case ValidationMessageCode.TitleIsRequired:
					return GetMessage("Error.TitleIsNotSpecified");
				default:
					return null;
			}
		}

		private BXParamsBag<object> GetReplaceParameters()
		{
			BXParamsBag<object> replace = new BXParamsBag<object>();
			replace["BlogId"] = blog.Id;
			replace["BlogSlug"] = blog.Slug;
			replace["PostId"] = post.Id;
			DateTime date = post.DatePublished;
			replace["PostYear"] = date.Year;
			replace["PostMonth"] = date.Month;
			replace["PostDay"] = date.Day;
			replace["UserId"] = post.AuthorId;
			return replace;
		}
		private bool CheckBlog()
		{
			if (componentMode == Mode.Add && auth.CanCreatePost || componentMode == Mode.Edit && auth.CanEditThisPost)
				return true;

			if (!blog.Active)
				Fatal(ErrorCode.FatalBlogIsNotActive);
			else
				Fatal(componentMode == Mode.Add ? ErrorCode.UnauthorizedCreatePost : ErrorCode.UnauthorizedEditPost);
			return false;
		}
		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();
			}
		}

		protected void Page_Load(object sender, EventArgs e)
		{
			fatalError = ErrorCode.None;
			try
			{
				componentMode = PostId == 0 ? Mode.Add : Mode.Edit;
				if (componentMode == Mode.Edit)
				{
					BXBlogPostCollection posts = BXBlogPost.GetList(
						new BXFilter(
							new BXFilterItem(BXBlogPost.Fields.Id, BXSqlFilterOperators.Equal, PostId),
							new BXFilterItem(BXBlogPost.Fields.Blog.Categories.Category.Sites.SiteId, BXSqlFilterOperators.Equal, DesignerSite)
						),
						null,
						new BXSelectAdd(BXBlogPost.Fields.Blog),
						null
					);

					if (posts.Count > 0)
						post = posts[0];

					if (post == null)
					{
						Fatal(ErrorCode.FatalPostNotFound);
						return;
					}

					blog = post.Blog;
					if (blog == null || !string.IsNullOrEmpty(BlogSlug) && blog.Slug != BlogSlug)
					{
						Fatal(ErrorCode.FatalPostNotFound);
						return;
					}

					auth = new BXBlogAuthorization(blog, post);

					if (!CheckBlog())
						return;
				}
				else
				{
					BXBlogCollection blogs = BXBlog.GetList(
						new BXFilter(
							new BXFilterItem(BXBlog.Fields.Slug, BXSqlFilterOperators.Equal, BlogSlug),
							new BXFilterItem(BXBlog.Fields.Categories.Category.Sites.SiteId, BXSqlFilterOperators.Equal, DesignerSite)
						),
						null
					);

					if (blogs.Count > 0)
						blog = blogs[0];

					if (blog == null)
					{
						Fatal(ErrorCode.FatalBlogNotFound);
						return;
					}

					auth = new BXBlogAuthorization(blog);

					if (!CheckBlog())
						return;
				}

				data = new ComponentData(post);


				//Set page title
				BXPublicPage bitrixPage = Page as BXPublicPage;
				if (bitrixPage != null && !IsComponentDesignMode && SetPageTitle)
				{
					bitrixPage.Title = bitrixPage.MasterTitleHtml = Encode(
						componentMode == Mode.Add
						? GetMessageRaw("PageTitle.CreatePost")
						: GetMessageRaw("PageTitle.EditPost")
					);
				}
			}
			catch (Exception ex)
			{
				Fatal(ex);
				return;
			}
			if (!templateIncluded)
			{
				templateIncluded = true;
				IncludeComponentTemplate();
			}
		}
		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");
			Icon = "images/icon.gif";
			Group = BXBlogModuleConfiguration.GetComponentGroup();

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

			ParamsDefinition.Add("ThemeCssFilePath", new BXParamText(GetMessageRaw("Param.ThemeCssFilePath"), "~/bitrix/components/bitrix/blog/templates/.default/style.css", mainCategory));
			ParamsDefinition.Add("ColorCssFilePath", new BXParamText(GetMessageRaw("Param.ColorCssFilePath"), "~/bitrix/components/bitrix/blog/templates/.default/themes/default/style.css", mainCategory));

			ParamsDefinition.Add("BlogSlug", new BXParamText(GetMessageRaw("Param.BlogSlug"), "", mainCategory));
			ParamsDefinition.Add("PostId", new BXParamText(GetMessageRaw("Param.PostId"), "", mainCategory));

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

			ParamsDefinition.Add("PublishUrlTemplate", new BXParamText(GetMessageRaw("Param.PublishUrlTemplate"), "", urlCategory));
			ParamsDefinition.Add("DraftUrlTemplate", new BXParamText(GetMessageRaw("Param.DraftUrlTemplate"), "", urlCategory));
			ParamsDefinition.Add("RedirectUrlTemplate", new BXParamText(GetMessageRaw("Param.RedirectUrlTemplate"), "", urlCategory));
		}

		public enum Mode
		{
			Add,
			Edit
		}
		public enum ErrorCode
		{
			None = 0,
			Fatal = 1,
			Unauthorized = 2,
			FatalComponentNotExecuted = Fatal | (0 << 2),
			FatalException = Fatal | (1 << 2),
			FatalBlogNotFound = Fatal | (2 << 2),
			FatalPostNotFound = Fatal | (3 << 2),
			FatalBlogIsNotActive = Fatal | (4 << 2),
			UnauthorizedCreatePost = Unauthorized | (0 << 2),
			UnauthorizedEditPost = Unauthorized | (1 << 2)
		}
		public enum ValidationMessageCode
		{
			None,
			TitleIsRequired
		}
		public class ComponentData
		{
			private string title;
			private string content;
			private DateTime datePublished;
			private readonly bool isNew;
			private bool isPublished;

			internal ComponentData(BXBlogPost post)
			{
				if (post == null)
				{
					isNew = true;
					datePublished = DateTime.Now;
					return;
				}

				isPublished = post.IsPublished;
				title = post.TextEncoder.Decode(post.Title);
				content = post.Content;
				datePublished = post.DatePublished;
			}

			public bool IsPublished
			{
				get
				{
					return isPublished;
				}
				set
				{
					isPublished = value;
				}
			}
			public bool IsNew
			{
				get
				{
					return isNew;
				}
			}
			public string Title
			{
				get
				{
					return title;
				}
				set
				{
					title = value;
				}
			}
			public string Content
			{
				get
				{
					return content;
				}
				set
				{
					content = value;
				}
			}
			public DateTime DatePublished
			{
				get
				{
					return datePublished;
				}
				set
				{
					datePublished = value;
				}
			}
		}
	}

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

