using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _3_KnightsTour {
	public class Calculations {
		public static List<Knight> CalculatePaths(Knight instance) {
			var returned = new List<Knight>();

			if (instance.IsFullyExplored())
				returned.Add(instance);
			else foreach (var dir in Knight.Movements) {
					if (instance.CanMove(dir)) {
						returned.AddRange(CalculatePaths(new Knight(instance, dir)));
					}
				}

			return returned;
		}

		public static Knight CalculatePathSingle(Knight instance) {
			if (instance.IsFullyExplored())
				return instance;

			foreach (var dir in Knight.Movements) {
				if (instance.CanMove(dir)) {
					var res = CalculatePathSingle(new Knight(instance, dir));
					if (res is not null)
						return res;
				}
			}

			return null;
		}
	}

	

	public class Knight {
		public Knight? PreviousPosition;

		public int BoardSize;

		public (int x, int y) Position;

		public List<(int x, int y)> UnvisitedPositions;

		public static List<(int x, int y)> Movements = new List<(int x, int y)> {
			new(2,1),
			new(1, 2),
			new(-1, 2),
			new(-2, 1),
			new(-2, -1),
			new(-1, -2),
			new(1, -2),
			new(2, -1),
		};

		public Knight(int x, int y, int size = 8) {
			BoardSize = size;

			UnvisitedPositions = new();

			for (var w = 0; w < size; w++) {
				for (var h = 0; h < size; h++) {
					UnvisitedPositions.Add((w, h));
				}
			}

			Position = (x, y);
			UnvisitedPositions.Remove(Position);
		}

		public Knight(Knight previousIteration, (int x, int y) direction) {
			PreviousPosition = previousIteration;

			BoardSize = previousIteration.BoardSize;

			UnvisitedPositions = new(previousIteration.UnvisitedPositions);


			Position = (previousIteration.Position.x + direction.x, previousIteration.Position.y + direction.y);
			UnvisitedPositions.Remove(Position);
		}

		public bool CanMove((int x, int y) direction) {
			return CanMove(direction.x, direction.y);
		}

		public bool CanMove(int x, int y) {
			return CanMoveTo(x + Position.x, y + Position.y);
		}

		public bool CanMoveTo(int x, int y) {
			if (x < 0 || x >= BoardSize || y < 0 || y >= BoardSize)
				return false;

			return UnvisitedPositions.Contains((x, y));
		}

		public bool IsFullyExplored() {
			return UnvisitedPositions.Count == 0;
		}

		public List<string> GetFullPath() {
			if (PreviousPosition is null)
				return new List<string>() { PositionToString() };

			else return PreviousPosition.GetFullPath().Append(PositionToString()).ToList();
		}

		private string PositionToString() {
			var xBytes = BitConverter.GetBytes(Position.x + 65);
			var stringified = Encoding.Unicode.GetString(xBytes);

			return $"{stringified}{Position.y}";
		}
	}
}
