//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
using System;
using System.Drawing;
using System.Drawing.Imaging;
using Timing;

namespace PictureToCD
{

	public class ProgressEventArgs: EventArgs
	{
		int count;
		int limit;

		public ProgressEventArgs(int count, int limit)
		{
			this.count = count;
			this.limit = limit;
		}

		public int Count
		{
			get
			{
				return(count);
			}
		}

		public int Limit
		{
			get
			{
				return(limit);
			}
		}
	}

	/// <summary>
	/// Podsumowanie dla ResizableBitmap.
	/// </summary>
	public unsafe class ResizableBitmap
	{
		Bitmap bitmap;
			// W celu zapewnienia szybkiego dostępu używane są trzy elementy.
		int width;
		BitmapData bitmapData = null;
		Byte* pBase = null;

		public static Counter counter = new Counter();
		public static int GetPixelCount = 0;

		public delegate void ProgressEventHandler(object sender, ProgressEventArgs e);

		public event ProgressEventHandler Progress;

		protected void OnProgress(int count, int limit)
		{
			if (Progress != null)
			{
				Progress(this, new ProgressEventArgs(count, limit));
			}
		}

		public ResizableBitmap(string filename)
		{
			//Console.WriteLine("Ładowanie: {0}", filename);
			// Uwaga: Błąd - próba załadowanie nieistniejącego obrazka
			// prowadzi do błędu braku pamięci.
			bitmap = (Bitmap) Image.FromFile(filename, true);

			//Console.WriteLine("Format: {0}", bitmap.PixelFormat);
			//Console.WriteLine(PixelSize);
		}

		public ResizableBitmap(Bitmap bitmap)
		{
			this.bitmap = bitmap;
		}

		public void Save(string filename)
		{
			bitmap.Save(filename, ImageFormat.Jpeg);
		}

		public void Dispose()
		{
			bitmap.Dispose();
		}

		public Bitmap Bitmap
		{
			get
			{
				return(bitmap);
			}
		}

		public Point PixelSize
		{
			get
			{
				GraphicsUnit unit = GraphicsUnit.Pixel;
				RectangleF bounds = bitmap.GetBounds(ref unit);

				return new Point((int) bounds.Width, (int) bounds.Height);
			}
		}

		public ResizableBitmap ResizeToWidth(int width)
		{
			Point size = PixelSize;
			//Console.WriteLine("Wielkość: {0}", size);
			int height = (int) (((float) width / (float) size.X) * size.Y);
			//Console.WriteLine("{0} {1}", width, height);

			return 
				Resize(width, height);
		}

		public ResizableBitmap ResizeToWidthFast(int width)
		{
			Point size = PixelSize;
			//Console.WriteLine("Wielkość: {0}", size);
			int height = (int) (((float) width / (float) size.X) * size.Y);
			//Console.WriteLine("{0} {1}", width, height);

			return 
				ResizeFast(width, height);
		}

		public ResizableBitmap Resize(float factor)
		{
			Point size = PixelSize;

			return
				Resize((int) (size.X * factor), 
				(int) (size.Y * factor));
		}

		public ResizableBitmap ResizeFast(int xSize, int ySize)
		{
			Bitmap destination = new Bitmap(xSize, ySize);

			Graphics gfx = Graphics.FromImage((Image) destination);

			gfx.DrawImage(bitmap, 0, 0, xSize, ySize);
			return new ResizableBitmap(destination);
		}		 

		public ResizableBitmap Resize(int xSize, int ySize)
		{
			LockBitmap(false);
			//Console.WriteLine("Zmień wielkość na {0}, {1}", xSize, ySize);
			Bitmap newBitmapObject = new Bitmap(xSize, ySize, PixelFormat.Format24bppRgb);
			ResizableBitmap newBitmap = new ResizableBitmap(newBitmapObject);
			newBitmap.LockBitmap(true);

			// Zmiana wielkości bitmapy.
			
			Point currentSize = this.PixelSize;

			//Console.WriteLine("Zmiana wielkości z ({0}, {1}) na ({2}, {3})", currentSize.X, currentSize.Y, xSize, ySize);
			float xCover = currentSize.X / xSize;
			float yCover = currentSize.Y / ySize;

			int progressCount = xSize / 10;
			for (int x = 0; x < xSize; x++)
			{
				//if (x % progressCount == 0)
				//	OnProgress(x + 1, xSize);

				//Console.WriteLine("{0}", x);
				for (int y = 0; y < ySize; y++)
				{
					//Console.WriteLine("{0} {1}", x, y);
					PixelData newColor = 
						Resample(x * xCover, (x + 1) * xCover - 1,
						y * yCover, (y + 1) * yCover - 1);
					newBitmap.SetPixel(x, y, newColor);
				}
			}
			UnlockBitmap();
			newBitmap.UnlockBitmap();
			return newBitmap;
		}

				PixelData Resample(float x1, float x2, float y1, float y2)
		{
			
			int x1int = (int) x1;

			if ((int) x1 != x1)
				x1int++;

			int y1int = (int) y1;

			if ((int) y1 != y1)
				y1int++;

			int x2int = (int) x2; 
			int y2int = (int) y2;
			float x1border = x1int - x1;
			float x2border = x2 - x2int;
			float y1border = y1int - y1;
			float y2border = y2 - y2int;

			ColorAdder colorAdder = new ColorAdder();
			PixelData *pPixel;

			for (int x = x1int; x <= x2int; x++)
			{
				for (int y = y1int; y <= y2int; y++)
				{
					pPixel = (PixelData*) (pBase + Off(x, y));
					colorAdder.Add(pPixel, 1.0f);
				}
			}

			PixelData pixel;
			
			for (int y = y1int; y <= y2int; y++)
			{
				pixel = PixSample(x1, y, x1border);
				colorAdder.Add(pixel, x1border);

				pixel = PixSample(x2, y, x2border);
				colorAdder.Add(pixel, x2border);
			}

			for (int x = x1int; x <= x2int; x++)
			{
				pixel = PixSample(x, y1, y1border);
				colorAdder.Add(pixel, y1border);

				pixel = PixSample(x, y2, y2border);
				colorAdder.Add(pixel, y2border);
			}

			pixel = PixSample(x1, y1, x1border * y1border);
			colorAdder.Add(pixel, x1border * y1border);
			pixel = PixSample(x1, y2, x1border * y2border);
			colorAdder.Add(pixel, x1border * y2border);
			pixel = PixSample(x2, y1, x2border * y1border);
			colorAdder.Add(pixel, x2border * y1border);
			pixel = PixSample(x2, y2, x2border * y2border);
			colorAdder.Add(pixel, x2border * y2border);

			return colorAdder.Pixel;
		}

		// próbkowanie koloru w określonych miejscach
		PixelData PixSample(float x, float y, float factor)
		{
			if (factor == 0.0f)
				return new PixelData();

			ColorAdder colorAdder = new ColorAdder();

			int xlow = (int) x;
			int xhigh = xlow + 1;
			float xlowf = x - xlow;
			float xhighf = xhigh - x;

			int ylow = (int) y;
			int yhigh = ylow + 1;
			float ylowf = y - ylow;
			float yhighf = yhigh - y;

			colorAdder.Add(*(PixelData*)(pBase + Off(xlow, ylow)), xlowf * ylowf);
			colorAdder.Add(*(PixelData*)(pBase + Off(xlow, yhigh)), xlowf * yhighf);
			colorAdder.Add(*(PixelData*)(pBase + Off(xhigh, ylow)), xhighf * ylowf);
			colorAdder.Add(*(PixelData*)(pBase + Off(xhigh, yhigh)), xhighf * yhighf);

			return colorAdder.Pixel;
		}

		Color GetPixelSlow(int x, int y)
		{
			//counter.Start();
			//Color color = new Color();
			Color color = bitmap.GetPixel(x, y);
			GetPixelCount++;
			//counter.Stop();
			return color;
		}

		public void LockBitmap(bool readWrite)
		{
			GraphicsUnit unit = GraphicsUnit.Pixel;
			RectangleF boundsF = bitmap.GetBounds(ref unit);
			//Console.WriteLine(boundsF);
			Rectangle bounds = new Rectangle((int) boundsF.X,
				(int) boundsF.Y,
				(int) boundsF.Width,
				(int) boundsF.Height);

				// Obliczenie liczby bajtów w wierszu, a następnie
				// zaokrąglenie do wielokrotności 4 bajtów. 
			width = (int) boundsF.Width * sizeof(PixelData);
			if (width % 4 != 0)
			{
				width = 4 * (width / 4 + 1);
			}
			//Console.WriteLine("W: {0}", width * sizeof(PixelData));
			//if (width % 2 == 1)
			//	width += 1;

			if (readWrite)
			{
				bitmapData = 
					bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			}
			else
			{
				bitmapData = 
					bitmap.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
			}

			pBase = (Byte*) bitmapData.Scan0.ToPointer();
		}

		public int Off(int x, int y)
		{
			return y * width + x * sizeof(PixelData);
		}


		public unsafe PixelData GetPixel(int x, int y)
		{
			return *(PixelData*) (pBase + Off(x, y));
		}

		public unsafe void SetPixel(int x, int y, PixelData pixel)
		{
			*(PixelData*)(pBase + Off(x,y)) = pixel;
		}

		public void UnlockBitmap()
		{
			bitmap.UnlockBits(bitmapData);
			bitmapData = null;
			pBase = null;
		}
	}
}
