using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace PckStudio.Models
{
    public partial class MinecraftModelView : Control
    {


		private const int IsometricFOV = 0x2E;

		private string skinFilename;

		internal float cameraZ = 31.4285717f;

		private int fov = 0x46;

		private float scale = 1f;

		internal float RotationX;

		internal float RotationY;

		private bool perspective = true;

		private bool showUsername;

		private string username = string.Empty;

		private string skinUrlBase = "http://s3.amazonaws.com/MinecraftSkins/";

		private string skinFileExt = ".png";

		private float PiBy180 = 0.0174532924f;

		private string info = "Minecraft Skin Viewer " + System.Windows.Forms.Application.ProductVersion + " Beta Prerelease";


		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("Appearance")]
		public bool ShowUsername
		{
			get
			{
				return showUsername;
			}
			set
			{
				showUsername = value;
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(true)]
		public event EventHandler SkinDowloadedx
		{
			add
			{
				System.EventHandler eventHandler = SkinDowloaded;
				System.EventHandler eventHandler2;
				do
				{
					eventHandler2 = eventHandler;
					System.EventHandler value2 = (System.EventHandler)System.Delegate.Combine(eventHandler2, value);
					eventHandler = System.Threading.Interlocked.CompareExchange<System.EventHandler>(ref SkinDowloaded, value2, eventHandler2);
				}
				while (eventHandler != eventHandler2);
			}
			remove
			{
				System.EventHandler eventHandler = SkinDowloaded;
				System.EventHandler eventHandler2;
				do
				{
					eventHandler2 = eventHandler;
					System.EventHandler value2 = (System.EventHandler)System.Delegate.Remove(eventHandler2, value);
					eventHandler = System.Threading.Interlocked.CompareExchange<System.EventHandler>(ref SkinDowloaded, value2, eventHandler2);
				}
				while (eventHandler != eventHandler2);
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("Skin")]
		public string Username
		{
			get
			{
				return username;
			}
			set
			{
				if (value.Length == 0)
				{
					username = string.Empty;
					return;
				}
				downloader.RunWorkerCompleted -= OnDownloaderRunWorkerCompleted;
				if (downloader.IsBusy)
				{
					downloader.CancelAsync();
				}
				downloader = new System.ComponentModel.BackgroundWorker();
				InitializeDownloader();
				username = value.Trim();
				downloader.RunWorkerAsync();
			}
		}

		protected override void OnBackColorChanged(System.EventArgs e)
		{
			base.OnBackColorChanged(e);
			backgroundColor = BackColor;
			UpdateBackgroundBrush();
			base.Invalidate();
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("Appearance")]
		public System.Drawing.Color BackGradientColor1
		{
			get
			{
				return backgroundGradientColor1;
			}
			set
			{
				backgroundGradientColor1 = value;
				UpdateBackgroundBrush();
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("Appearance")]
		public System.Drawing.Color BackGradientColor2
		{
			get
			{
				return backgroundGradientColor2;
			}
			set
			{
				backgroundGradientColor2 = value;
				UpdateBackgroundBrush();
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(false)]
		[System.ComponentModel.Category("Appearance")]
		[System.Obsolete]
		public new System.Windows.Forms.ImageLayout BackgroundImageLayout
		{
			get
			{
				return System.Windows.Forms.ImageLayout.Tile;
			}
			set
			{
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("Appearance")]
		public new System.Drawing.Image BackgroundImage
		{
			get
			{
				return backgroundTexture;
			}
			set
			{
				backgroundTexture = value;
				UpdateBackgroundBrush();
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("Appearance")]
		public BackgroundTypes BackgroundType
		{
			get
			{
				return backgroundType;
			}
			set
			{
				backgroundType = value;
				UpdateBackgroundBrush();
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("View")]
		public ProjectionTypes Projection
		{
			get
			{
				if (!perspective)
				{
					return ProjectionTypes.Isometric;
				}
				return ProjectionTypes.Perspective;
			}
			set
			{
				perspective = (value == ProjectionTypes.Perspective);
				SetupProjection();
				foreach (Object3D object3D in object3DList)
				{
					object3D.Update();
				}
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("View")]
		public int FOV
		{
			get
			{
				return fov;
			}
			set
			{
				fov = value;
				SetupProjection();
				foreach (Object3D object3D in object3DList)
				{
					object3D.Update();
				}
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("View")]
		public int DegreesX
		{
			get
			{
				return (int)RotationX;
			}
			set
			{
				RotationX = (float)value;
				Matrix3D globalTransformation = Matrix3D.CreateRotationX(RotationX * PiBy180) * Matrix3D.CreateRotationY(RotationY * PiBy180);
				foreach (Object3D object3D in object3DList)
				{
					object3D.GlobalTransformation = globalTransformation;
				}
				base.Invalidate();
			}
		}

		[System.ComponentModel.Browsable(true)]
		[System.ComponentModel.Category("View")]
		public int DegreesY
		{
			get
			{
				return (int)RotationY;
			}
			set
			{
				RotationY = (float)value;
				Matrix3D globalTransformation = Matrix3D.CreateRotationX(RotationX * PiBy180) * Matrix3D.CreateRotationY(RotationY * PiBy180);
				foreach (Object3D object3D in object3DList)
				{
					object3D.GlobalTransformation = globalTransformation;
				}
				base.Invalidate();
			}
		}

		public Models.ModelBase Model
		{
			set
			{
				Clear();
				value.AddToModelView(this);
				Matrix3D globalTransformation = Matrix3D.CreateRotationX(RotationX * 3.14159274f / 180f) * Matrix3D.CreateRotationY(RotationY * 3.14159274f / 180f);
				foreach (Object3D object3D in object3DList)
				{
					object3D.GlobalTransformation = globalTransformation;
				}
				base.Invalidate();
			}
		}


		private void InitializeDownloader()
		{
			downloader.WorkerSupportsCancellation = true;
			downloader.DoWork += OnDownloaderDoWork;
			downloader.RunWorkerCompleted += OnDownloaderRunWorkerCompleted;
		}

		protected virtual void OnSkinDownloaded(System.EventArgs args)
		{
			if (SkinDowloaded != null)
			{
				SkinDowloaded(this, args);
			}
		}

		private void OnDownloaderRunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
		{
			RenderUsernameImage();
			OnSkinDownloaded(System.EventArgs.Empty);
		}

		private void OnDownloaderDoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
		{
			System.Net.WebClient webClient = new System.Net.WebClient();
			try
			{
				byte[] buffer = webClient.DownloadData(skinUrlBase + username + skinFileExt);
				if (webSkin != null)
				{
					webSkin.Dispose();
				}
				webSkin = System.Drawing.Image.FromStream(new System.IO.MemoryStream(buffer));
			}
			catch
			{
				webSkin = null;
				username = string.Empty;
				usernameImage = null;
			}
		}

		private void Clear()
		{
			texelList.Clear();
			object3DList.Clear();
			dynamicObject3DtList.Clear();
			rotatingObject3D = null;
		}

		protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent)
		{
			base.OnPaintBackground(pevent);
			System.Drawing.Graphics graphics = pevent.Graphics;
			graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
			graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
			graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
			graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
			graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
			graphics.FillRectangle(backgroundBrush, base.ClientRectangle);
			graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
			if (showUsername && usernameImage != null)
			{
				graphics.DrawImage(usernameImage, base.Width / 2 - usernameImage.Width / 2, base.Height - 0x20);
			}
			if (versionImage == null)
			{
				versionImage = RenderVersionText();
			}
			graphics.DrawImage(versionImage, 3, 3);
		}

		protected override void OnPaint(System.Windows.Forms.PaintEventArgs pe)
		{
			texelList.Sort(texelComparer);
			System.Drawing.Graphics graphics = pe.Graphics;
			graphics.TranslateTransform((float)(base.Width / 2), (float)(base.Height / 2));
			graphics.ScaleTransform(scale, -scale);
			graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
			graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
			graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
			graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
			graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
			for (int i = 0; i < texelList.Count; i++)
			{
				texelList[i].Draw(graphics);
			}
		}

		public System.Drawing.Image RenderToImage(System.Drawing.Size size, System.Drawing.RectangleF crop)
		{
			if ((float)(size.Width / size.Height) != crop.Width / crop.Height)
			{
				throw new System.ArgumentException("Aspect ratio is ambiguous");
			}
			System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(size.Width, size.Height);
			using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
			{
				System.Drawing.Size size2 = new System.Drawing.Size((int)((float)size.Width / crop.Width), (int)((float)size.Height / crop.Height));
				texelList.Sort(texelComparer);
				graphics.TranslateTransform(-crop.Left * (float)size2.Width, -crop.Top * (float)size2.Height);
				graphics.TranslateTransform((float)(size2.Width / 2), (float)(size2.Height / 2));
				float num = (float)System.Math.Min(size2.Width, size2.Height) * 0.01f / (float)System.Math.Tan((double)(perspective ? fov : 0x2E) * 3.1415926535897931 / 360.0);
				graphics.ScaleTransform(num, -num);
				graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
				graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
				graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
				graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
				graphics.Clear(System.Drawing.Color.Transparent);
				for (int i = 0; i < texelList.Count; i++)
				{
					texelList[i].Draw(graphics);
				}
			}
			return bitmap;
		}

		private System.Drawing.Image RenderText(string text)
		{
			int num = 0;
			using (System.Drawing.Graphics graphics = base.CreateGraphics())
			{
				num = (int)graphics.MeasureString(text, Font).Width;
			}
			System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(num + 2, 0x12);
			using (System.Drawing.Graphics graphics2 = System.Drawing.Graphics.FromImage(bitmap))
			{
				using (System.Drawing.Brush brush = new System.Drawing.SolidBrush(MinecraftModelView.textShadowColor))
				{
					graphics2.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
					graphics2.DrawString(text, Font, brush, 2f, 2f);
					graphics2.DrawString(text, Font, System.Drawing.Brushes.White, 0f, 0f);
				}
			}
			return bitmap;
		}

		private System.Drawing.Image RenderVersionText()
		{
			System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(0x154, 0x12);
			System.Version version = new System.Version(System.Windows.Forms.Application.ProductVersion);
			string s = string.Concat(new object[]
			{
				System.Windows.Forms.Application.ProductName,
				" ",
				version.Major,
				".",
				version.Minor,
				(version.Build != 0) ? ("." + version.Build) : ""
			});
			using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
			{
				using (System.Drawing.Brush brush = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(0x7F, System.Drawing.Color.Gray)))
				{
					graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
					graphics.DrawString(s, Font, brush, 1f, 1f);
					graphics.DrawString(s, Font, System.Drawing.Brushes.White, 0f, 0f);
				}
			}
			return bitmap;
		}

		private void RenderUsernameImage()
		{
			if (username != null && username.Length > 0)
			{
				usernameImage = RenderText(username);
			}
		}

		private void UpdateBackgroundBrush()
		{
			backgroundBrush = ((backgroundType == BackgroundTypes.Texture) ? new System.Drawing.TextureBrush(backgroundTexture) : ((backgroundType == BackgroundTypes.Gradient) ? new System.Drawing.Drawing2D.LinearGradientBrush(new System.Drawing.Point(0, 0), new System.Drawing.Point(0, System.Math.Max(1, base.Height)), backgroundGradientColor1, backgroundGradientColor2) : new System.Drawing.SolidBrush(backgroundColor)));
		}

		public System.Drawing.Brush GetBackgroundBrush(System.Drawing.Size size)
		{
			if (backgroundType == BackgroundTypes.Texture)
			{
				return new System.Drawing.TextureBrush(backgroundTexture);
			}
			if (backgroundType != BackgroundTypes.Gradient)
			{
				return new System.Drawing.SolidBrush(backgroundColor);
			}
			return new System.Drawing.Drawing2D.LinearGradientBrush(new System.Drawing.Point(0, 0), new System.Drawing.Point(0, System.Math.Max(1, size.Height)), backgroundGradientColor1, backgroundGradientColor2);
		}

		private void SetupProjection()
		{
			cameraZ = 2400f / (float)fov;
			scale = (float)System.Math.Min(base.Width, base.Height) * 0.01f / (float)System.Math.Tan((double)(perspective ? fov : 0x2E) * 3.1415926535897931 / 360.0);
		}

		protected override void OnResize(System.EventArgs e)
		{
			SetupProjection();
			UpdateBackgroundBrush();
			base.OnResize(e);
		}

		internal void RemoveTexelsOf(TexturePlane texturePlane)
		{
			for (int i = 0; i < texelList.Count; i++)
			{
				if (texelList[i].TexturePlane == texturePlane)
				{
					texelList.RemoveAt(i);
					i--;
				}
			}
		}

		internal void AddTexel(Texel texel)
		{
			texelList.Add(texel);
		}

		protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
		{
			base.OnMouseDown(e);
			System.Drawing.PointF location = new System.Drawing.PointF(((float)e.X - (float)base.Width * 0.5f) / scale, -((float)e.Y - (float)base.Height * 0.5f) / scale);
			rotatingObject3D = null;
			Object3D item = null;
			float num = -1000f;
			foreach (Object3D object3D in object3DList)
			{
				float num2 = object3D.HitTest(location);
				if (num2 > num)
				{
					num = num2;
					item = object3D;
				}
			}
			if (num > -1000f && dynamicObject3DtList.Contains(item))
			{
				rotatingObject3D = item;
			}
			mouseLastLocation = e.Location;
			base.Invalidate();
		}

		protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
		{
			base.OnMouseUp(e);
			rotatingObject3D = null;
		}

		protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
		{
			base.OnMouseMove(e);
			if (e.Button != System.Windows.Forms.MouseButtons.Left)
			{
				return;
			}
			if (rotatingObject3D != null)
			{
				rotatingObject3D.RotateByMouse((float)(e.X - mouseLastLocation.X) * 400f / (float)base.Height, (float)(e.Y - mouseLastLocation.Y) * 400f / (float)base.Height);
				mouseLastLocation = e.Location;
				base.Invalidate();
				return;
			}
			RotationY += (float)(e.X - mouseLastLocation.X) * 400f / (float)base.Height;
			RotationX += (float)(e.Y - mouseLastLocation.Y) * 400f / (float)base.Height;
			mouseLastLocation = e.Location;
			Matrix3D globalTransformation = Matrix3D.CreateRotationX(RotationX * 3.14159274f / 180f) * Matrix3D.CreateRotationY(RotationY * 3.14159274f / 180f);
			foreach (Object3D object3D in object3DList)
			{
				object3D.GlobalTransformation = globalTransformation;
			}
			base.Invalidate();
		}

		public void AddStatic(Object3D object3D)
		{
			object3D.Viewport = this;
			object3DList.Add(object3D);
			foreach (Object3D object3D2 in object3DList)
			{
				object3D2.Update();
			}
		}

		public void AddDynamic(Object3D object3D)
		{
			object3D.Viewport = this;
			object3DList.Add(object3D);
			dynamicObject3DtList.Add(object3D);
			foreach (Object3D object3D2 in object3DList)
			{
				object3D2.Update();
			}
		}

		internal System.Drawing.PointF Point3DTo2D(Point3D point3D)
		{
			if (perspective)
			{
				return new System.Drawing.PointF(point3D.X * (-50f / (point3D.Z - cameraZ)), point3D.Y * (-50f / (point3D.Z - cameraZ)));
			}
			return new System.Drawing.PointF(point3D.X, point3D.Y);
		}

		internal float GetZOrder(Point3D point3D)
		{
			if (perspective)
			{
				return point3D.X * point3D.X + point3D.Y * point3D.Y + (cameraZ - point3D.Z) * (cameraZ - point3D.Z);
			}
			return -point3D.Z;
		}

		private unsafe System.Drawing.Image CorrectSkin(System.Drawing.Image skin)
		{
			if (skin == null)
			{
				return null;
			}
			System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(skin);
			System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height);
			System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
			for (int i = 0; i < 0x10; i++)
			{
				byte* ptr = (byte*)((void*)bitmapData.Scan0) + (int)i * (int)bitmapData.Stride;
				for (int j = 3; j < 0x80; j += 4)
				{
					ptr[j] = byte.MaxValue;
				}
			}
			for (int k = 0x10; k < 0x20; k++)
			{
				byte* ptr2 = (byte*)((void*)bitmapData.Scan0) + (int)k * (int)bitmapData.Stride;
				for (int l = 3; l < 0x100; l += 4)
				{
					ptr2[l] = byte.MaxValue;
				}
			}
			bool flag = false;
			for (int m = 0; m < 8; m++)
			{
				byte* ptr3 = (byte*)((void*)bitmapData.Scan0) + (int)m * (int)bitmapData.Stride;
				for (int n = 0xA3; n < 0xE0; n += 4)
				{
					if (ptr3[n] != 0xFF)
					{
						flag = true;
					}
				}
			}
			for (int num = 8; num < 0x10; num++)
			{
				byte* ptr4 = (byte*)((void*)bitmapData.Scan0) + (int)num * (int)bitmapData.Stride;
				for (int num2 = 0x83; num2 < 0x100; num2 += 4)
				{
					if (ptr4[num2] != 0xFF)
					{
						flag = true;
					}
				}
			}
			if (!flag)
			{
				for (int num3 = 0; num3 < 8; num3++)
				{
					byte* ptr5 = (byte*)((void*)bitmapData.Scan0) + (int)num3 * (int)bitmapData.Stride;
					for (int num4 = 0xA3; num4 < 0xE0; num4++)
					{
						ptr5[num4] = 0;
					}
				}
				for (int num5 = 8; num5 < 0x10; num5++)
				{
					byte* ptr6 = (byte*)((void*)bitmapData.Scan0) + (int)num5 * (int)bitmapData.Stride;
					for (int num6 = 0x83; num6 < 0x100; num6++)
					{
						ptr6[num6] = 0;
					}
				}
			}
			bitmap.UnlockBits(bitmapData);
			return bitmap;
		}

		internal void UpdateImage()
		{
			SetSkinFromFile(skinFilename);
		}

		internal bool SetSkinFromFile(string filename)
		{
			skinFilename = filename;
			bool flag = true;
			using (System.IO.FileStream fileStream = System.IO.File.OpenRead(filename))
			{
				System.Drawing.Image image = System.Drawing.Image.FromStream(fileStream);
				if (image.Width != 0x40 || image.Height != 0x20)
				{
					System.Windows.Forms.MessageBox.Show("Image '" + System.IO.Path.GetFileName(filename) + "' is not Minecraft skin.", "Minecraft Skin Viewer", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
					flag = false;
				}
			}
			if (flag)
			{
				usernameImage = null;
				username = string.Empty;
			}
			return flag;
		}

		public MinecraftModelView()
		{
			base.SetStyle(System.Windows.Forms.ControlStyles.ResizeRedraw | System.Windows.Forms.ControlStyles.AllPaintingInWmPaint | System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true);
			InitializeComponent();
			texelComparer = new TexelComparer();
		}

        public MinecraftModelView(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
        }
    }
}
