<Query Kind="Program">
  <Namespace>System.Drawing</Namespace>
</Query>

bool[,] Map;
int X0;
int Y0;
int Width;
int Height;

enum Water { None=0, Flow=1, Rest=2 }
Water[,] water;

void Main()
{
	Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"17_input.txt")));
	//DumpMap();

	water[500, 0] = Water.Flow;
	int w = 0;
	Water[,] wold = new Water[Width, Height];
	for (int gen = 0; ; gen++)
	{
		Tick();
		//Util.ClearResults();
		//DumpMap();
		int w0 = WaterCount();
		if (w == w0 && WaterEquals(wold, water)) { $"{StillWaterCount()} after {gen} generations (all = {w0})".Dump(); DumpMap(); /* DumpMapASCII(); */ return; }
		w=w0;
		for (int y = 0; y < Height; y++) for (int x = 0; x < Width; x++) wold[x,y]=water[x,y];
	}
	
	
}

bool WaterEquals(Water[,] a, Water[,] b)
{
	for (int y = 0; y < Height; y++) for (int x = 0; x < Width; x++) if (a[x,y] != b[x,y])return false;
	return true;
}

int WaterCount()
{
	int c = 0;
	for (int y = Y0; y < Height; y++) for (int x = 0; x < Width; x++) if (water[x, y] != Water.None) c++;
	return c;
}

int StillWaterCount()
{
	int c = 0;
	for (int y = Y0; y < Height; y++) for (int x = 0; x < Width; x++) if (water[x, y] == Water.Rest) c++;
	return c;
}

void Tick()
{
	for (int xx = 0; xx < Width; xx++) for (int yy = 0; yy < Height; yy++) if (water[xx,yy]==Water.Flow) water[xx,yy]=Water.None;
	
	var next = new Stack<(int,int)>();
	next.Push( (500, 0) );
	while (next.Any())
	{
		(var x, var y) = next.Pop();
		
		if (y==Height-1) continue;
		
		if (Map[x,y+1] || water[x,y+1]==Water.Rest)
		{
			var l = false;
			var r = false;
			if (!Map[x - 1, y] && water[x - 1, y] == Water.None) { l = true; water[x - 1, y] = Water.Flow; next.Push((x - 1, y)); }
			if (!Map[x + 1, y] && water[x + 1, y] == Water.None) { r = true; water[x + 1, y] = Water.Flow; next.Push((x + 1, y)); }

			if (!l && !r)
			{
				var solid = true;
				for (int xx = x; solid; xx++)
				{
					if (water[xx, y] == Water.None) { solid = false; break; }
					if (!(Map[xx, y + 1] || water[xx, y + 1] == Water.Rest)) { solid = false; break; }
					if (Map[xx + 1, y]) break;
				}
				for (int xx = x; solid; xx--)
				{
					if (water[xx, y] == Water.None) { solid = false; break; }
					if (!(Map[xx, y + 1] || water[xx, y + 1] == Water.Rest)) { solid = false; break; }
					if (Map[xx - 1, y]) break;
				}

				if (solid)
				{
					for (int xx = x; !Map[xx, y]; xx++) { Assert(water[xx, y] != Water.None); water[xx, y] = Water.Rest; }
					for (int xx = x; !Map[xx, y]; xx--) { Assert(water[xx, y] != Water.None); water[xx, y] = Water.Rest; }
				}
			}
		}
		else
		{
			water[x, y+1] = Water.Flow;
			next.Push( (x,y+1) );
		}
	}
}

void Load(string[] v)
{
	var data = new List<(int,int,int,int)>();
	
	for (int i = 0; i < v.Length; i++)
	{
		var a = v[i].Split(',')[0].Trim();
		var b = v[i].Split(',')[1].Trim();

		if (b.StartsWith("x")) { var t = b; b = a; a = t; }

		a = a.Substring(2);
		b = b.Substring(2);

		var a1 = int.Parse(a.Contains("..") ? a.Split('.')[0] : a);
		var a2 = int.Parse(a.Contains("..") ? a.Split('.')[2] : a);
		var b1 = int.Parse(b.Contains("..") ? b.Split('.')[0] : b);
		var b2 = int.Parse(b.Contains("..") ? b.Split('.')[2] : b);
		
		data.Add( (a1,a2,b1,b2) );
	}

	Width  = data.Max(m => Math.Max(m.Item1, m.Item2)) + 1;
	Height = data.Max(m => Math.Max(m.Item3, m.Item4)) + 1;
	X0     = data.Min(m => Math.Min(m.Item1, m.Item2)) - 1;
	Y0     = data.Min(m => Math.Min(m.Item3, m.Item4));
	Map = new bool[Width, Height];

	foreach ((var x1, var x2, var y1, var y2) in data)
	{
		for (int x = x1; x <= x2; x++) for (int y = y1; y <= y2; y++) Map[x, y] = true;
	}

	water = new Water[Width, Height];
}

void DumpMap()
{
	Bitmap bmp = new Bitmap(Width - X0, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
	using (Graphics g = Graphics.FromImage(bmp))
	{
		g.Clear(Color.Wheat);
		for (int y = 0; y < Height; y++)
		{
			for (int x = X0; x < Width; x++)
			{
				if (x == (500 - X0) && y == 0) g.FillRectangle(Brushes.DarkBlue, x - X0, y, 1, 1);
				else if (Map[x, y]) g.FillRectangle(Brushes.Black, x - X0, y, 1, 1);
				else if (water[x, y] == Water.Flow) g.FillRectangle(Brushes.Cyan, x - X0, y, 1, 1);
				else if (water[x, y] == Water.Rest) g.FillRectangle(Brushes.LightBlue, x - X0, y, 1, 1);
			}
		}
	}
	bmp.Dump();
}

void DumpMapASCII()
{
	StringBuilder b = new StringBuilder();
	for (int y = 0; y < Height; y++)
	{
		for (int x = 0; x < Width; x++)
		{
			if (x == (500) && y == 0) b.Append('+');
			else if (Map[x, y]) b.Append('#');
			else if (water[x, y] == Water.Flow) b.Append('|');
			else if (water[x, y] == Water.Rest) b.Append('~');
			else b.Append('.');
		}
		b.AppendLine();
	}
	b.ToString().Dump();
}

void Assert(bool b) { if (!b) throw new Exception(); }