using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
// using System.Threading.Tasks;
using System.Threading;


namespace MainProject.Libs
{
	/// <summary>
	/// Image filtering class for 24bpp RGB images.
	/// </summary>
	public class cImageFilter
	{
		Bitmap bmp = null;
		BitmapData bData = null;
		int stride = 0;
		int rowOffset = 0;
		int rowWidth = 0;
		
		int BmpWidth = 0;
		int BmpHeight = 0;
		
		System.IntPtr Scan0;
		int bytesPixel = 3;
		
		Bitmap bmpSave = null;
		
		const int posB = 0;
		const int posG = 1;
		const int posR = 2;
		const int posA = 3;
		
		public cImageFilter(ref Bitmap newBmp, int colorBits)
		{
			bytesPixel = colorBits / 8;
			bmp = newBmp;
			LockImage();
		}
		
		public cImageFilter(string srcFile, int colorBits)
		{
			bytesPixel = colorBits / 8;
			
			FileStream fs = File.OpenRead(srcFile);
			bmp = (Bitmap) Bitmap.FromStream(fs);
			fs.Close();
			LockImage();
		}

		~cImageFilter() {
			UnlockImage();
			if (bmpSave != null) {
				bmpSave.Dispose();
				bmpSave = null;
			}
		}
		
		
		public void LockImage() {
			if (bData != null) UnlockImage();
			if (bytesPixel == 3) bData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			if (bytesPixel == 4) bData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
			stride = bData.Stride;
			Scan0 = bData.Scan0;
			rowOffset = stride - bmp.Width * bytesPixel;
			rowWidth = bmp.Width * bytesPixel;

			BmpWidth = bmp.Width;
			BmpHeight = bmp.Height;
		}

		public void UnlockImage() {
			try {
				if (bData != null) bmp.UnlockBits(bData);
				bData = null;
			} catch {}
		}

		public Bitmap GetImage()
		{
			return bmp;
		}
		
		public void Save(string dstFile) {
			bmp.UnlockBits(bData);
			bmpSave = new Bitmap(bmp);
			switch (dstFile.ToLower().Substring(dstFile.Length - 3)) {
				case "jpg":
					bmpSave.Save(dstFile, ImageFormat.Jpeg);
					break;
				case "png":
					bmpSave.Save(dstFile, ImageFormat.Png);
					break;
				case "gif":
					bmpSave.Save(dstFile, ImageFormat.Gif);
					break;
				case "bmp":
					bmpSave.Save(dstFile, ImageFormat.Bmp);
					break;
				case "tif":
					bmpSave.Save(dstFile, ImageFormat.Tiff);
					break;
			}
			bmpSave = null;
		}

		public void Save(System.Drawing.Image img, string dstFile) {
			switch (dstFile.ToLower().Substring(dstFile.Length - 3)) {
				case "jpg":
					img.Save(dstFile, ImageFormat.Jpeg);
					break;
				case "png":
					img.Save(dstFile, ImageFormat.Png);
					break;
				case "gif":
					img.Save(dstFile, ImageFormat.Gif);
					break;
				case "bmp":
					img.Save(dstFile, ImageFormat.Bmp);
					break;
				case "tif":
					img.Save(dstFile, ImageFormat.Tiff);
					break;
			}
		}


		// p[0] = BLUE, p[1] = GREEN, p[2] = RED
		public void Negative() {

			unsafe
			{
				byte * p = (byte *)(void *)Scan0;
				for(int y=0; y < bmp.Height; ++y)
				{
					for (int x = 0; x < rowWidth; ++x)
					{
						p[0] ^= 255;
						++p;
					}
					p += rowOffset;
				}
			}			
		}
		
		public void Gray() {

			unsafe
			{
				byte * p = (byte *)(void *)Scan0;
				for(int y=0; y < bmp.Height; ++y)
				{
					for (int x = 0; x < rowWidth; x += bytesPixel)
					{
						p[0] = p[1] = p[2] = (byte) (.299 * p[2] + p[1] * .587 + p[0] * .114);
						p += bytesPixel;
					}
					p += rowOffset;
				}
			}			
		}

		public byte GetPixel(int x, int y) {
			unsafe {
				byte * p = (byte *)(void *)Scan0;
				return p[(y * stride) + (bytesPixel * x)];
			}
		}
		public void SetPixel(int x, int y, byte c) {
			unsafe {
				byte * p = (byte *)(void *)Scan0;
				p += (y * stride) + (bytesPixel * x);
				p[0] = p[1] = p[2] = c;
			}	
		}
		
		
		public void BarcodeEnhanceHorizontal() {
			byte minV = 255;
			byte maxV = 0;
			
			unsafe
			{
				byte * p = (byte *)(void *)Scan0;
				for(int y=0; y < bmp.Height; ++y)
				{
					for (int x = 0; x < rowWidth; x += bytesPixel)
					{
						minV = Math.Min(minV, p[0]);
						maxV = Math.Max(maxV, p[0]);
						p += bytesPixel;
					}
					p += rowOffset;
				}
				
				byte th = (byte) (((maxV - minV) * 0.4) + minV);
				byte nearBytes = 5;
				
				for (int y = 0; y < BmpHeight; ++y) {
					for (int x = 8; x < BmpWidth - 8; ++x) {
						if (GetPixel(x,y) > th) continue;
						int count = 0;
						for (int k = 1; k <= nearBytes; ++k) {
							if (GetPixel(x-k, y) <= th) count++;
						}
						if (count == nearBytes) SetPixel(x,y, minV);
					}
				}
			}
		}

		public void BarcodeEnhanceVertical() {
			byte minV = 255;
			byte maxV = 0;
			
			unsafe
			{
				byte * p = (byte *)(void *)Scan0;
				for(int y=0; y < bmp.Height; ++y)
				{
					for (int x = 0; x < rowWidth; x += bytesPixel)
					{
						minV = Math.Min(minV, p[0]);
						maxV = Math.Max(maxV, p[0]);
						p += bytesPixel;
					}
					p += rowOffset;
				}
				
				//rendo più scuri
				byte th = (byte) (((maxV - minV) * 0.4) + minV);
				byte nearBytes = 5;
				
				for (int x = 0; x < BmpWidth; ++x) {
					for (int y = 8; y < BmpHeight - 8; ++y) {
						if (GetPixel(x,y) > th) continue;
						int count = 0;
						for (int k = 1; k <= nearBytes; ++k) {
							if (GetPixel(x, y-k) <= th) count++;
						}
						if (count >= nearBytes-1) {
							SetPixel(x,y, minV);
						}
					}
				}

			}
		}

		public void RemoveGrayArtefacts() {
			byte minV = 255;
			byte maxV = 0;
			
			unsafe
			{
				byte * p = (byte *)(void *)Scan0;
				for(int y=0; y < bmp.Height; ++y)
				{
					for (int x = 0; x < rowWidth; x += bytesPixel)
					{
						minV = Math.Min(minV, p[0]);
						maxV = Math.Max(maxV, p[0]);
						p += bytesPixel;
					}
					p += rowOffset;
				}
				
				//rendo più chiari
				byte thGray = (byte) (((maxV - minV) * 0.8) + minV);
				byte nearBytes = 3;
				
				for (int x = nearBytes; x < BmpWidth-nearBytes; ++x) {
					for (int y = nearBytes; y < BmpHeight - nearBytes; ++y) {
						if (GetPixel(x,y) < thGray) continue;
						int count = 0;
						if (GetPixel(x-1,y-1) > thGray) count++;
						if (GetPixel(x,y-1) > thGray) count++;
						if (GetPixel(x+1,y-1) > thGray) count++;

						if (GetPixel(x-1,y) > thGray) count++;
						if (GetPixel(x+1,y) > thGray) count++;
						
						if (GetPixel(x-1,y+1) > thGray) count++;
						if (GetPixel(x,y+1) > thGray) count++;
						if (GetPixel(x+1,y+1) > thGray) count++;

						if (count >= 5) {
							SetPixel(x,y, maxV);
						}
					}
				}


			}
		}
		

		
		public bool Rotate(string srcFile, int degree)
		{
			//validation
			if (!File.Exists(srcFile)) return false;
			//reference to image file
			System.Drawing.Image FullsizeImage = System.Drawing.Image.FromFile(srcFile);
			//rotate image
			switch (degree) {
				case 90:
					FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
					break;
				case 180:
					FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
					break;
				case 270:
					FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate270FlipNone);
					break;
			}
			//copy image to memory (allow overwrite original file)
			System.Drawing.Image newImage = FullsizeImage.GetThumbnailImage(FullsizeImage.Width, FullsizeImage.Height, null, IntPtr.Zero);
			//dispose original file
			FullsizeImage.Dispose();
			//save
			Save(newImage, srcFile);
			return true;
		}
		
		public bool Mirror(string srcFile, bool flipX, bool flipY)
		{
			//validation
			if (!File.Exists(srcFile)) return false;
			//reference to image file
			System.Drawing.Image FullsizeImage = System.Drawing.Image.FromFile(srcFile);
			//mirror
			if (flipX && !flipY) FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX);
			if (!flipX && flipY) FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipY);
			if (flipX && flipY) FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipXY);
			//copy image to memory (allow overwrite original file)
			System.Drawing.Image NewImage = FullsizeImage.GetThumbnailImage(FullsizeImage.Width, FullsizeImage.Height, null, IntPtr.Zero);
			//dispose original file
			FullsizeImage.Dispose();
			//save
			NewImage.Save(srcFile);
			return true;
		}


	}
}
