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

using System;
using System.Collections;
using System.Web;
using CommunityServer.Components;
using CommunityServer.Configuration;

namespace CommunityServer {
    class CachedUser {
        public DateTime Created;
        public User User;
    }

    // *********************************************************************
    //  Users
    //
    /// <summary>
    /// This class encapsulates all data operations for managing forum users.
    /// </summary>
    // ***********************************************************************/
    public class Users {

//        public static void SetUserLocation(string location)
//        {
//            CSContext context = CSContext.Current;
//        	Hashtable userLookupTable = UserCache();
//            CachedUser user = userLookupTable[UserKey(context.User.UserID.ToString())] as CachedUser;
//            if(user == null)
//                user = userLookupTable[UserKey(context.User.Username)] as CachedUser;
//
//        }

        public static void ValidateUserAccess(User userToCheck, User currentUser)
        {
            if(!currentUser.IsMembershipAdministrator)
            {
                if(userToCheck == null ||  userToCheck.IsBanned)
                    throw new CSException(CSExceptionType.UserAccountInvalid);
            }
        }

        public static bool EnablePM(User user)
        {
            if(user.IsBanned)
                return false;

            //This part is a big hacky and will be cleaned up once PM's become a true
            //application in CS 3.0. This will get us by until then. SW.
            CSApplicationData forum = ApplicationSet.Applications[ApplicationType.Forum];
            if(forum == null || !forum.Enabled)
                return false;

            return true;
        }

        # region GetUser methods
        // *********************************************************************
        //  GetUser
        //
        /// <summary>
        /// Return a User class for the specified user
        /// </summary>
        /// <returns>
        /// Instance of User with details about a given forum user.</returns>
        // ***********************************************************************/
        public static User GetUser () 
        {
            return GetUser(0, GetLoggedOnUsername(), true, true);
        }

        public static User GetUser (bool isOnline) {
            return GetUser(0, GetLoggedOnUsername(), isOnline, true);
        }

        public static User GetUser (int userID, bool isOnline) {
            return GetUser(userID, null, isOnline, true);
        }

        public static User GetUser (int userID, bool isOnline, bool isCacheable) {
            return GetUser(userID, null, isOnline, isCacheable);
        }

        private static string UserKey(object key)
        {
            return string.Format("User-{0}", key).ToLower();
        }

		public static User GetUser (int userID, string username, bool isOnline, bool isCacheable) 
		{
			User user = null;

			// If the request is not authenticated return
			// a new user instance
			//
			if ((userID == 0) && (username == "Anonymous")) {
				user = Users.GetAnonymousUser();
				return user;
			}
			
			CSContext csContext = CSContext.Current;
            int maxOnlineAgeInSeconds = 30;
            int maxOfflineAgeInMinutes = 180;
            Hashtable userLookupTable = UserCache();

            
            
            string userKey = userID > 0 ? UserKey(userID.ToString()) : UserKey(username);

            // Attempt to return the user from ContextItems to save
            // us a trip to the database.
            //
			user = csContext.Items[userKey] as User;
			if(user != null)
				return user;

            // Attempt to return the user from Cache to save
            // us a trip to the database.
            //

            CachedUser cachedUser = userLookupTable[userKey] as CachedUser;

            if (cachedUser != null) 
            {

                if (isOnline) 
                {

                    if (cachedUser.Created > DateTime.Now.AddSeconds(-maxOnlineAgeInSeconds))
                    {
                        return cachedUser.User;
                    }
                    else
                    {
                        userLookupTable[UserKey(cachedUser.User.Username)] = null;
                        userLookupTable[UserKey(cachedUser.User.UserID.ToString())] = null;
                    }
                    
                } 
                else 
                {

                    if (cachedUser.Created > DateTime.Now.AddMinutes(-maxOfflineAgeInMinutes))
                        return cachedUser.User;
                    else
                    {
                        userLookupTable[UserKey(cachedUser.User.Username)] = null;
                        userLookupTable[UserKey(cachedUser.User.UserID.ToString())] = null;
                    }

                }
                
            }

            // User was not in the Context.Items collection and not in the Cache
            // we need to go to the database and fetch the user
            //
            user = GetUserFromDataProvider(userID, username, isOnline, false);

			// A bit of error checking to ensure we have a new user.
			//
			// When GetUserFromDataProvider() failed to lookup an existing UserID from the
			// cookie stored, an error was thrown and this caused the very annoying
			// and very well known "Forms" bug showing up on any page.
			// This resolves that.  I caused GetUserFromDatProvider() to return null
			// if that happens now and we check for it below.
			//
			// TODO this should not be to log out the user, it should be in the page logic
			if (user == null ) 
			{
				return new User();
			}


            // If we can't cache, add the user to Context items so we don't have
            // to retrieve the user again for the same request even if the user is
            // not cacheable
            //
		    csContext.Items[UserKey(user.Username)] = user;
            csContext.Items[UserKey(user.UserID.ToString())] = user;

            // If not cacheable, return
            //
            if (!isCacheable)
                return user;

            if(cachedUser == null) cachedUser = new CachedUser();

            cachedUser.User = user;
            cachedUser.Created = DateTime.Now;

            // Add user to lookup table
            //
            userLookupTable[UserKey(user.Username)] = cachedUser;
            userLookupTable[UserKey(user.UserID.ToString())] = cachedUser;
            
            return user;

        }

        private static Hashtable UserCache()
        {
            const string cacheKey = "UserLookupTable";
            Hashtable userLookupTable = CSCache.Get(cacheKey) as Hashtable;
            if (userLookupTable == null)
            {
                userLookupTable = new Hashtable();
                CSCache.Insert(cacheKey,userLookupTable,3 * CSCache.MinuteFactor);
            }
            return userLookupTable;
        }

        public static IList GetLoggedInModerators()
        {
            Hashtable userLookupTable = UserCache();
            ArrayList list = new ArrayList();
            try
            {
                foreach(User user in userLookupTable.Values)
                {
                    if(user.IsAdministrator || user.IsModerator)
                        list.Add(user);
                }
            }
            catch{} //we do not want to lock on this iteration.

            return list;
        }

        public static User GetUserWithWriteableProfile()
        {
            return GetUserFromDataProvider(0,GetLoggedOnUsername(),false,true);
        }

        public static User GetUserWithWriteableProfile(int userID, string username, bool isOnline)
        {
            return GetUserFromDataProvider(userID,username,isOnline,true);
        }

		private static User GetUserFromDataProvider(int userID, string username, bool isOnline, bool isEditable) 
		{

            // Get an instance of the CommonDataProvider
            //
		    CommonDataProvider dp = CommonDataProvider.Instance();

            string lastAction = string.Empty;// SiteUrls.LocationKey();

            // Attempt to get the user from the dataprovider layer
            //
            try 
            {
                return dp.GetUser(userID, username, isOnline, isEditable, lastAction);
            }
            catch (CSException) 
            {
                // if we made it to here, we can't figure out the user
                //
                return null;
            }
        }

        // ***********************************************************************
        // GetCurrentUsername
        /// <summary>
        ///  Returns the user name without the domain, replaces forumContext.Username
        /// </summary>
        /// <returns>stripped username</returns>
        /// <remarks>
        /// Author:		PLePage
        /// Date:		07/09/03
        /// This strips the users domain from their name.  If the configuration 
        /// specifies a domain, it will remove it, * will remove all domains and 
        /// nothing will not remove any domain appendages.
        /// 
        /// History:
        /// 7/16/2004	Terry Denham	moddified the method to use the StripDomainName as
        ///		this needs to be handled before we actually connect to the database so that
        ///		the admin can configure the web.config file, hit the site and get logged in
        ///		and associated with the correct role. If we make it pull from the database
        /// </remarks>
        // ***********************************************************************/
        public static string GetLoggedOnUsername() {
            CSContext csContext = CSContext.Current;

//            string strDomain = CSContext.Current.SiteSettings.WindowsDomain;
            if (!csContext.IsWebRequest ||  csContext.Context.User == null || csContext.Context.User.Identity.Name == string.Empty)
                return "Anonymous";

            return csContext.UserName;
        }

        #endregion


        #region FindUserByEmail / Username
        // *********************************************************************
        //  FindUserByEmail
        //
        /// <summary>
        /// Returns a user given an email address.
        /// </summary>
        /// <param name="emailAddress">Email address to look up username by.</param>
        /// <returns>Username</returns>
        /// 
        // ********************************************************************/
        public static User FindUserByEmail(string emailAddress) {
            
            // Create Instance of the CommonDataProvider
            CommonDataProvider dp = CommonDataProvider.Instance();

            return GetUser(dp.GetUserIDByEmail(emailAddress), false);
        }

        public static User FindUserByUsername (string username) {
            return GetUser(0, username, false, true);
        }
        #endregion

        #region GetUsers
        // *********************************************************************
        //  GetUsers
        //
        /// <summary>
        /// Returns all the users currently in the system.
        /// </summary>
        /// <param name="pageIndex">Page position in which to return user's for, e.g. position of result set</param>
        /// <param name="pageSize">Size of a given page, e.g. size of result set.</param>
        /// <param name="sortBy">How the returned user's are to be sorted.</param>
        /// <param name="sortOrder">Direction in which to sort</param>
        /// <returns>A collection of user.</returns>
        /// 
        // ********************************************************************/

        public static int UserCount(UserQuery query, bool cacheable)
        {
            int count = -1;
            string key = query.Key + "Count";
            if(cacheable)
            {
                object obj = CSCache.Get(key);
                if(obj != null)
                    count = (int)obj;
            }

            if(count == -1)
            {
                CommonDataProvider dp = CommonDataProvider.Instance();
                count = dp.UserCount(query);
                if(cacheable)
                CSCache.Insert(key,count,CSCache.MinuteFactor*10);
            }

            return count;
        }

        
        

        public static UserSet GetUsers(UserQuery query, bool cacheable) {
            
            UserSet users = null;
            // Return moderation counters also
            //
            bool returnModerationCounters = CSContext.Current.SiteSettings.EnableUserModerationCounters;

            // If we're compiled with debug code we never cache
            //
#if DEBUG_NOCACHE
            cacheable = false;
#endif
            
            string usersKey = query.Key + returnModerationCounters.ToString();

            if (cacheable) {

                // Serve from the cache when possible
                //
                users = CSCache.Get(usersKey) as UserSet;

            } 

            if (users == null) 
            {

                CommonDataProvider dp = CommonDataProvider.Instance();
                users = dp.GetUsers (query, returnModerationCounters);

                if(cacheable)
                {
                    CSCache.Insert(usersKey, users, CSCache.MinuteFactor/2);
                }

            }

            return users;
        }

        #endregion

        #region Track Anonymous Users

        public static void RemoveAnonymousTracking(HttpContext context, string cookieName)
        {
            HttpCookie cookie = context.Request.Cookies[cookieName];

			if (cookie != null)
			{
				cookie.Expires = DateTime.Now.AddDays(-99);
				context.Response.Cookies.Add(cookie);
			}
        }

        public static Guid GetAnonyousUserTrackingID(HttpContext context)
        {
            Guid userID = Guid.Empty;
            SiteSettings siteSettings = CSContext.Current.SiteSettings;
            HttpCookie cookie = context.Request.Cookies[siteSettings.AnonymousCookieName];

            if ((cookie == null) || (cookie.Value == null)) 
            {
                userID = Guid.NewGuid();
                cookie = new HttpCookie(siteSettings.AnonymousCookieName);
                
            } 
            else 
            {
                try
                {
                    userID = new Guid(cookie.Value);
                }
                catch
                {
                    userID = Guid.NewGuid();
                }
            }

            cookie.Value = userID.ToString();
            cookie.Expires = DateTime.Now.AddMinutes(siteSettings.AnonymousCookieExpiration);
            context.Response.Cookies.Add(cookie);


            return userID;
        }

        // *********************************************************************
        //  TrackAnonymousUsers
        //
        /// <summary>
        /// Used to keep track of the number of anonymous users on the system
        /// </summary>
        /// <returns>A collection of user.</returns>
        /// 
        // ********************************************************************/
//        public static void TrackAnonymousUsers() {
//
//            CSContext context = CSContext.Current;
//            if(context.IsWebRequest && context.SiteSettings.EnableAnonymousUserTracking)
//                UsersOnline.IncludeGuest(context.User, context.AnonymousUserID);
//        }

        #region Anonymous - OLD
//        public static void UpdateAnonymousUsers (int settingsID) {
//            string key = string.Format("SettingsID:{0}AnonymousUserList",settingsID);
//
//            CommonDataProvider dp = CommonDataProvider.Instance();
//
//            Hashtable anonymousUsers = GetAnonymousUserList(settingsID);
//
//            SiteStatistics.LoadSiteStatistics(settingsID, true, 3).CurrentAnonymousUsers = dp.UpdateAnonymousUsers( anonymousUsers, settingsID );
//
//            CSCache.Remove(key);
//        }

//        private static Hashtable GetAnonymousUserList (int settingsID) {
//            string key = string.Format("SettingsID:{0}AnonymousUserList",settingsID);
//            
//            Hashtable anonymousUserList = CSCache.Get(key) as Hashtable;
//
//            // Do we have the hashtable?
//            //
//            if (anonymousUserList == null) {
//             
//
//                // Get the hashtable
//                //
//                anonymousUserList = new Hashtable();
//                CSCache.Insert(key,anonymousUserList, 10 * CSCache.MinuteFactor);
//
//            }
//
//            return anonymousUserList;
//
//        }

        #endregion

        #endregion

        #region CreateAnonymousUser
        
		public static User GetAnonymousUser () 
        {
			return GetAnonymousUser(true);
		}
        public static User GetAnonymousUser (bool fromCache) 
        {
			CSContext context = CSContext.Current;
            const string cacheKeyFormat = "Site:{0}.AnonymousUser";
            string cacheKey = String.Format(cacheKeyFormat, context.SiteSettings.SettingsID );

            User u = null;

            if(fromCache)
                u = CSCache.Get(cacheKey) as User;

			if( u == null) {
				u = CommonDataProvider.Instance().GetAnonymousUser( context.SiteSettings.SettingsID );

				if( u != null && u.Username != null && u.UserID > 0 && fromCache ) {
					CSCache.Insert( cacheKey, u, 120 );
				}
			}

            return u;
			
        }

        #endregion CreateAnonymousUser

        #region Update User

        // *********************************************************************
        //  Update
        //
        /// <summary>
        /// Updates a user's personal information.
        /// </summary>
        /// <param name="user">The user to update.  The Username indicates what user to update.</param>
        /// <param name="NewPassword">If the user is changing their password, the user's new password.
        /// Otherwise, this should be the user's existing password.</param>
        /// <returns>This method returns a boolean: it returns True if
        /// the update succeeds, false otherwise.  (The update might fail if the user enters an
        /// incorrect password.)</returns>
        /// <remarks>For the user to update their information, they must supply their password.  Therefore,
        /// the Password property of the user object passed in should be set to the user's existing password.
        /// The NewPassword parameter should contain the user's new password (if they are changing it) or
        /// existing password if they are not.  From this method, only the user's personal information can
        /// be updated (the user's password, forum view settings, email address, etc.); to update the user's
        /// system-level settings (whether or not they are banned, their trusted status, etc.), use the
        /// UpdateUserInfoFromAdminPage method.  <seealso cref="UpdateUserInfoFromAdminPage"/></remarks>
        /// 
        // ********************************************************************/
        public static bool UpdateUser (User user) {

			CSEvents.BeforeUser(user,ObjectState.Update);

			// Make sure we update all references to User ONLY IF the user being changed is the current user
			CSContext context = CSContext.Current;
            if (context.User.UserID == user.UserID)
			    context.User = user;

            // Create Instance of the CommonDataProvider
            
            
            CreateUserStatus status;

            if(user.HasProfile && !user.Profile.IsReadOnly)
            {
                user.Profile.Save();
            }
			
            // Call the underlying update
            CommonDataProvider dp = CommonDataProvider.Instance();
            dp.CreateUpdateDeleteUser(user, DataProviderAction.Update, out status);
            

            CSEvents.AfterUser(user,ObjectState.Update);

			RemoveUserFromCache(user);

            return true;
        }
        #endregion

    	public static RenameUserStatus RenameUser(User user, string desiredUserName, bool ignoreDisallowedNames)
    	{
			return RenameUser(user, desiredUserName, ignoreDisallowedNames, false);
    	}

		public static RenameUserStatus RenameUser(User user, string desiredUserName, bool ignoreDisallowedNames, bool skipPermissionCheck)
		{
			User loggedInUser = CSContext.Current.User;

			if (!skipPermissionCheck && (!loggedInUser.IsAdministrator && !loggedInUser.IsMembershipAdministrator))
				return RenameUserStatus.InsufficientPermissions;

			RenameUserStatus status = CommonDataProvider.Instance().RenameUser(user.UserID, desiredUserName, ignoreDisallowedNames);
			if (status == RenameUserStatus.Success)
				RemoveUserFromCache(user);

			return status;
		}

		public static DeleteUserStatus DeleteUser(User user)
		{
			return DeleteUser(user, GetAnonymousUser().Username);
		}

		public static DeleteUserStatus DeleteUser(User user, string reassignUserName)
		{
			User loggedInUser = CSContext.Current.User;

			if (!loggedInUser.IsAdministrator && !loggedInUser.IsMembershipAdministrator)
				return DeleteUserStatus.InsufficientPermissions;

			User reassignUser = Users.GetUser(0, reassignUserName, false, true);
			if (reassignUser == null)
				return DeleteUserStatus.InvalidReassignUserName;

			DeleteUserStatus status = CommonDataProvider.Instance().DeleteUser(user.UserID, reassignUserName);
			if (status == DeleteUserStatus.Success)
			{
				RemoveUserFromCache(user);
				RemoveUserFromCache(reassignUser);
			}

			return status;
		}

		private static void RemoveUserFromCache(User user)
		{
			Hashtable userLookupTable = UserCache();
			userLookupTable[UserKey(user.UserID.ToString())] = null;
			userLookupTable[UserKey(user.Username)] = null;
		}

        #region Toggle
        // *********************************************************************
        //  ToggleOptions
        //
        /// <summary>
        /// Toggle various user options
        /// </summary>
        /// <param name="username">Name of user we're updating</param>
        /// <param name="hideReadThreads">Hide threads that the user has already read</param>
        /// 
        // ********************************************************************/
        public static void ToggleOptions(string username, bool hideReadThreads) 
        {
            ToggleOptions(username, hideReadThreads, ViewOptions.NotSet);			
        }

        // *********************************************************************
        //  ToggleOptions
        //
        /// <summary>
        /// Toggle various user options
        /// </summary>
        /// <param name="username">Name of user we're updating</param>
        /// <param name="hideReadThreads">Hide threads that the user has already read</param>
        /// <param name="viewOptions">How the user views posts</param>
        /// 
        // ********************************************************************/
        public static void ToggleOptions(string username, bool hideReadThreads, ViewOptions viewOptions) {
            // Create Instance of the CommonDataProvider
            CommonDataProvider dp = CommonDataProvider.Instance();

            dp.ToggleOptions(username, hideReadThreads, viewOptions);			
        }

        #endregion


        #region Valid User
        // *********************************************************************
        //  ValidUser
        //
        /// <summary>
        /// Determines if the user is a valid user.
        /// </summary>
        /// <param name="user">The user to check.  Note that the Username and Password properties of the
        /// User object must be set.</param>
        /// <returns>A boolean: true if the user's Username/password are valid; false if they are not,
        /// or if the user has been banned.</returns>
        /// 
        // ********************************************************************/
       
        
        public static LoginUserStatus ValidUser(User user) 
        {
			CSContext context = CSContext.Current;
            if(!MemberRoleProfileProvider.Instance().Members.ValidateUser(user.Username,user.Password))
            {
				

                EventLogEntry entry = new EventLogEntry();
                entry.Category = "Security";
                entry.EventID = 600;
                entry.SettingsID = context.SettingsID;
                entry.EventType = EventType.Warning;
                entry.Message = "Failed login for " + user.Username;
                EventLogs.Write(entry);
                
                return LoginUserStatus.InvalidCredentials;
            }
			

            // Create Instance of the CommonDataProvider
            //
            //CommonDataProvider dp = CommonDataProvider.Instance();		
            
            // Lookup account by provided username
            //
            User userLookup = Users.FindUserByUsername( user.Username );
            if (userLookup == null)
                return LoginUserStatus.InvalidCredentials;
                
            
            if(!userLookup.Member.IsApproved)
                return LoginUserStatus.AccountPending;

            
            // Check Account Status
            // (This could be done before to validate credentials because
            // system doesn't allow duplicate usernames)
            //
            if (!context.SiteSettings.EnableBannedUsersToLogin &&
                userLookup.IsBanned &&  
                DateTime.Now <= userLookup.BannedUntil) 
            {
                // It's a banned account
                return LoginUserStatus.AccountBanned;
            }
                // LN 5/21/04: Update user to DB if ban expired
            else if (userLookup.IsBanned &&  DateTime.Now > userLookup.BannedUntil) 
            {
                // Update to back to datastore
                userLookup.AccountStatus = UserAccountStatus.Approved;
                userLookup.BannedUntil = DateTime.Now;

                Users.UpdateUser(userLookup);
            }
            if (userLookup.AccountStatus == UserAccountStatus.ApprovalPending) 
            {
                // It's a pending account
                return LoginUserStatus.AccountPending;
            }
            if (userLookup.AccountStatus == UserAccountStatus.Disapproved) 
            {
                // It's a disapproved account
                return LoginUserStatus.AccountDisapproved;
            }

            //Set current user
            context.User = userLookup;

            if(context.IsWebRequest)
                RemoveAnonymousTracking(context.Context,context.SiteSettings.AnonymousCookieName);

            //allow others to take action here
            CSEvents.UserValidated(userLookup);

            return LoginUserStatus.Success;


        }

        #endregion

        #region CreateUser
        // *********************************************************************
        //  CreateNewUser
        //
        /// <summary>
        /// Creates a new user.
        /// </summary>
        /// <param name="user">A User object containing information about the user to create.  Only the
        /// Username and Email properties are used here.</param>
        /// <returns></returns>
        /// <remarks>This method chooses a random password for the user and emails the user his new Username/password.
        /// From that point on, the user can configure their settings.</remarks>
        /// 
        // ********************************************************************/
		public static CreateUserStatus Create(User user, bool sendEmail) {
			return Create(user, sendEmail, false);
		}

		public static CreateUserStatus Create(User user, bool sendEmail, bool ignoreDisallowNames) {
            
			CSEvents.BeforeUser(user, ObjectState.Create);

			CSContext csContext = CSContext.Current;
			SiteSettings settings = csContext.SiteSettings;

            AccountActivation activation = settings.AccountActivation;
            CreateUserStatus status;
            
            string password = user.Password;

            // Lucian: deprecated since it is not handled in CreateUser control
            // and regEx validation on control's form.
            // Make sure the username begins with an alpha character
            //if (!Regex.IsMatch(user.Username, "^[A-Za-z].*"))
            //    return CreateUserStatus.InvalidFirstCharacter;

            // Check if username is disallowed
            if ((!ignoreDisallowNames) && ( DisallowedNames.NameIsDisallowed(user.Username) == true ))
                return CreateUserStatus.DisallowedUsername;

			// Set the user's default moderation level
			user.ModerationLevel = csContext.SiteSettings.NewUserModerationLevel;

            // Create Instance of the CommonDataProvider
            CommonDataProvider dp = CommonDataProvider.Instance();

			try {
				User createdUser = dp.CreateUpdateDeleteUser(user, DataProviderAction.Create, out status);
                
                
                if(createdUser != null && status == CreateUserStatus.Created)
                {
					csContext.User = createdUser;

                	CSConfiguration config = csContext.Config;

                    if(settings.EnableDefaultRole && config.DefaultRoles.Length > 0)
                    {
						foreach(string role in config.DefaultRoles)
						{
							Roles.AddUserToRole(createdUser.Username, role);
						}
                    }

					CSEvents.AfterUser(createdUser,ObjectState.Create);
                }

                
			}
			catch (CSException e) {
				return e.CreateUserStatus;
			}

			// process the emails now
			//
            if(sendEmail == true) 
			{

                User currentUser = csContext.User;
                if (currentUser.IsForumAdministrator || currentUser.IsBlogAdministrator || currentUser.IsGalleryAdministrator) 
                {
                    activation = AccountActivation.Automatic;
                }
				
				// TDD HACK 7/19/2004
				// we are about to send email to the user notifying them that their account was created, problem is
				// when we create the user above we can't set the DateCreated property as this is set through the proc
				// but the email needs to know the DateCreated property. So for now, we'll just set the date to the current
				// datetime of the server. We don't care about the user local time at this point because the user hasn't
				// logged in to set their user profile.
				
				//user.DateCreated = DateTime.Now;
				//user.LastLogin = DateTime.Now;

				// based on the account type, we send different emails
				//
				switch (activation) {
					case AccountActivation.AdminApproval:
						Emails.UserAccountPending (user);
						break;

					case AccountActivation.InvitationOnly:
					case AccountActivation.Email:
					case AccountActivation.Automatic:
						Emails.UserCreate(user, password);
						break;
				}
            }
            
            return CreateUserStatus.Created;
        }
        #endregion

		#region ToggleForceLogin
        public static void ToggleForceLogin(User user)
        {
            // Update the setting in the database
            CommonDataProvider dp = CommonDataProvider.Instance();
            dp.ToggleUserForceLogin(user);

			RemoveUserFromCache(user);
        }
		#endregion

		#region CheckUserLastPostDate

		public static bool CheckUserLastPostDate(User user, int postInterval)
		{
			string cacheKey = "UserLastPostTable";
			string userKey = "User:" + user.UserID;
			Hashtable userLastPostTable = CSCache.Get(cacheKey) as Hashtable;

			// If they're anonymous, tack their IP onto the userKey
			if(user.IsAnonymous)
				userKey += ":IP:" + CSContext.Current.Context.Request.UserHostAddress;

			// If the table is null, no posters to care about, so return true
			if(userLastPostTable == null)
				return true;

			// Iterate through the table... remove all bad entries
			// This may seem a little slower, but it helps to keep the table small
			// Some users may only post once a day, but if the forums stay active all that time,
			// their postdate will still be in memory... regularly walking it and removing unnecessary
			// entries keeps it short
			lock(userLastPostTable.SyncRoot)
			{
				string[] keys = new string[userLastPostTable.Count];
				userLastPostTable.Keys.CopyTo(keys, 0);

				foreach(string key in keys)
					if(((DateTime)userLastPostTable[key]).AddSeconds(postInterval) <= DateTime.Now)
						userLastPostTable.Remove(key);
			}

			// If they aren't in there, then they aren't still being blocked, return true;
			if(!userLastPostTable.Contains(userKey))
				return true;

			// An entry for them is still in there, so they still can't post
			return false;
		}

		public static void UpdateUserLastPostDate(User user)
		{
			string cacheKey = "UserLastPostTable";
			string userKey = "User:" + user.UserID;
			Hashtable userLastPostTable = CSCache.Get(cacheKey) as Hashtable;

			CSContext cntx = CSContext.Current;

			// If they're anonymous, tack their IP onto the userKey
			if(user.IsAnonymous)
				if(cntx.IsWebRequest)
					userKey += ":IP:" + cntx.Context.Request.UserHostAddress;

			// Create the table if it doesn't exist
			if(userLastPostTable == null)
			{
				userLastPostTable = new Hashtable();
				CSCache.Max(cacheKey, userLastPostTable);
			}

			// If they have an entry already in there, remove it
			if(userLastPostTable.Contains(userKey))
				userLastPostTable.Remove(userKey);

			// Add the new time
			userLastPostTable.Add(userKey, DateTime.Now);
		}

		#endregion
        
        /// <summary>
		/// This function returns the age for a user
		/// </summary>
		/// <param name="user">The user</param>
		/// <returns>The user's age in years</returns>
		public static int GetUserAge(User user)
		{
			DateTime birthDate = user.Profile.BirthDate;
			int years;
			
			if(birthDate == DateTime.MinValue)
			{
				years = 0;
			}
			else
			{
				years = DateTime.Now.Year - birthDate.Year;
				if (DateTime.Now.Month < birthDate.Month || (DateTime.Now.Month == birthDate.Month && DateTime.Now.Day < birthDate.Day))
				{
					years--;
				}
			}
			return years;
		}

        /// <summary>
        /// Validates if provided password is compliant with Microsoft Membership 
        /// requirements: minimum required password length and minimum required non
        /// alphanumeric chars no.
        /// </summary>
        /// <param name="newPassword">Password to be verified.</param>
        /// <param name="errorMessage">Error message to return.</param>
        /// <returns>True if compliant, otherwise False.</returns>
        public static bool PasswordIsMembershipCompliant (string newPassword, out string errorMessage) {
            
           return MemberRoleProfileProvider.Instance().Members.PasswordIsMembershipCompliant(newPassword,out errorMessage);
        }


    }


}
