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

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.IO;
using System.Web.Caching;
using CommunityServer.Components;


namespace CommunityServer.Discussions.Components {
    // *********************************************************************
    //  Threads
    //
    /// <summary>
    /// This class contains methods for working with an individual post.  There are methods to
    /// Add a New Post, Update an Existing Post, retrieve a single post, etc.
    /// </summary>
    // ***********************************************************************/
    public class Threads {

        private static bool IsCachable(ForumThreadQuery query)
        {
            return query.PageIndex == 0 && (query.Status == ThreadStatus.NotSet) && (query.UserFilter == ThreadUsersFilter.All || query.UserFilter == ThreadUsersFilter.HideTopicsParticipatedIn) && query.ForumID != 0 && !query.UnReadOnly;
        }

		private static string GetUserKeyValue(ThreadUsersFilter filter, CSContext context)
		{
			switch (filter)
			{
				case ThreadUsersFilter.HideTopicsParticipatedIn:
				case ThreadUsersFilter.HideTopicsNotParticipatedIn:
					return context.User.UserID.ToString();

				default:
					return "";
			}
		}

		public static ThreadSet GetThreads(ForumThreadQuery query) {
			
            CSContext context = CSContext.Current;
            User user = context.User;
			
			string key = "Forum-Threads::S:{0}-PS:{1}-PI:{2}-Day:{3}-Year{4}-SB:{5}-SO{6}-AT:{7}-U:{8}-R:{9}-U:{10}-L:{11}-OT:{12}-T:{13}-C:{14}-ISP:{15}";
			string cacheKey = string.Format(key,
                query.ForumID,//0
                query.PageIndex,//1
                query.PageSize,//2
                query.ThreadsNewerThan.DayOfYear,//3 
                query.ThreadsNewerThan.Year,//4 
                query.SortBy,//5
                query.Order,//6
                query.ActiveTopics,//7
                query.UnAnsweredOnly,//8
                user.RoleKey,//9
                GetUserKeyValue(query.UserFilter, context),//10
                query.ForumID < 0 ? user.FilterLanguagesCacheKey : "",//11
                query.LogicallyOrTags.ToString(),//12
                query.FilterByTags ? String.Join("\n", query.Tags) : "",//13 
                query.IncludeCategories, //14
                query.IgnoreStickyPosts);
	
			ThreadSet threadSet = context.Items[cacheKey] as ThreadSet;
            
            if (threadSet == null && user.IsAnonymous) 
				threadSet = CSCache.Get(cacheKey) as ThreadSet;

            bool isCachable = IsCachable(query);

			if (threadSet == null && isCachable)
				threadSet = CSCache.Get(cacheKey) as ThreadSet;


      
            if(threadSet == null)
            {
                ForumDataProvider dp = ForumDataProvider.Instance();
                threadSet = dp.GetThreads(query);

                if	(isCachable)
                {
                    if(query.UserFilter == ThreadUsersFilter.HideTopicsParticipatedIn)
                    {
                        CSCache.Insert(cacheKey,threadSet, CSCache.SecondFactorCalculate(15),CacheItemPriority.Low);
                    }
                    else
                    {
                        CSCache.Insert(cacheKey,threadSet, CSCache.MinuteFactor,CacheItemPriority.BelowNormal);
                    }
                }

                context.Items[key] = threadSet;
            }

			// We need to do read/unread again since we didn't execute that code path above
			if (!user.IsAnonymous)
				return ResolveReadStatus(CopyThreadSet(threadSet,isCachable), query.ForumID, user.UserID);
            else
		        return threadSet;
        }

        private static ThreadSet CopyThreadSet(ThreadSet threadSet, bool isCacheable)
        {
            if(!isCacheable)
                return threadSet;

            ThreadSet tempThreadSet = new ThreadSet();
            tempThreadSet.TotalRecords = threadSet.TotalRecords;
            tempThreadSet.PageIndex = threadSet.PageIndex;

            foreach(Thread temp in threadSet.Threads)
            {
                Thread copy = temp.Copy() as Thread;
                tempThreadSet.Threads.Add(copy);
            }

            return threadSet;
        }

        private static ThreadSet ResolveReadStatus(ThreadSet threadSet, int forumID, int userID)
        {
            if (threadSet != null) 
            {
                ArrayList threads;
                HybridDictionary readThreads = GetReadThreads(forumID, userID, false);

                int markReadAfter = (int) readThreads["ReadAfter"];

                // Set the status on announcements
                threads = threadSet.Announcements;
                for (int i = 0; i < threads.Count; i++) 
                {
                    Thread t = threads[i] as Thread;

                    if ((t.MostRecentPostID < markReadAfter) || (readThreads[t.ThreadID] != null))
                        t.HasRead = true;
                    else
                        t.HasRead = false;
                }

                // Set the status on threads
                threads = threadSet.Threads;
                for (int i = 0; i < threads.Count; i++) 
                {
                    Thread t = threads[i] as Thread;

                    if ((t.MostRecentPostID < markReadAfter) || (readThreads[t.ThreadID] != null))
                        t.HasRead = true;
                    else
                        t.HasRead = false;
                }
            }

            return threadSet;
        }

        // *********************************************************************
        //  GetNextThreadID
        //
        /// <summary>
        /// Returns the id of the next thread.
        /// </summary>
        /// <param name="postID">Current threadid is determined from postsid</param>
        // ***********************************************************************/
        public static int GetNextThreadID(int postID) {
            // Create Instance of the IDataProvider
            ForumDataProvider dp = ForumDataProvider.Instance();

            return dp.GetNextThreadID(postID);			
        }

        // *********************************************************************
        //  GetPrevThreadID
        //
        /// <summary>
        /// Returns the id of the previous thread.
        /// </summary>
        /// <param name="postID">Current threadid is determined from postsid</param>
        // ***********************************************************************/
        public static int GetPrevThreadID(int postID) {
            // Create Instance of the IDataProvider
            ForumDataProvider dp = ForumDataProvider.Instance();

            return dp.GetPrevThreadID(postID);			
        }

		// *********************************************************************
		//  GetReadThreads
		//
		/// <summary>
		/// Returns an array list of threads the user has read
		/// </summary>
		// ***********************************************************************/
		public static HybridDictionary GetReadThreads(int sectionID, int userID, bool flush) {

			string cacheKey = "threads-read-" + sectionID.ToString() + "|" + userID.ToString();

			HybridDictionary d = CSCache.Get(cacheKey) as HybridDictionary;

			if (d == null || flush) {

				// Create Instance of the IDataProvider
				ForumDataProvider dp = ForumDataProvider.Instance();

				d = dp.GetThreadsRead(sectionID, userID);		
			}

			CSCache.MicroInsert(cacheKey, d, 1);

			return d;
		}

		public static Thread GetThread(int threadID)
		{
            CSContext context = CSContext.Current;
			string cacheKey = "Thread-ThreadID:" + threadID + "-UserID:" + context.User.UserID;
			Thread thread =  context.Items[cacheKey] as Thread;
            

			if(thread == null)
			{
				thread = ForumDataProvider.Instance().GetThread(threadID, context.User.UserID);
				context.Items.Add(cacheKey, thread);
			}

			return thread;
		}

        public static void UpdateThreadStatus (int threadID, ThreadStatus status) {
            // For tests :)
            //HttpContext.Current.Response.Write( string.Format( "<br />Thread ID={0} and its status should be: {1}.<br />", threadID, status ) );

            ForumDataProvider.Instance().UpdateThreadStatus( threadID, status );
        }
        
        /// <summary>
        /// Permanently deletes a thread and its related data.
        /// </summary>
        public static void DeleteThread (int sectionID, int threadID)
        {
            DeleteThread( sectionID, threadID, true );
        }

        public static void DeleteThread (int sectionID, int threadID, bool resetStatistics)
        {
            ForumDataProvider.Instance().DeleteThread( sectionID, threadID, resetStatistics );
        }
    }
}
