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

namespace Orciid.Media.Converters
{
	public class BitmapConverter
	{
		private const int DEFAULT_JPEG_QUALITY = 85;
		
		private BitmapConverter()
		{
		}

		public static void Register()
		{
			MimeType.RegisterExtension("bmp", "image/bmp");
			MimeType.RegisterExtension("jpg", "image/jpeg");
			MimeType.RegisterExtension("jpg", "image/pjpeg");
			MimeType.RegisterExtension("jpg", "image/jpeg");
			MimeType.RegisterExtension("tif", "image/tiff");
			MimeType.RegisterExtension("png", "image/png");
			MimeType.RegisterExtension("gif", "image/gif");

			string[] sourceformats = { "image/bmp", "image/jpeg", "image/pjpeg",
										   "image/tiff", "image/png", "image/gif" };

			FileConverter tobitmap = new FileConverter(ConvertToBitmapFile);
			foreach (string source in sourceformats)
				MediaConverter.RegisterFileConverter(
					source, 
					"image/bmp",
					Capabilities.Resize | Capabilities.AddBorder, 
					true, 
					tobitmap);

			FileConverter tojpeg = new FileConverter(ConvertToJpegFile);
			foreach (string source in sourceformats)
			{
				MediaConverter.RegisterFileConverter(
					source, 
					"image/jpeg",
					Capabilities.Resize | Capabilities.AddBorder | Capabilities.SetQuality, 
					true, 
					tojpeg);
				MediaConverter.RegisterFileConverter(
					source, 
					"image/pjpeg",
					Capabilities.Resize | Capabilities.AddBorder | Capabilities.SetQuality, 
					true, 
					tojpeg);
			}

			FileConverter totiff = new FileConverter(ConvertToTiffFile);
			foreach (string source in sourceformats)
				MediaConverter.RegisterFileConverter(
					source, 
					"image/tiff",
					Capabilities.Resize | Capabilities.AddBorder, 
					true, 
					totiff);
			
			FileConverter topng = new FileConverter(ConvertToPngFile);
			foreach (string source in sourceformats)
				MediaConverter.RegisterFileConverter(
					source, 
					"image/png",
					Capabilities.Resize | Capabilities.AddBorder, 
					true, 
					topng);
		}

		private static ImageCodecInfo GetEncoderInfo(string mimeType)
		{
			ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
			for (int j = 0; j < encoders.Length; j++)
				if (encoders[j].MimeType == mimeType)
					return encoders[j];
			return null;
		}

		private static bool ConvertToBitmapFile(string sourcefile, string targetfile,
			Parameters parameters)
		{
			using (Bitmap image = ProcessBitmap(sourcefile, parameters))
				image.Save(targetfile, ImageFormat.Bmp);
			return true;
		}

		private static bool ConvertToJpegFile(string sourcefile, string targetfile,
			Parameters parameters)
		{			
			EncoderParameters ep = new EncoderParameters();
			ep.Param[0] = new EncoderParameter(Encoder.Quality, 
				new long[] { parameters.Quality == Parameters.Unspecified ? DEFAULT_JPEG_QUALITY : parameters.Quality });
			ImageCodecInfo encoder = GetEncoderInfo("image/jpeg");
			if (encoder == null)
				return false;
			using (Bitmap image = ProcessBitmap(sourcefile, parameters))
				image.Save(targetfile, encoder, ep);
			return true;
		}

		private static bool ConvertToTiffFile(string sourcefile, string targetfile,
			Parameters parameters)
		{
			EncoderParameters ep = new EncoderParameters();
			ep.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionLZW);
			ImageCodecInfo encoder = GetEncoderInfo("image/tiff");
			if (encoder == null)
				return false;
			using (Bitmap image = ProcessBitmap(sourcefile, parameters))
				image.Save(targetfile, encoder, ep);
			return true;
		}

		private static bool ConvertToPngFile(string sourcefile, string targetfile,
			Parameters parameters)
		{
			using (Bitmap image = ProcessBitmap(sourcefile, parameters))
				image.Save(targetfile, ImageFormat.Png);
			return true;
		}

		internal static Bitmap ProcessBitmap(string sourcefile, Parameters parameters)
		{
			Bitmap image = new Bitmap(sourcefile);
			if (parameters.TargetWidth != Parameters.Unspecified &&
				parameters.TargetHeight != Parameters.Unspecified &&
				(parameters.TargetWidth != image.Width ||
				parameters.TargetHeight != image.Height))
			{
				double xscale = (image.Width > parameters.TargetWidth ?
					(double)parameters.TargetWidth / image.Width : 1);
				double yscale = (image.Height > parameters.TargetHeight ?
					(double)parameters.TargetHeight / image.Height : 1);
				int w = (xscale < yscale ? parameters.TargetWidth : (int)(image.Width * yscale));
				int h = (xscale > yscale ? parameters.TargetHeight : (int)(image.Height * xscale));

				Bitmap b = new Bitmap(
					parameters.ExactTargetSize ? parameters.TargetWidth : w,
					parameters.ExactTargetSize ? parameters.TargetHeight : h,
					PixelFormat.Format24bppRgb);
				b.SetResolution(72, 72);
				using (Graphics g = Graphics.FromImage(b))
					if (parameters.ExactTargetSize)
					{
						using (Brush brush = new SolidBrush(
									parameters.BorderColor == Color.Empty ? 
									Color.Black : parameters.BorderColor))
							g.FillRectangle(brush, 0, 0, parameters.TargetWidth, parameters.TargetHeight);
						g.DrawImage(image, 
							(parameters.TargetWidth - w) / 2, (parameters.TargetHeight - h) / 2,
							w, h);
					}
					else
						g.DrawImage(image, 0, 0, w, h);
				image.Dispose();
				image = b;
			}
			return image;
		}
	}
}
