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

using System;
using System.Collections;

using CommunityServer.Components;

namespace CommunityServer.Discussions.Components {

    /// <summary>
    /// Summary description for ForumPermission.
    /// </summary>
	public class ForumPermission : PermissionBase {

		public ForumPermission() : base() {
		}

		public ForumPermission(  int site, Guid role, string name, Permission allow, Permission deny, bool impliedPermissions ) 
			: base( site, role, name, allow, deny, impliedPermissions ) {
		}


		#region Public Properites - Permissions
		public virtual bool View {
			get{ return GetBit( Permission.View ); }
		}

		public virtual bool Read {
			get{ return GetBit( Permission.Read ); }
		}

		public virtual bool Post {
			get{ return GetBit( Permission.Post ); }
		}

		public virtual bool Reply {
			get{ return GetBit( Permission.Reply ); }
		}
        
		public virtual bool Edit {
			get{ return GetBit( Permission.Edit ); }
		}
        
		public virtual bool Delete {
			get{ return GetBit( Permission.Delete ); }
		}
        
		public virtual bool LocalAttachment {
			get{ return GetBit( Permission.LocalAttachment ); }
		}

		public virtual bool RemoteAttachment 
		{
			get{ return GetBit( Permission.RemoteAttachment ); }
		}
        
		public virtual bool Vote {
			get{ return GetBit( Permission.Vote ); }
		}

		public virtual bool Ink 
		{
			get{ return GetBit( Permission.Ink ); }
		}

		public virtual bool Video 
		{
			get{ return GetBit( Permission.Video ); }
		}

		public virtual bool CreatePoll 
		{
			get{ return GetBit( Permission.CreatePoll ); }
		}

		public virtual bool MarkAsAnswer
		{
			get { return GetBit(Permission.MarkAsAnswer); }
		}

		public virtual bool Sticky {
			get{ return GetBit( Permission.Sticky ); }
		}
        
		public virtual bool Announcement {
			get{ return GetBit( Permission.Announcement ); }
		}

		public virtual bool EditOthers {
			get{ return GetBit( Permission.EditOthers ); }
		}

		public virtual bool Moderate {
			get{ return GetBit( Permission.Moderate ); }
		}

		public virtual bool Administer {
			get{ return GetBit( Permission.Administer ); }
		}
		
		public virtual bool SystemAdminister {
			get{ return GetBit( Permission.SystemAdmin ); }
		}
		#endregion

		public static bool Validate(Section section, Permission permission, User user, Post p) {
			if(section == null || section.PermissionSet == null || user == null )
				return false;

            if(user.IsForumAdministrator)
                return true;

            ForumPermission fpFinal;
            bool bReturn = true;

            // TODO: Properly handle permissions check on subforums.
            // Note: This is a temporary solution.
            //
            // Do we have a subforum?
            //
            if (section.ParentID > 0)
            {
                // Get the parent section/forum and verify the credentials against it.
                //
                Section parentSection = Forums.GetTopmostParentSection( section.ParentID );
                if (parentSection == null)
                    return false;

                fpFinal = parentSection.ResolvePermission(user) as ForumPermission;
            }
            else
            {
                fpFinal = section.ResolvePermission(user) as ForumPermission;
            }
            
            /* Old approach
			bool bReturn = true;
			ForumPermission fpFinal = section.ResolvePermission(user) as ForumPermission;
            */

            if(fpFinal.Moderate)
                return true;

			if((permission == (permission | Permission.MarkAsAnswer)) && bReturn )
				bReturn &= fpFinal.MarkAsAnswer;

			if((permission == (permission | Permission.Administer)) && bReturn )
				bReturn &= fpFinal.Administer;

			if((permission == (permission | Permission.Announcement)) && bReturn ) 
				bReturn &= fpFinal.Announcement;

			if((permission == (permission | Permission.RemoteAttachment)) && bReturn ) 
				bReturn &= fpFinal.RemoteAttachment;

			if((permission == (permission | Permission.LocalAttachment)) && bReturn )
				bReturn &= fpFinal.LocalAttachment;

			if((permission == (permission | Permission.CreatePoll)) && bReturn )
				bReturn &= fpFinal.CreatePoll;
            
			if((permission == (permission | Permission.Delete)) && bReturn ) 
				bReturn &= fpFinal.Delete;

			if((permission == (permission | Permission.Edit)) && bReturn )
				bReturn &= fpFinal.Edit;

			if((permission == (permission | Permission.EditOthers)) && bReturn )
				bReturn &= fpFinal.EditOthers;

			if((permission == (permission | Permission.Moderate)) && bReturn )
				bReturn &= fpFinal.Moderate;

			if((permission == (permission | Permission.Post)) && bReturn )
				bReturn &= fpFinal.Post;

			if((permission == (permission | Permission.Read)) && bReturn )
				bReturn &= fpFinal.Read;

			if((permission == (permission | Permission.Reply)) && bReturn )
				bReturn &= fpFinal.Reply;

			if((permission == (permission | Permission.Sticky)) && bReturn )
				bReturn &= fpFinal.Sticky;

			if((permission == (permission | Permission.View)) && bReturn )
				bReturn &= fpFinal.View;

			if((permission == (permission | Permission.Vote)) && bReturn )
				bReturn &= fpFinal.Vote;

			if((permission == (permission | Permission.Ink)) && bReturn )
				bReturn &= fpFinal.Ink;

			if((permission == (permission | Permission.Video)) && bReturn )
				bReturn &= fpFinal.Video;

    		return bReturn;
		}

		#region AccessCheck



		public static void AccessCheck(Section section, Permission permission, User user, Post post) {
			CSContext csContext = CSContext.Current;

			if (user.IsForumAdministrator)
				return;

			/*
			 * Not sure when this was commented out, but it seems
			 * this was done to lower the overhead of making a trip
			 * to the DP.  Needs to check against the forum permission instead.
			 * 
			// Moderators can delete posts
			//
			if ( (CSContext.Current.User.IsModerator) && (Moderate.CheckIfUserIsModerator(CSContext.Current.User.UserID, post.ForumID)) )
				return;

			*/

			ForumPermission fp = section.ResolvePermission( user ) as ForumPermission;

            if(fp.Moderate)
                return;

			ForumConfiguration forumConfig = ForumConfiguration.Instance();

			// TDD HACK 10/20/2004
			// there is a bug with the C# compiler in that if you have an Int64 enum and try to use it in a switch statement, 
			// the case values are only treated as Int32 and not the enum type. To solve this I'm having to use the constant 
			// values from the enumeration to make this work.
			long lValue = (long)permission;
			switch (lValue) {
				case 0x0100000000000000: //(long)Permission.Administer:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if( !fp.Administer )
						RedirectOrExcpetion(CSExceptionType.AdministrationAccessDenied);
					break;
				case (long)Permission.Announcement:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if (csContext.User.IsAnonymous)
						if (!csContext.Context.Request.IsAuthenticated)
							RedirectOrExcpetion(CSExceptionType.PostEditAccessDenied);

					if (!fp.Announcement)
						RedirectOrExcpetion(CSExceptionType.PostAnnounceAccessDenied);
					break;
				case (long)Permission.LocalAttachment:
					// TODO may need to add configuration value to allow anonymous attachments
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if(!fp.LocalAttachment)
						RedirectOrExcpetion(CSExceptionType.PostAttachmentsNotAllowed);
					break;
				case (long)Permission.RemoteAttachment:
					// TODO may need to add configuration value to allow anonymous attachments
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if(!fp.RemoteAttachment)
						RedirectOrExcpetion(CSExceptionType.PostAttachmentsNotAllowed);
					break;
				case (long)0x0000000100000000: //Permission.CreatePoll:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if (!fp.CreatePoll)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);
					break;
				case (long)Permission.Delete:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if (csContext.User.IsAnonymous)
						if (!csContext.Context.Request.IsAuthenticated)
							RedirectOrExcpetion(CSExceptionType.PostDeleteAccessDenied);

					// If the user is denied delete return
					if (!fp.Delete)
						RedirectOrExcpetion(CSExceptionType.PostDeleteAccessDenied);

					// Ensure we have a post
					//
					if (post == null)
						throw new Exception("Post parameter is required for Delete check");

					// Ensure the user that created this post is the user attempting to delete it
					//
					if (csContext.User.UserID != post.User.UserID)
						RedirectOrExcpetion(CSExceptionType.PostDeleteAccessDenied);

					// Does the post have children, if so the user can't delete
					//
					if (post.Replies > 0)
						RedirectOrExcpetion(CSExceptionType.PostDeleteAccessDeniedHasReplies);

					// Has the time limit been exceeded for this user to delete the post?
					//
					if (forumConfig.PostDeleteAgeInMinutes > 0)
						if( DateTime.Now < post.PostDate.AddMinutes(forumConfig.PostDeleteAgeInMinutes))
							RedirectOrExcpetion(CSExceptionType.PostDeletePermissionExpired);
					break;
				case (long)Permission.Edit:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if (csContext.User.IsAnonymous)
						if (!csContext.Context.Request.IsAuthenticated)
							RedirectOrExcpetion(CSExceptionType.PostEditAccessDenied);

					if (!fp.Edit)
						RedirectOrExcpetion(CSExceptionType.PostEditAccessDenied);

					if (post == null)
						throw new Exception("Post parameter is required for Edit check");

					// Ensure the user that created this post is the user attempting to delete it
					// or that the user is an editor
					if (csContext.User.UserID != post.User.UserID && !fp.EditOthers )
						RedirectOrExcpetion(CSExceptionType.PostEditAccessDenied);

					// Has the time limit been exceeded for this user to delete the post?
					//
					if (forumConfig.PostEditBodyAgeInMinutes > 0)
						if (DateTime.Now > post.PostDate.AddMinutes(forumConfig.PostEditBodyAgeInMinutes))
							RedirectOrExcpetion(CSExceptionType.PostEditPermissionExpired);

					break;
				case 0x0000000800000000://Permission.EditOthers:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if(!fp.EditOthers)
						RedirectOrExcpetion(CSExceptionType.PostEditAccessDenied);
					break;
				case 0x0000100000000000://Permission.Moderate:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if(!fp.Moderate)
						RedirectOrExcpetion(CSExceptionType.ModerateAccessDenied);
					break;
				case (long)Permission.Post:
					if ((!csContext.SiteSettings.EnableAnonymousUserPosting) && (!section.EnableAnonymousPosting) && (csContext.User.IsAnonymous))
						if (!csContext.Context.Request.IsAuthenticated)
							RedirectOrExcpetion(CSExceptionType.PostAccessDenied);

					if (!fp.Post)
						RedirectOrExcpetion(CSExceptionType.PostAccessDenied);
					break;

				case (long)Permission.Read:
					if(!fp.Read)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					break;
				case (long)Permission.Reply:
					
					if ((csContext.User.IsAnonymous) && (!section.EnableAnonymousPosting) && (!csContext.SiteSettings.EnableAnonymousUserPosting))
						if (!csContext.Context.Request.IsAuthenticated)
							RedirectOrExcpetion(CSExceptionType.PostReplyAccessDenied);

					if (!fp.Reply) 
						RedirectOrExcpetion(CSExceptionType.PostReplyAccessDenied);
					
					// Ensure we have a post
					//
					if (post == null)
						throw new Exception("Post parameter is required for Reply check");

					// Can't reply if locked
					//
					if (post.IsLocked)
						RedirectOrExcpetion(CSExceptionType.PostLocked);

					break;
				case 0x0000000200000000://Permission.Sticky:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);
					
					if(!fp.Sticky)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);
					break;
				case (long)Permission.View:
					if(!fp.View)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);
					break;
				case (long)Permission.Vote:
					if (!csContext.Context.Request.IsAuthenticated)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);

					if (!fp.Vote)
						RedirectOrExcpetion(CSExceptionType.AccessDenied);
					break;

				case (long)Permission.Video:
					if(!fp.Video)
						RedirectOrExcpetion(CSExceptionType.UnknownError,"User does not have permission to post a video");

					break;
				case (long)Permission.Ink:
					if(!fp.Ink)
						RedirectOrExcpetion(CSExceptionType.UnknownError,"User does not have permission to use ink");
					break;
				case (long)Permission.MarkAsAnswer:
					if(!fp.MarkAsAnswer)
						RedirectOrExcpetion(CSExceptionType.UnknownError,"User does not have permission to mark as answer");
					break;
				default	:
					RedirectOrExcpetion(CSExceptionType.AccessDenied);
                    break;
			}
		}
		#endregion

    }

}
