using System.Collections.Generic;
using System.Drawing;

namespace DEM2Contour
{
	class MarchingSquares
	{
		public Dictionary<double, List<Line>> contourData { private set; get; }

		private double[,] initialData;

		public MarchingSquares(double[,] arrayData,double interval,double min,double max)
		{
			contourData = new Dictionary<double, List<Line>>();
			this.initialData = arrayData;

			for (double value = 0; value < max; value += interval)
				if (value > min)
					contourData.Add(value, new List<Line>());

			DoMSAlgorithm();
        }

		private void DoMSAlgorithm()
		{
			int length = 1;

			for (int n = 0; n < initialData.GetLength(1) - length; n++)
			{
				for (int m = 0; m < initialData.GetLength(0) - length; m++)
				{
					foreach(var contour in contourData)
					{
						switch(GetBinaryIndex(contour.Key, m, n))
						{
							#region 第一,二行
							case "0001":
							case "1110":
								contour.Value.Add(
									new Line(CalExactPosition(contour.Key, m, n, m, n + length),
									CalExactPosition(contour.Key, m + length, n + length, m, n + length)));
								break;
							case "0010":
							case "1101":
								contour.Value.Add(
									new Line(CalExactPosition(contour.Key, m + length, n, m + length, n + length),
									CalExactPosition(contour.Key, m + length, n + length, m, n + length)));
								break;
							case "0100":
							case "1011":
								contour.Value.Add(
									new Line(CalExactPosition(contour.Key, m, n, m + length, n),
									CalExactPosition(contour.Key, m + length, n + length, m + length, n)));
								break;
							case "1000":
							case "0111":
								contour.Value.Add(
									new Line(CalExactPosition(contour.Key, m, n, m + length, n),
									CalExactPosition(contour.Key, m, n, m, n + length)));
								break;

							#endregion

							#region 第三行

							case "0011":
							case "1100":
								contour.Value.Add(
									new Line(CalExactPosition(contour.Key, m, n, m, n + length),
									CalExactPosition(contour.Key, m + length, n, m + length, n + length)));
								break;
							case "0110":
							case "1001":
								contour.Value.Add(
									new Line(CalExactPosition(contour.Key, m, n, m + length, n),
									CalExactPosition(contour.Key, m + length, n + length, m, n + length)));
								break;

							#endregion

							#region 特殊情况，两条线

							//case "1010":
							//	if (initialData[m + 1, n + 1] >= contour.Key)
							//	{
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m, n, m + length, n),
							//			CalExactPosition(contour.Key, m + length, n + length, m + length, n)));
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m + length, n + length, m, n + length),
							//			CalExactPosition(contour.Key, m, n + length, m, n)));
							//	}
							//	else
							//	{
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m, n, m, n + length),
							//			CalExactPosition(contour.Key, m, n, m + length, n)));
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m + length, n, m + length, n + length),
							//			CalExactPosition(contour.Key, m + length, n + length, m, n + length)));
							//	}
							//	break;
							//case "0101":
							//	if (initialData[m + 1, n + 1] < contour.Key)
							//	{
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m, n, m + length, n),
							//			CalExactPosition(contour.Key, m + length, n + length, m + length, n)));
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m + length, n + length, m, n + length),
							//			CalExactPosition(contour.Key, m, n + length, m, n)));
							//	}
							//	else
							//	{
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m, n, m, n + 1),
							//			CalExactPosition(contour.Key, m, n, m + 1, n)));
							//		contour.Value.Add(
							//			new Line(CalExactPosition(contour.Key, m + 1, n, m + 1, n + 1),
							//			CalExactPosition(contour.Key, m + 1, n + 1, m, n + 1)));
							//	}
							//	break;
								
							#endregion
						}
					}
				}
			}

        }

		private string GetBinaryIndex(double value, int X, int Y)
		{
			string result = null;

			result += initialData[X, Y] >= value ? "1" : "0";
			result += initialData[X + 1, Y] >= value ? "1" : "0";
			result += initialData[X + 1, Y + 1] >= value ? "1" : "0";
			result += initialData[X, Y + 1] >= value ? "1" : "0";

			return result;
		}

		private PointF CalExactPosition(double value,int startX, int startY, int endX, int endY)
		{
			double radio = ((value - initialData[startX, startY]) / (initialData[endX, endY] - initialData[startX, startY]));
			return new PointF((float)(startX + radio * (endX - startX)), (float)(startY + radio * (endY - startY)));
        }
	}

	class Line
	{
		public PointF startPoint;
		public PointF endPoint;

		public Line(PointF startPoint, PointF endPoint)
		{
			this.startPoint = startPoint;
			this.endPoint = endPoint;
		}
	}
}
