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

using System;
using System.Collections;
using System.Data;
using System.Data.SqlTypes;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using CommunityServer.Components;
using CommunityServer.Configuration;
using Telligent.CommunityServer.MetadataExtractor;
using Telligent.CommunityServer.MetadataExtractor.Jpeg;
using Telligent.CommunityServer.MetadataExtractor.Lang;
using Directory = Telligent.CommunityServer.MetadataExtractor.Directory;
using Tag = Telligent.CommunityServer.MetadataExtractor.Tag;

namespace CommunityServer.Galleries.Components
{
	/// <summary>
	/// Summary description for GalleryDataProvider.
	/// </summary>
	public abstract class GalleryDataProvider
	{

		public static readonly string GalleryDataProviderName = "GalleryDataProvider";

		#region Instance

		private static GalleryDataProvider _defaultInstance = null;

		static GalleryDataProvider()
		{
			CreateDefaultCommonProvider();
		}

		/// <summary>
		/// Returns an instance of the user-specified data provider class.
		/// </summary>
		/// <returns>An instance of the user-specified data provider class.  This class must inherit the
		/// CommonDataProvider interface.</returns>
		public static GalleryDataProvider Instance() 
		{
			return _defaultInstance;
		}

		public static GalleryDataProvider Instance (Provider dataProvider) 
		{
			GalleryDataProvider fdp = CSCache.Get(dataProvider.Name) as GalleryDataProvider;
			if(fdp == null)
			{
				fdp = DataProviders.Invoke(dataProvider) as GalleryDataProvider;
				CSCache.Max(dataProvider.Name,fdp);
			}
			return fdp;
		}

		/// <summary>
		/// Creates the Default CommonDataProvider
		/// </summary>
		private static void CreateDefaultCommonProvider()
		{
			// Get the names of the providers
			//
			CSConfiguration config = CSConfiguration.GetConfig();

			// Read the configuration specific information
			// for this provider
			//
			Provider sqlForumsProvider = (Provider) config.Providers[GalleryDataProviderName];

			// Read the connection string for this provider
			//
            
			_defaultInstance = DataProviders.CreateInstance(sqlForumsProvider) as GalleryDataProvider;
		}
        
		#endregion

		public abstract Hashtable GetGalleries(bool mergePermissions);
		public abstract void UpdatePictureCategories(int galleryID, int postID, string[] categoryList);
		public abstract ArrayList GetPictureCategories(int postID);
		public abstract void CreatePicture(GalleryPost galleryPost);
		public abstract void UpdatePicture(GalleryPost galleryPost, int editedByUserID);
		public abstract void DeletePicture(int sectionID, int threadID);
		public abstract void DeletePost(int postID, int userID);
		public abstract ThreadSet GetPictures(GalleryThreadQuery query);
		public abstract GalleryPost GetPicture(int postID, int userID, bool includeCategories);
		public abstract int GetRandomPostID(int sectionID, int categoryID);
		public abstract PostSet GetPosts(GalleryThreadQuery query);
		public abstract void CreateComment(GalleryPost comment);
		public abstract ArrayList GetGalleryPermissions(int id);
		public abstract bool ApprovePost(int postID, int userIDApprovedBy);
        public abstract void UpdatePictureMetadata(int postID, Metadata metadata);
        public abstract Metadata GetPictureMetadata(int postID);
		public abstract Hashtable GetAllPictures(int galleryID);
		public abstract PostSet SearchReindexPosts(int setsize, int settingsID);
		public abstract ThreadSet SearchIndexPosts (int setsize, GalleryThreadQuery query);
		public abstract PostSet GetThreadsTrackedByUser(int SectionID, int UserID);
		//public abstract SearchResultSet GetSearchResults(SearchBarrelCriteria barrelCriteria);
		public abstract ArrayList GetRoleQuotas();
		public abstract void CreateUpdateDeleteGalleryRoleQuota(GalleryRoleQuota quota, DataProviderAction action);
		

		#region Populate

		#region Statistics

		/// <summary>
		/// Populates an existing Gallery object
		/// </summary>
		public static void PopulateGalleryPostTypeStatistics(IDataRecord dr, Gallery g)
		{
			GalleryPostType galleryPostType = (GalleryPostType)Globals.SafeInt(dr["ApplicationPostType"].ToString(),0); 

			switch(galleryPostType)
			{
				case GalleryPostType.Comment:
					g.CommentCount = (int)dr["PostCount"];
					break;
				case GalleryPostType.Image:
					g.PostCount = (int)dr["PostCount"];
					break;
				case GalleryPostType.Trackback:
					g.TrackbackCount = (int)dr["PostCount"];
					break;
			}
		}

		#endregion


		public static GalleryPost PopulateGalleryPostFromIDataRecord(IDataRecord dr) 
		{
			GalleryPost post = new GalleryPost();

			// Populate picture
			//

			post.SectionID =				(int) dr["SectionID"];
			post.PostID =				(int) dr["PostID"];

			post.Views =					(int) dr["TotalViews"];
			//galleryPost.AggViews =			(int) dr["AggViews"];
			post.ThreadID =				(int) dr["ThreadID"];
			post.Subject =				(string) dr["Subject"];
			post.Body =					(string) dr["Body"];
			post.Username =					dr["PostAuthor"].ToString().Trim().Length > 0 ? (string) dr["PostAuthor"] : (string) dr["ThreadPostAuthor"];
			post.AuthorID =				(int) dr["UserID"];
		    //populate the post UserID as well as the IThread AuthorID
		    post.UserID =               (int)dr["UserID"];

		    post.PostDate =				(DateTime) dr["PostDate"];

			post.ThreadDate =			(DateTime) dr["ThreadDate"];
			post.IsLocked =				(bool) dr["IsLocked"];
			post.IsApproved =			(bool)dr["IsApproved"];
			post.FormattedBody =			(string) dr["FormattedBody"];

			post.PostLevel =				(int)dr["PostLevel"];
			post.ParentID =				(int)dr["ParentID"];

			post.StickyDate =			(DateTime) dr["StickyDate"];
			post.IsSticky =				(bool) dr["IsSticky"];
			post.SortOrder =				(int) dr["SortOrder"];
			post.SettingsID =			(int) dr["SettingsID"];
           
			post.PostConfig =			(GalleryPostConfig) (int)dr["PostConfiguration"];
			post.GalleryPostType =		(GalleryPostType) dr["ApplicationPostType"];
			
			//posts from cs versions below 2.0 usertime might be null
			try{post.UserTime =               (DateTime) dr["UserTime"];}
			catch{post.UserTime =              post.PostDate;}
            
			post.Name = dr["PostName"] as string;

			post.RatingSum =				(int) dr["RatingSum"];
			post.TotalRatings =			(int) dr["TotalRatings"];

			SerializerData data = CommonDataProvider.PopulateSerializerDataIDataRecord(dr, SerializationType.Post);
			post.SetSerializerData(data);

			// Fields for ThreadSet
			try
			{
				post.Replies =                (int) dr["TotalReplies"];
				post.MostRecentPostID =       (int) dr["MostRecentPostID"];
				post.MostRecentPostAuthor =   (string) dr["MostRecentPostAuthor"];
				post.MostRecentPostAuthorID = (int) dr["MostRecentPostAuthorID"];
				post.Status =                 (ThreadStatus) dr["ThreadStatus"];
				post.EmoticonID				= (int) dr["ThreadEmoticonID"];
			}
			catch { }

			try { post.AttachmentFilename	= (string) dr["AttachmentFilename"]; }
			catch { }

			try
			{
				if(post.AttachmentFilename != null)
					post.Attachment = CommonDataProvider.PopulatePostAttachmentMetaDataFromIDataRecord(dr);

				if(post.Attachment.Height == 0 && post.Height > 0)
					post.Attachment.Height = post.Height;
				if(post.Attachment.Width == 0 && post.Width > 0)
					post.Attachment.Width = post.Width;

			}
			catch{};

			// Fields for just a Post
			try
			{
				post.Replies				=   (int) dr["Replies"];
				post.PostLevel			=	(int)dr["PostLevel"];
				post.SortOrder			=	(int) dr["SortOrder"];
				post.UserHostAddress		=	(string) dr["IPAddress"];
				post.PostType			=	(PostContentType) dr["PostType"];
				post.EmoticonID			=	(int) dr["EmoticonID"];
				post.IsTracked			=	(bool) dr["UserIsTrackingThread"];
				post.ThreadIDNext		=	(int) dr["NextThreadID"];
				post.ThreadIDPrev		=	(int) dr["PrevThreadID"];

				if(dr["EditNotes"] != DBNull.Value)
					post.EditNotes = (string) dr["EditNotes"];
			}
			catch { }

			post.Points = (int) dr["PostPoints"];

			return post;
		}

		public static GalleryPost PopulateGalleryPostFromDataRow(DataRow dr, DataRelation relationShip)
		{
			// TDD 01/22/2006
			// The following line was removed since you cant cast a DataRow to an IDataRecord. There is no
			// way to go from a DataRow to an IDataRecord so I'm having to copy the code from the
			// PopulateGalleryFromIDataRecord method to this one.
			// 			GalleryPost post = PopulateGalleryPostFromIDataRecord((IDataRecord)dr);
			
			// Populate picture
			//

			GalleryPost post = new GalleryPost();

			#region Populate post

			post.SectionID =			(int) dr["SectionID"];
			post.PostID =				(int) dr["PostID"];

			post.Views =				(int) dr["TotalViews"];
			//galleryPost.AggViews =		(int) dr["AggViews"];
			post.ThreadID =				(int) dr["ThreadID"];
			post.Subject =				(string) dr["Subject"];
			post.Body =					(string) dr["Body"];
			post.Username =					dr["PostAuthor"].ToString().Trim().Length > 0 ? (string) dr["PostAuthor"] : (string) dr["ThreadPostAuthor"];
			post.AuthorID =				(int) dr["UserID"];
            
		    //populate the post UserID as well as the IThread AuthorID
		    post.UserID =               (int)dr["UserID"];

		    post.PostDate =				(DateTime) dr["PostDate"];

			post.ThreadDate =			(DateTime) dr["ThreadDate"];
			post.IsLocked =				(bool) dr["IsLocked"];
			post.IsApproved =			(bool)dr["IsApproved"];
			post.FormattedBody =		(string) dr["FormattedBody"];

			post.PostLevel =			(int)dr["PostLevel"];
			post.ParentID =				(int)dr["ParentID"];

			post.StickyDate =			(DateTime) dr["StickyDate"];
			post.IsSticky =				(bool) dr["IsSticky"];
			post.SortOrder =			(int) dr["SortOrder"];
			post.SettingsID =			(int) dr["SettingsID"];
           
			post.PostConfig =			(GalleryPostConfig) (int)dr["PostConfiguration"];
			post.GalleryPostType =		(GalleryPostType) dr["ApplicationPostType"];
			
			//posts from cs versions below 2.0 usertime might be null
			try{post.UserTime =               (DateTime) dr["UserTime"];}
			catch{post.UserTime =              post.PostDate;}
            
			post.Name = dr["PostName"] as string;

			post.RatingSum =			(int) dr["RatingSum"];
			post.TotalRatings =			(int) dr["TotalRatings"];

			SerializerData data = CommonDataProvider.PopulateSerializerDataFromDataRow(dr, SerializationType.Post);
			post.SetSerializerData(data);

			// Fields for ThreadSet
			try
			{
				post.Replies =                (int) dr["TotalReplies"];
				post.MostRecentPostID =       (int) dr["MostRecentPostID"];
				post.MostRecentPostAuthor =   (string) dr["MostRecentPostAuthor"];
				post.MostRecentPostAuthorID = (int) dr["MostRecentPostAuthorID"];
				post.Status =                 (ThreadStatus) dr["ThreadStatus"];
				post.EmoticonID				= (int) dr["ThreadEmoticonID"];
			}
			catch { }

			try { post.AttachmentFilename	= (string) dr["AttachmentFilename"]; }
			catch { }

			try
			{
				if(post.AttachmentFilename != null)
					post.Attachment = CommonDataProvider.PopulatePostAttachmentMetaDataFromIDataRow(dr);

				if(post.Attachment.Height == 0 && post.Height > 0)
					post.Attachment.Height = post.Height;
				if(post.Attachment.Width == 0 && post.Width > 0)
					post.Attachment.Width = post.Width;

			}
			catch{};

			// Fields for just a Post
			try
			{
				post.Replies				=   (int) dr["Replies"];
				post.PostLevel			=	(int)dr["PostLevel"];
				post.SortOrder			=	(int) dr["SortOrder"];
				post.UserHostAddress		=	(string) dr["IPAddress"];
				post.PostType			=	(PostContentType) dr["PostType"];
				post.EmoticonID			=	(int) dr["EmoticonID"];
				post.IsTracked			=	(bool) dr["UserIsTrackingThread"];
				post.ThreadIDNext		=	(int) dr["NextThreadID"];
				post.ThreadIDPrev		=	(int) dr["PrevThreadID"];

				if(dr["EditNotes"] != DBNull.Value)
					post.EditNotes = (string) dr["EditNotes"];
			}
			catch { }

			post.Points = (int) dr["PostPoints"];

			#endregion
			
			DataRow[] categories = (dr).GetChildRows(relationShip);
			if(categories != null && categories.Length > 0)
			{
				post.Categories = new string[categories.Length];
				for(int i = 0; i<categories.Length; i++)
				{
					post.Categories[i] = categories[i]["Name"] as string;
				}
			}

			return post;
		}

        public static void PopulateMetadataFromIDataRecord(IDataRecord dr, Metadata metadata)
        {
            try
            {
                string fullType = (string)dr["MetaKey"];
                if (fullType.IndexOf(":") == -1)
                    return;

                string directoryType = fullType.Substring(0, fullType.IndexOf(":"));
                int tagType = int.Parse(fullType.Substring(fullType.IndexOf(":") + 1));

                Type type = Type.GetType(directoryType + ", Telligent.CommunityServer.Metadata.Extractor");

                Directory directory = metadata.GetDirectory(type);
                object obj = ReadMetadataValue((string)dr["MetaType"], (string)dr["MetaValue"]);
                if (obj != null)
                    directory.SetObject(tagType, obj);
            }
            catch { }
        }

        private static object ReadMetadataValue(string type, string val)
        {
            object obj;

            if (type.IndexOf("JpegComp") != -1)
                return null;
            if (Globals.IsNullorEmpty(val))
                return null;
            else if (type.EndsWith("[]"))
            {
                string newType = type.Replace("[]", "");
                if (val.EndsWith(","))
                    val = val.Substring(0, val.Length - 1);
                string[] parts = val.Split(',');

                if (type.EndsWith("Rational[]"))
                    type += ", Telligent.CommunityServer.Metadata.Extractor";
                Type aType = Type.GetType(type);
                ConstructorInfo[] ci = aType.GetConstructors();
                obj = ci[0].Invoke(new object[] { parts.Length });

                for (int i = 0; i < parts.Length; i++)
                {
                    object objN = ReadMetadataValue(newType, parts[i]);
                    if (objN != null)
                        ((Array)obj).SetValue(objN, i);
                }
            }
            else if (type.EndsWith("Rational"))
                obj = new Rational(val);
            else if (type.EndsWith("JpegComponent"))
            {
                string[] parts = val.Split(',');
                obj = new JpegComponent(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
            }
            else
                obj = Convert.ChangeType(val, Type.GetType(type));
            return obj;
        }

		#endregion

		#region Helpers

		public static object ConvertCategoriesToXML(string[] Categories)
		{
			if(Categories == null || Categories.Length == 0)
				return DBNull.Value;

			StringWriter sw = new StringWriter();
			XmlTextWriter writer = new XmlTextWriter(sw);
			writer.WriteStartElement("Categories");
			foreach (string category in Categories)
			{
				writer.WriteElementString("Category", category);
			}
			writer.Close();
			return sw.ToString();
		}

        public static object ConvertMetadataToXML(Metadata metadata)
        {
            if (metadata == null || metadata.GetDirectoryCount() == 0)
                return DBNull.Value;

            StringWriter sw = new StringWriter();
            XmlTextWriter writer = new XmlTextWriter(sw);
            writer.WriteStartElement("entries");

            foreach (Directory directory in metadata.DirectoryList)
            {
                IEnumerator tagsIterator = directory.GetTagIterator();

                while (tagsIterator.MoveNext())
                {
                    Tag aTag = (Tag)tagsIterator.Current;

                    if (aTag.GetTagName().IndexOf("Unknown") >= 0)
                        continue;

                    int tagType = aTag.GetTagType();
                    object obj = directory.GetObject(tagType);

                    if (obj is byte[])
                        continue;

                    writer.WriteStartElement("entry");
                    writer.WriteAttributeString("key", directory.GetType().FullName + ":" + tagType);
                    writer.WriteAttributeString("type", obj.GetType().FullName);
                    writer.WriteAttributeString("value", ConvertMetadataValue(obj));

                    writer.WriteEndElement();
                }
            }

            writer.Close();

            return sw.ToString();
        }

        private static string ConvertMetadataValue(object obj)
        {
            if (obj is DateTime)
                return ((DateTime)obj).ToString("yyyy-MM-dd HH:mm:ss");
            else if (obj is Array)
            {
                Array objArray = (Array)obj;
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < (objArray.Length - 1); i++)
                    sb.AppendFormat("{0},", objArray.GetValue(i));
                sb.Append(objArray.GetValue(objArray.Length - 1));
                return sb.ToString();
            }
            else
                return Convert.ToString(obj);
        }
        
	    public static GalleryRoleQuota PopulateGalleryRoleQuotaFromIDataReader(IDataReader dr)
		{
			GalleryRoleQuota quota	= new GalleryRoleQuota((Guid)(dr["RoleID"]));

			//because the db can hold a big int lets range this
			SqlInt64 diskQuota = SqlInt64.Parse(dr["DiskQuota"].ToString()) ;
			if(diskQuota > int.MaxValue)
				diskQuota = int.MaxValue;
			else if (diskQuota < int.MinValue)
				diskQuota = int.MinValue;

			quota.DiskQuota		= Int32.Parse(diskQuota.ToString()) ;
			
			quota.ImageQuota	= Convert.ToInt32(dr["ImageQuota"]);

			return quota;
		}

		#endregion

	}
}
