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

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using CommunityServer.Components;

namespace CommunityServer.Galleries.Components
{

	#region GalleryImageSettings

	public class GalleryImageSettings : ICloneable
	{
		#region Properties

		#region Private

		private int width;
		private int height;
		private int quality;
		private int	brightness = -1;
		private bool maintainRatio = true;
		private bool originalSize = false;
		private bool upScale = false;
		private GalleryImageType galleryImageType = GalleryImageType.Other; 

		#endregion

		public GalleryImageType GalleryImageType
		{
			get{return galleryImageType;}
			set{galleryImageType = value;}
		}

		/// <summary>
		/// This property defines the brightness of scaled images (0-200, -1 to disable changing the brightness)
		/// </summary>
		public int Brightness
		{
			get{return brightness;}
			set
			{
				if(value < 0)
					brightness = -1;
				else if(value > 100)
					brightness = 100;
				else
					brightness = value;
			}
		}

		/// <summary>
		/// The X dimension of the image in pixels
		/// </summary>
		public int Width
		{
			get{return width;}
			set{width = value;}
		}

		/// <summary>
		/// The Y dimension of the image in pixels
		/// </summary>
		public int Height
		{
			get{return height;}
			set{height = value;}
		}

		/// <summary>
		/// This property defines the quality percentage of scaled images
		/// </summary>
		public int Quality
		{
			get{return quality;}
			set{quality = value;}
		}

		public bool MaintainRatio
		{
			get{return maintainRatio;}
			set{maintainRatio = value;}
		}

		public bool OriginalSize
		{
			get{return originalSize;}
			set{originalSize = value;}
		}

		public bool UpScale
		{
			get{return upScale;}
			set{upScale = value;}
		}

		#endregion

		public GalleryImageSettings()
		{

		}

		public GalleryImageSettings(int width, int height, int quality, bool maintainRatio, bool originalSize, bool upScale)
		{
			Width = width;
			Height = height;
			Quality = quality;
			MaintainRatio = maintainRatio;
			OriginalSize = originalSize;
			UpScale = upScale;
		}

		public GalleryImageSettings(int width, int height, int quality, bool maintainRatio)
		{
			Width = width;
			Height = height;
			Quality = quality;
			MaintainRatio = maintainRatio;
		}

		/* We shouldnt need to make these anymore because the predefined types are already in the Gallery
		public GalleryImageSettings(Gallery g, GalleryImageType imageType)
		{
			switch(imageType)
			{
				case GalleryImageType.Thumbnail:
				case GalleryImageType.Micro:
					this.Height = g.ThumbnailX;
					this.Width = g.ThumbnailY;
					this.Quality = g.ThumbnailQuality;
					this.Brightness = g.ThumbnailBrightness;
					break;
				case GalleryImageType.SecondaryThumbnail:
				case GalleryImageType.Gallery:
					this.Height = g.SecondaryThumbnailX;
					this.Width = g.SecondaryThumbnailY;
					this.Quality = g.SecondaryThumbnailQuality;
					break;
				case GalleryImageType.Slideshow:
					this.Height = g.SlideshowX;
					this.Width = g.SlideshowY;
					this.Quality = g.SlideshowQuality;
					break;
				case GalleryImageType.Details:
					this.Height = g.PictureDetailsX;
					this.Width = g.PictureDetailsY;
					this.Quality = g.PictureDetailsQuality;
					break;
				case GalleryImageType.Folder:
					//throw new ArgumentException("You cannot scale the image folder"); 
					this.Quality = 100;
					break;
				case GalleryImageType.Original:
					//throw new ArgumentException("Scaling the original image is not necessary"); 
					this.Quality = 100;
					break;
				case GalleryImageType.Other:
					//throw new ArgumentException("You must specify the height and widht for GalleryImageType.Other"); 
					this.Quality = 100;
					break;
			}


		}
			*/

		/// <summary>
		/// Uses internal defaults for settings
		/// </summary>
		/// <param name="imageType"></param>
		public GalleryImageSettings(GalleryImageType imageType, int Width, int Height, int Quality)
		{
			this.GalleryImageType = imageType;
					this.Height = Height;
					this.Width = Width;
					this.Quality = Quality;
		}

		public object Clone()
		{
			return this.MemberwiseClone();
		}
	}

	#endregion

	public class ImageHandling
	{

		public static void GetMaintainedRatio(int currentHeight, int currentWidth, GalleryImageSettings settings)
		{
			// See if we want to maintain the image ratio
			if(settings.MaintainRatio == true)
			{
				//Check to make sure we have a height and width on the attachemnet (if not check the data)
				if(currentHeight == 0 || currentWidth == 0)
					return;

				double heightRatio = (double)currentHeight / currentWidth;
				double widthRatio = (double)currentWidth / currentHeight;

				int desiredHeight = settings.Height;
				int desiredWidth = settings.Width;


				settings.Height = desiredHeight;
				if(widthRatio > 0)
					settings.Width = Convert.ToInt32(settings.Height * widthRatio);

				if (settings.Width > desiredWidth)
				{
					settings.Width = desiredWidth;
					settings.Height = Convert.ToInt32(settings.Width * heightRatio);
				}
			}

			// In some instances, we might not want to scale it larger
			if((settings.UpScale == false) && (settings.Height > currentHeight || settings.Width > currentWidth))
			{
				// TODO: Not perfect
				settings.Height = currentHeight;
				settings.Width = currentWidth;
				settings.OriginalSize = true;
				return;
			}
		}

		public static void GetMaintainedRatio(GalleryPost galleryPost, GalleryImageSettings settings)
		{
			//Check to make sure we have a height and width on the attachemnet (if not check the data)
			if(galleryPost.Attachment.Height == 0 || galleryPost.Attachment.Width == 0)
			{
				PostAttachment pa = CommonDataProvider.Instance().GetPostAttachment(galleryPost.PostID, false);
				ImageInfo imageInfo = new ImageInfo(pa.Content);
				if(!imageInfo.Check())
					return;
				galleryPost.Attachment.Height = imageInfo.Height;
				galleryPost.Attachment.Width = imageInfo.Width;
				galleryPost.Attachment.Length = pa.Content.Length;
			}

			GetMaintainedRatio(galleryPost.Attachment.Height,galleryPost.Attachment.Width, settings);
		}

		public static void ScalePicture(GalleryPost galleryPost, GalleryImageSettings settings)
		{
			if(galleryPost.IsPicture)
			{
				ImageHandling.GetMaintainedRatio(galleryPost, settings);
				string filename = GalleryConfiguration.Instance().CacheSettings.GetLocalFileName(galleryPost, settings);
				ImageHandling.ScalePicture(filename, galleryPost, settings);
			}
		}

		public static void ScalePicture(string filename, GalleryPost galleryPost, GalleryImageSettings settings)
		{
			// See the image doesn't already exist
			if(!File.Exists(filename))
			{
				// Get the image
				PostAttachment pictureData = GalleryPosts.GetPictureData(galleryPost);

				if((pictureData.Content == null) || (pictureData.Content.Length == 0))
					return;

				ScalePicture(filename, galleryPost.Gallery, pictureData, settings);

			}
		}

		/// <summary>
		/// Scales the pictureData to the specified settings and saves it to the specified filename 
		/// (if gallery is not null then the watermark is applied if present)
		/// </summary>
		public static void ScalePicture(string filename, Gallery gallery, PostAttachment pictureData, GalleryImageSettings settings)
		{
			try
			{
				Bitmap image = (Bitmap)Bitmap.FromStream(pictureData.Content, true);

				// We got here, so we do want to scale it.
				Graphics graph;
				Bitmap bitmap = new Bitmap(settings.Width, settings.Height);
				graph = Graphics.FromImage(bitmap);
				graph.InterpolationMode = InterpolationMode.HighQualityBicubic;

				// pre paint white to the background of transparent images
				graph.Clear(Color.White);

				// Set the brightness
				if(settings.Brightness != -1)
				{
					float brightness = (settings.Brightness - 50) / 100.0f;
					ImageAttributes attrs = new ImageAttributes();
					ColorMatrix colorMatrix = new ColorMatrix(new float[][] {
																				new float[] { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f },
																				new float[] { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f },
																				new float[] { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
																				new float[] { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f },
																				new float[] { brightness, brightness, brightness, 0.0f, 1.0f }
																			} );
					attrs.SetColorMatrix(colorMatrix);

					Bitmap bitmapB = new Bitmap(settings.Width, settings.Height);
					Graphics graphB = Graphics.FromImage(bitmapB);
					graphB.InterpolationMode = InterpolationMode.HighQualityBicubic;
					graphB.DrawImage(image, 0, 0, settings.Width, settings.Height);
					graph.DrawImage(bitmapB, new Rectangle(0, 0, bitmapB.Width, bitmapB.Height), 0, 0, bitmapB.Width, bitmapB.Height, GraphicsUnit.Pixel, attrs);
					graphB.Dispose();
					bitmapB.Dispose();
				}
				else
					graph.DrawImage(image, 0, 0, settings.Width, settings.Height);

				//Watermarks
				if(gallery != null && gallery.WatermarkType != WatermarkType.None)
				{
					if(gallery.WatermarkMinWidth < settings.Width || gallery.WatermarkMinHeight < settings.Height)
					{
						if(gallery.WatermarkType == WatermarkType.Text)
							addWatermarkText(ref graph, settings, gallery.WatermarkText, gallery.WatermarkPosition);
						else
							addWatermarkImage(ref graph, settings, gallery.SectionID, gallery.WatermarkPosition);
					}
				}
				// specify codec
				ImageCodecInfo codec = GetEncoderInfo("image/jpeg");

				// set image quality
				EncoderParameters eps = new EncoderParameters(1);
				eps = new EncoderParameters();
				eps.Param[0] = new EncoderParameter(Encoder.Quality, (long)settings.Quality);

				// Save to the cache
				try { bitmap.Save(filename, codec, eps); }
				catch { (new CSException(CSExceptionType.AccessDenied, "Permission to save cache file denied (" + filename + ")")).Log(); }

				bitmap.Dispose();
				graph.Dispose();
				eps.Dispose();
				image.Dispose();
			}
			catch{}
		}


		/// <summary>
		/// Gets the encoder information for the specified mimetype.  Used in imagescaling
		/// </summary>
		/// <param name="mimeType">The mimetype of the picture.</param>
		/// <returns>System.Drawing.Imaging.ImageCodecInfo</returns>
		public static ImageCodecInfo GetEncoderInfo(string mimeType)
		{
			ImageCodecInfo [] myEncoders =
				ImageCodecInfo.GetImageEncoders();

			foreach (ImageCodecInfo myEncoder in myEncoders)
				if (myEncoder.MimeType == mimeType)
					return myEncoder;
			return null;
		}

		#region Watermarks


		/// <summary>
		/// This method adds text as a watermark to the picture.
		/// </summary>
		/// <param name="picture">The picture being added to.</param>
		public static void addWatermarkText(ref Graphics picture,GalleryImageSettings settings, string Text, WatermarkPosition position)
		{
			if(Text.TrimEnd(' ').Length == 0)
				return;

			int[] sizes = new int[]{16,14,12,10,8,6,4};
			Font crFont = null;
			SizeF crSize = new	SizeF();
			for (int i=0 ;i<7; i++)
			{
				crFont = new Font("arial", sizes[i], FontStyle.Bold);
				crSize = picture.MeasureString(Text, crFont);

				if((ushort)crSize.Width < (ushort)settings.Width)
					break;
			}

			float xpos = 0;
			float ypos = 0;

			switch(position)
			{
				//Note the position we are indicating is the location for the top center of the watermark text
				case WatermarkPosition.TOP_LEFT:
					xpos = ((float)settings.Width * (float).01) + (crSize.Width / 2);
					ypos = (float)settings.Height * (float).01;
					break;
				case WatermarkPosition.TOP_RIGHT:
					xpos = ((float)settings.Width * (float).99) - (crSize.Width / 2);
					ypos = (float)settings.Height * (float).01;
					break;
				case WatermarkPosition.BOTTOM_RIGHT:
					xpos = ((float)settings.Width * (float).99) - (crSize.Width / 2);
					ypos = ((float)settings.Height * (float).99) - crSize.Height;
					break;
				case WatermarkPosition.BOTTOM_LEFT:
					xpos = ((float)settings.Width * (float).01) + (crSize.Width / 2);
					ypos = ((float)settings.Height * (float).99) - crSize.Height;
					break;
				case WatermarkPosition.MIDDLE:
					xpos = ((float)settings.Width / 2);
					ypos = ((float)settings.Height / 2) - (crSize.Height / 2);
					break;
			}

			StringFormat StrFormat = new StringFormat();
			StrFormat.Alignment = StringAlignment.Center;

			SolidBrush semiTransBrush2 = new SolidBrush(Color.FromArgb(153, 0, 0,0));
			picture.DrawString(Text, crFont, semiTransBrush2, xpos+1, ypos+1, StrFormat);

			SolidBrush semiTransBrush = new SolidBrush(Color.FromArgb(153, 255, 255, 255));
			picture.DrawString(Text, crFont, semiTransBrush, xpos, ypos, StrFormat);


			semiTransBrush2.Dispose();
			semiTransBrush.Dispose();
		}


		/// <summary>
		/// This method adds an image as a watermark to the picture.
		/// </summary>
		/// <param name="picture">The picture being added to.</param>
		public static void addWatermarkImage(ref Graphics picture, GalleryImageSettings settings, int galleryID, WatermarkPosition position)
		{
			string filename = GalleryConfiguration.Instance().CacheSettings.GetLocalWatermarkFileName(galleryID);
			if(!File.Exists(filename))
				return;

			Image watermark = new Bitmap(filename);

			//TODO: scale the watermark
			//ImageHandling.GetMaintainedRatio(galleryPost, settings);
			//ImageHandling.ScalePicture(filename, galleryPost, settings) ;

			ImageAttributes imageAttributes = new ImageAttributes();
			ColorMap colorMap = new ColorMap();

			colorMap.OldColor = Color.FromArgb(255, 0, 255, 0);
			colorMap.NewColor = Color.FromArgb(0, 0, 0, 0);
			ColorMap[] remapTable = {colorMap};

			imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);

			float[][] colorMatrixElements = {
												new float[] {1.0f,  0.0f,  0.0f,  0.0f, 0.0f},
												new float[] {0.0f,  1.0f,  0.0f,  0.0f, 0.0f},
												new float[] {0.0f,  0.0f,  1.0f,  0.0f, 0.0f},
												new float[] {0.0f,  0.0f,  0.0f,  0.3f, 0.0f},
												new float[] {0.0f,  0.0f,  0.0f,  0.0f, 1.0f}
											};

			ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);

			imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

			int xpos = 0;
			int ypos = 0;

			switch(position)
			{
				//The dimension we are specifying is the top right location of the watermark image
				case WatermarkPosition.TOP_LEFT:
					xpos = 10;
					ypos = 10;
					break;
				case WatermarkPosition.TOP_RIGHT:
					xpos = ((settings.Width - watermark.Width) - 10);
					ypos = 10;
					break;
				case WatermarkPosition.BOTTOM_RIGHT:
					xpos = ((settings.Width - watermark.Width) - 10);
					ypos = settings.Height - watermark.Height - 10;
					break;
				case WatermarkPosition.BOTTOM_LEFT:
					xpos = 10;
					ypos = settings.Height - watermark.Height - 10;
					break;
				case WatermarkPosition.MIDDLE:
					xpos = (settings.Width / 2) - (watermark.Width / 2);
					ypos = (settings.Height / 2) - (watermark.Height / 2);
					break;
			}

			picture.DrawImage(watermark, new Rectangle(xpos, ypos, watermark.Width, watermark.Height), 0, 0, watermark.Width, watermark.Height, GraphicsUnit.Pixel, imageAttributes);


			watermark.Dispose();
			imageAttributes.Dispose();
		}

		#endregion

		#region GetURLS
		public static string GetPictureUrl(GalleryPost galleryPost, GalleryImageSettings settings)
		{
			try
			{
				if(galleryPost.IsPicture)
				{
					ScalePicture(galleryPost, settings);

					if(settings.GalleryImageType == GalleryImageType.Other)
						return GalleryUrls.Instance().PictureUrl(((Gallery)galleryPost.Section).ApplicationKey, galleryPost.PostID, settings.Width, settings.Height);
					else
						return GalleryUrls.Instance().PictureUrl(galleryPost, settings.GalleryImageType, settings.Width, settings.Height);  
					//else
					//	return GalleryUrls.Instance().PictureUrl(galleryPost);
				}
				
			}
			catch{}
			return "";
			
		}
		#endregion

	}
}
