using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using Microsoft.VisualBasic;

namespace ImageQuantization;

public class OctreeQuantizer : Quantizer
{
	private class Octree
	{
		protected class OctreeNode
		{
			private bool _leaf;

			private int _pixelCount;

			private int _red;

			private int _green;

			private int _blue;

			private OctreeNode[] _children;

			private OctreeNode _nextReducible;

			private int _paletteIndex;

			public OctreeNode NextReducible
			{
				get
				{
					return _nextReducible;
				}
				set
				{
					_nextReducible = value;
				}
			}

			public OctreeNode[] Children => _children;

			public OctreeNode(int level, int colorBits, Octree octree)
			{
				_leaf = level == colorBits;
				_red = 0;
				_green = 0;
				_blue = 0;
				_pixelCount = 0;
				checked
				{
					if (_leaf)
					{
						octree.Leaves++;
						_nextReducible = null;
						_children = null;
					}
					else
					{
						_nextReducible = octree.ReducibleNodes[level];
						octree.ReducibleNodes[level] = this;
						_children = new OctreeNode[9];
					}
				}
			}

			public void AddColor(Color32 pixel, int colorBits, int level, ref Octree octree)
			{
				if (_leaf)
				{
					Increment(pixel);
					octree.TrackPrevious(this);
					return;
				}
				checked
				{
					int num = 7 - level;
					int num2 = ((pixel.Red & mask[level]) >> num - 2) | ((pixel.Green & mask[level]) >> num - 1) | ((pixel.Blue & mask[level]) >> num);
					OctreeNode octreeNode = _children[num2];
					if (Information.IsNothing(octreeNode))
					{
						octreeNode = new OctreeNode(level + 1, colorBits, octree);
						_children[num2] = octreeNode;
					}
					octreeNode.AddColor(pixel, colorBits, level + 1, ref octree);
				}
			}

			public int Reduce()
			{
				_red = 0;
				_green = 0;
				_blue = 0;
				int num = 0;
				int num2 = 0;
				checked
				{
					do
					{
						if (!Information.IsNothing(_children[num2]))
						{
							_red += _children[num2]._red;
							_green += _children[num2]._green;
							_blue += _children[num2]._blue;
							_pixelCount += _children[num2]._pixelCount;
							num++;
							_children[num2] = null;
						}
						num2++;
					}
					while (num2 <= 7);
					_leaf = true;
					return num - 1;
				}
			}

			public void ConstructPalette(ref ArrayList palette, ref int paletteIndex)
			{
				checked
				{
					if (_leaf)
					{
						_paletteIndex = paletteIndex;
						paletteIndex++;
						palette.Add(Color.FromArgb((int)Math.Round((double)_red / (double)_pixelCount), (int)Math.Round((double)_green / (double)_pixelCount), (int)Math.Round((double)_blue / (double)_pixelCount)));
						return;
					}
					int num = 0;
					do
					{
						if (!Information.IsNothing(_children[num]))
						{
							_children[num].ConstructPalette(ref palette, ref paletteIndex);
						}
						num++;
					}
					while (num <= 7);
				}
			}

			public int GetPaletteIndex(Color32 col, int level)
			{
				int paletteIndex = _paletteIndex;
				checked
				{
					if (!_leaf)
					{
						int num = 7 - level;
						int num2 = ((col.Red & mask[level]) >> num - 2) | ((col.Green & mask[level]) >> num - 1) | ((col.Blue & mask[level]) >> num);
						if (Information.IsNothing(_children[num2]))
						{
							throw new Exception("Didn't expect this!");
						}
						paletteIndex = _children[num2].GetPaletteIndex(col, level + 1);
					}
					return paletteIndex;
				}
			}

			public void Increment(Color32 pixel)
			{
				checked
				{
					_pixelCount++;
					_red += pixel.Red;
					_green += pixel.Green;
					_blue += pixel.Blue;
				}
			}
		}

		private static int[] mask = new int[8] { 128, 64, 32, 16, 8, 4, 2, 1 };

		private OctreeNode _root;

		private int _leafCount;

		private OctreeNode[] _reducibleNodes;

		private int _maxColorBits;

		private OctreeNode _previousNode;

		private int _previousColor;

		public int Leaves
		{
			get
			{
				return _leafCount;
			}
			set
			{
				_leafCount = value;
			}
		}

		protected OctreeNode[] ReducibleNodes => _reducibleNodes;

		public Octree(int maxColorBits)
		{
			_maxColorBits = maxColorBits;
			_leafCount = 0;
			_reducibleNodes = new OctreeNode[10];
			_root = new OctreeNode(0, _maxColorBits, this);
			_previousColor = 0;
			_previousNode = null;
		}

		public void AddColor(Color32 pixel)
		{
			if (_previousColor == pixel.ARGB)
			{
				if (Information.IsNothing(_previousNode))
				{
					_previousColor = pixel.ARGB;
					OctreeNode root = _root;
					Color32 pixel2 = pixel;
					int maxColorBits = _maxColorBits;
					Octree octree = this;
					root.AddColor(pixel2, maxColorBits, 0, ref octree);
				}
				else
				{
					_previousNode.Increment(pixel);
				}
			}
			else
			{
				_previousColor = pixel.ARGB;
				OctreeNode root2 = _root;
				Color32 pixel3 = pixel;
				int maxColorBits2 = _maxColorBits;
				Octree octree = this;
				root2.AddColor(pixel3, maxColorBits2, 0, ref octree);
			}
		}

		public void Reduce()
		{
			checked
			{
				int num = _maxColorBits - 1;
				while ((num > 0) & Information.IsNothing(_reducibleNodes[num]))
				{
					num--;
				}
				OctreeNode octreeNode = _reducibleNodes[num];
				_reducibleNodes[num] = octreeNode.NextReducible;
				_leafCount -= octreeNode.Reduce();
				_previousNode = null;
			}
		}

		protected void TrackPrevious(OctreeNode node)
		{
			_previousNode = node;
		}

		public ArrayList Palletize(int colorCount)
		{
			while (Leaves > colorCount)
			{
				Reduce();
			}
			ArrayList palette = new ArrayList(Leaves);
			int paletteIndex = 0;
			_root.ConstructPalette(ref palette, ref paletteIndex);
			return palette;
		}

		public int GetPaletteIndex(Color32 pixel)
		{
			return _root.GetPaletteIndex(pixel, 0);
		}
	}

	private Octree _octree;

	private int _maxColors;

	public OctreeQuantizer(int maxColors, int maxColorBits)
		: base(singlePass: false)
	{
		if (maxColors > 255)
		{
			throw new ArgumentOutOfRangeException("maxColors", maxColors, "The number of colors should be less than 256");
		}
		if (maxColorBits < 1 || maxColorBits > 8)
		{
			throw new ArgumentOutOfRangeException("maxColorBits", maxColorBits, "This should be between 1 and 8");
		}
		_octree = new Octree(maxColorBits);
		_maxColors = maxColors;
	}

	protected override void InitialQuantizePixel(Color32 pixel)
	{
		_octree.AddColor(pixel);
	}

	protected override byte QuantizePixel(Color32 pixel)
	{
		checked
		{
			byte result = (byte)_maxColors;
			if (pixel.Alpha > 0)
			{
				result = (byte)_octree.GetPaletteIndex(pixel);
			}
			return result;
		}
	}

	protected override ColorPalette GetPalette(ColorPalette original)
	{
		checked
		{
			ArrayList arrayList = _octree.Palletize(_maxColors - 1);
			int num = arrayList.Count - 1;
			Color color = default(Color);
			for (int i = 0; i <= num; i++)
			{
				ref Color reference = ref original.Entries[i];
				object obj = arrayList[i];
				reference = ((obj != null) ? ((Color)obj) : color);
			}
			ref Color reference2 = ref original.Entries[_maxColors];
			reference2 = Color.FromArgb(0, 0, 0, 0);
			return original;
		}
	}
}
