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

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

namespace CommunityServer.Files.Components
{
    /// <summary>
    /// Summary description for Pictures.
    /// </summary>
	public class Entries
	{
		public static void FormatPost(Entry entry)
		{
			entry.FormattedBody = entry.Body;
			CSEvents.PrePost(entry,ObjectState.Create,ApplicationType.FileGallery);
			CSEvents.BeforePost(entry,ObjectState.Create,ApplicationType.FileGallery);
		}

		public static void Create(int galleryID, Entry entry, Guid postAttachmentID) 
		{
			// Ensure the current license allows a new file to be uploaded.
			CreateEntryAccessCheck();
			CSContext cntx = CSContext.Current;
			// Make sure some basic settings exist
			entry.SectionID = galleryID;
			entry.PostDate = DateTime.Now;
			entry.Username = cntx.UserName;
			entry.AuthorID = cntx.User.UserID;

			FormatPost(entry);

			// Add it to the database
			FileGalleryDataProvider fileGalleryDB = FileGalleryDataProvider.Instance();
			fileGalleryDB.CreateEntry(entry);

			// Make sure it was successfully added and then add the picture itself
			if(entry.PostID > 0)
			{
				if (entry.Categories != null)
				{
					// Update in the database
					FileGalleryDataProvider.Instance().UpdateEntryCategories(entry.SectionID, entry.PostID, entry.Categories);
				
					PostCategories.ExpireCategoryCache(entry.SectionID);

					// Clear the cache
					foreach(string name in entry.Categories)
					{
						PostCategory category = PostCategories.GetCategory(name, entry.SectionID, true);
						if (category != null)
							ClearEntriesCache(entry.SectionID, category.CategoryID, -1);
					}
				}

				FileGalleryAttachmentSettings fgas = FileGalleryConfiguration.GetConfig().AttachmentSettings;
				PostAttachment pa = PostAttachments.ToggleTemporary(postAttachmentID, entry.Subject, !fgas.EnableDataStoreStorage, entry.PostID, entry.SectionID, cntx.User.UserID);
                if (fgas.EnableFileSystemStorage)
					fgas.SaveAttachmentToDisk(pa, entry.SectionID, entry.PostID);

				if((entry.Subject == null) || (entry.Subject == string.Empty))
					entry.Subject = pa.FileName;

				entry.AttachmentFilename = pa.FileName;
				entry.FileSize = (int)pa.Length;				
				Update(entry);

//				CreateEntryData(entry, pictureData);

				// Clear cache
				ClearEntriesCache(entry.SectionID, -1, entry.PostID);
			}

			CSEvents.AfterPost(entry,ObjectState.Create,ApplicationType.FileGallery);
		}

		public static void Update(Entry entry, Guid postAttachmentID)
		{
			FileGalleryAttachmentSettings fgas = FileGalleryConfiguration.GetConfig().AttachmentSettings;

			// clean up old attachment, if it exists
			try
			{
				fgas.DeleteAttachmentFromDisk(entry.PostID);
			}
			catch(IOException e)
			{ 
				new CSException(CSExceptionType.AccessDenied, "DeleteEntry: Permission to delete file on filesystem denied.", e).Log(); 
			}
			catch(System.Security.SecurityException e)
			{ 
				new CSException(CSExceptionType.AccessDenied, "DeleteEntry: Permission to delete file on filesystem denied.", e).Log(); 
			}
			catch { }

			PostAttachment pa = PostAttachments.ToggleTemporary(postAttachmentID, entry.Subject, !fgas.EnableDataStoreStorage, entry.PostID, entry.SectionID, CSContext.Current.User.UserID);
			if (fgas.EnableFileSystemStorage)
				fgas.SaveAttachmentToDisk(pa, entry.SectionID, entry.PostID);

			if((entry.Subject == null) || (entry.Subject == string.Empty))
				entry.Subject = pa.FileName;

			entry.AttachmentFilename = pa.FileName;
			entry.FileSize = (int)pa.Length;	
			
			Update(entry);
		}

		public static void Update(Entry entry) 
		{
			entry.FormattedBody = entry.Body;

			CSEvents.PrePost(entry,ObjectState.Update,ApplicationType.FileGallery);
			CSEvents.BeforePost(entry,ObjectState.Update,ApplicationType.FileGallery);

			// Update it in the database
			FileGalleryDataProvider.Instance().UpdateEntry(entry, CSContext.Current.User.UserID);

			if (entry.Categories != null)
			{
				// Update in the database
				FileGalleryDataProvider.Instance().UpdateEntryCategories(entry.SectionID, entry.PostID, entry.Categories);

				PostCategories.ExpireCategoryCache(entry.SectionID);

				// Clear the cache
				foreach(string name in entry.Categories)
				{
					PostCategory category = PostCategories.GetCategory(name, entry.SectionID, true);
					if (category != null)
						ClearEntriesCache(entry.SectionID, category.CategoryID, -1);
				}
			}

			// Clear cache
			ClearEntriesCache(entry.SectionID, -1, entry.PostID);

			CSEvents.AfterPost(entry,ObjectState.Update,ApplicationType.FileGallery);
		}

		public static void Delete(Entry entry)
		{
			try
			{
				FileGalleryAttachmentSettings fgas = FileGalleryConfiguration.GetConfig().AttachmentSettings;
				fgas.DeleteAttachmentFromDisk(entry.PostID);
			}
			catch (Exception e)
			{ 
				new CSException(CSExceptionType.UnknownError, "An unknown error occured while deleting a file attachment.", e).Log(); 
			}
			
			// Delete it from the database
			FileGalleryDataProvider.Instance().DeleteEntry(entry.SectionID, entry.ThreadID);

			// Clear cache
			ClearEntriesCache(entry.SectionID, -1, entry.PostID);
		}

		public static Entry GetRandomEntry(int sectionID, int categoryID)
		{
			string cacheKey = "RandomEntry-Folder:" + sectionID + "-Category:" + categoryID;
			int postID;

			CSContext cntx = CSContext.Current;
			if(!cntx.Items.Contains(cacheKey))
			{
				postID = FileGalleryDataProvider.Instance().GetRandomPostID(sectionID, categoryID);
				cntx.Items.Add(cacheKey, postID);
			}
			else
				postID = (int)cntx.Items[cacheKey];

			if(postID == -1)
				return null;
			else
				return GetEntry(sectionID, postID );
		}

		public static Entry GetEntry(string applicationKey, int postID)
		{
			return GetEntry(postID);
		}

		public static Entry GetEntry(int folderID, int postID)
		{
			return GetEntry(postID);
		}

		public static Entry GetEntry(int entryID)
		{
			return GetEntry(entryID, true);
		}

		public static Entry GetEntry(int entryID, bool useCache)
		{
			CSContext cntx = CSContext.Current;
			int userID = Users.GetUser(true).UserID;
			string cacheKey = string.Format("Entry-Site:{0}EntryID:{1}", cntx.SiteSettings.SettingsID, entryID);
			Entry entry = cntx.Items[cacheKey] as Entry;
			if(entry == null)
			{
				if (useCache)
					entry = CSCache.Get(cacheKey) as Entry;

				if (entry == null)
				{
					entry = FileGalleryDataProvider.Instance().GetEntry(entryID, userID);
					cntx.Items.Add(cacheKey, entry);

					if (useCache)
						CSCache.Insert(cacheKey, entry, 1 * CSCache.MinuteFactor, CacheItemPriority.BelowNormal);
				}
			}
			return entry;
		}

		public static ThreadSet GetEntries(FileGalleryThreadQuery query)
		{
			return GetEntries(query, true);
		}

		public static ThreadSet GetEntries(FileGalleryThreadQuery query, bool cacheable)
		{
			return GetEntries(query, cacheable, false);
		}

		public static ThreadSet GetEntries(FileGalleryThreadQuery query, bool cacheable, bool isAggregate)
		{
			string key = cacheable ? string.Format("EntryQuery-{0}{1}", query.Key, CSContext.Current.RolesCacheKey) : null;
			ThreadSet ts = null;

			if(cacheable)
				ts = CSCache.Get(key) as ThreadSet;

			if(ts == null)
			{
				ts = FileGalleryDataProvider.Instance().GetEntries(query);

				if(cacheable)
					CSCache.Insert(key, ts, CSCache.MinuteFactor * 5, isAggregate ? CacheItemPriority.High : CacheItemPriority.BelowNormal);
			}

			return ts;
		}

		public static void ClearEntriesCache(int folderID, int categoryID, int entryID)
		{
			CSContext cntx = CSContext.Current;
			// Entry itself
			if(entryID != -1)
			{
				CSCache.Remove( string.Format("Entry-Site:{0}EntryID:{1}", cntx.SiteSettings.SettingsID, entryID) );
				CSCache.Remove( string.Format("EntryCategories-Site:{0}-EntryID:{1}", cntx.SiteSettings.SettingsID, entryID));
				CSCache.RemoveByPattern("EntryQuery\\-.*");
				Folders.ExpireFoldersCache();
			}

			// Entry collections
			if (categoryID != -1)
			{
				CSCache.Remove( string.Format("Entries-Site:{0}Folder:{1}Category:{2}", cntx.SiteSettings.SettingsID, folderID, categoryID) );
				CSCache.RemoveByPattern( string.Format("EntryCategories\\-Site:{0}-EntryID:[0-9]*", cntx.SiteSettings.SettingsID));
				CSCache.RemoveByPattern("EntryQuery\\-.*");
			}
			
			// Entry sorting
			CSCache.Remove("CreateEntryAccess");
			CSCache.Remove( "EntriesSortBy-Site:" + cntx.SiteSettings.SettingsID + "-Folder:" + folderID + "-Category:" + categoryID );
		}

		public static void DeleteEntryData(Entry entry)
		{
			FileGalleryAttachmentSettings fgas = FileGalleryConfiguration.GetConfig().AttachmentSettings;

			try
			{
				fgas.DeleteAttachmentFromDisk(entry.PostID);
			}
			catch(Exception ex)
			{ 
				new CSException(CSExceptionType.UnknownError, "An unknown error occured while deleting a file attachment.", ex).Log(); 
			}			

			ClearEntriesCache(entry.SectionID, -1, entry.PostID);

			// Submit it to the database
			FileGalleryDataProvider.Instance().DeleteEntryData(entry.PostID);
		}

		public static PostAttachment GetEntryData(int entryID)
		{
			CSContext.Current.PostID = entryID;
			return FileGalleryConfiguration.Instance().AttachmentSettings.GetAttachment();
		}

		public static ArrayList GetEntryCategories(Entry entry)
		{
			return GetEntryCategories(entry, true);
		}

		public static ArrayList GetEntryCategories(Entry entry, bool useCache)
		{
			CSContext cntx = CSContext.Current;
			string cacheKey ="EntryCategories-Site:" + cntx.SiteSettings.SettingsID + "-EntryID:" + entry.PostID;
			ArrayList categories = cntx.Items[cacheKey] as ArrayList;

			if (categories == null)
			{
				if (useCache)
					categories = CSCache.Get(cacheKey) as ArrayList;

				if (categories == null)
				{
					categories = FileGalleryDataProvider.Instance().GetEntryCategories(entry.PostID);
					cntx.Items.Add(cacheKey, categories);

					if (useCache)
						CSCache.Insert(cacheKey, categories, 1 * CSCache.MinuteFactor, CacheItemPriority.BelowNormal);
				}
			}

			return categories;
		}

		public static int GetEntrySortOrder(int folderID, int categoryID, int pictureID, EntriesSortBy sortBy)
		{
			string cacheKey = "EntriesSortBy-Site:" + CSContext.Current.SiteSettings.SettingsID + "-Folder:" + folderID + "-Category:" + categoryID + "-SortBy:" + sortBy;
			Hashtable sortorder = CSCache.Get(cacheKey) as Hashtable;

			if(sortorder == null)
			{
				sortorder = new Hashtable();
				CSCache.Insert(cacheKey, sortorder, 3 * CSCache.MinuteFactor);
			}

			if(!sortorder.Contains(pictureID))
				sortorder.Add(pictureID, FileGalleryDataProvider.Instance().GetEntrySortOrder( folderID, categoryID, pictureID, sortBy) );

			return (int)sortorder[pictureID];
		}

		public static void UpdateEntrySortOrder(int folderID, int categoryID, Hashtable pictures, EntriesSortBy sortBy)
		{
			string cacheKey = "EntriesSortBy-Site:" + CSContext.Current.SiteSettings.SettingsID + "-Folder:" + folderID + "-Category:" + categoryID + "-SortBy:" + sortBy;
			Hashtable sortorder = CSCache.Get(cacheKey) as Hashtable;

			if(sortorder == null)
			{
				sortorder = new Hashtable();
				CSCache.Insert(cacheKey, sortorder, 3 * CSCache.MinuteFactor);
			}

			foreach(int pictureID in pictures.Keys)
			{
				Entry entry = pictures[pictureID] as Entry;
				if(sortorder.Contains(pictureID))
					sortorder.Remove(pictureID);
				sortorder.Add(pictureID, entry.SortOrder);
			}
		}

		public static void CreateEntryAccessCheck()
		{
			if (!ValidateCreateEntryAccess())
				throw new CSException(CSExceptionType.InvalidLicense, "You have exceeded the maximum number of uploads allowed by your current license.");
		}

		public static bool ValidateCreateEntryAccess()
		{
			object state = CSCache.Get("CreateEntryAccess");
			if (state != null)
				return (bool) state;
			
			bool allowed;
			if (Telligent.Registration.CommunityServer.FileGalleryLimit == int.MaxValue)
				allowed = true;
			else
			{
				int totalEntries = 0;
				foreach (Folder f in Folders.GetFolders(CSContext.Current.UserID, true, true, true))
				{
					totalEntries += f.TotalThreads;
				}

				allowed = totalEntries < Telligent.Registration.CommunityServer.FileGalleryLimit;
			}

			CSCache.Insert("CreateEntryAccess", allowed, CSCache.MinuteFactor * 15);
			return allowed;
		}
	}
}
