﻿using Gamelogic;
using System.Collections.Generic;
using System.Linq;
using Gamelogic.Grids;
using UnityEngine;

namespace Gamelogic.Grids.Examples
{
	public class SquareSlider : GridBehaviour<RectPoint>
	{
		public MatchCell cellPrefab;
		public GameObject gridRoot;
		public TextMesh message;

		private RectGrid<MatchCell> grid;
		private IMap3D<RectPoint> map;
		private const int width = 9;
		private const int height = 9;
		private const float scale = 1.5f;

		public void Start()
		{
			Reset();
		}

		public void Reset()
		{
			message.gameObject.SetActive(false);
			gridRoot.transform.DestroyChildren();

			BuildGrid();
			InitGame();
		}

		private void InitGame()
		{
			for (int i = 0; i < Random.Range(1, 6); i++)
			{
				var point1 = grid.RandomItem();
				var row1 = FindRow(point1).ToList();
				var point2 = new RectPoint(point1.X, height - point1.Y - 1);
				var row2 = FindRow(point2).ToList();

				int k = Random.Range(1, 6);

				for (int j = 0; j < k; j++)
				{
					SlideRowLeft(row1);
					SlideRowRight(row2);
				}

				var col1 = FindCol(point1).ToList();
				var point3 = new RectPoint(width - 1 - point1.X, point1.Y);
				var col2 = FindCol(point3).ToList();

				for (int j = 0; j < k; j++)
				{
					SlideRowUp(col1);
					SlideRowDown(col2);
				}
			}
		}

		public void RotatePoints(List<RectPoint> points)
		{
			int i = 0;

			MatchCell tmp = grid[points[i]];

			for (i = 0; i < points.Count - 1; i++)
			{
				grid[points[i]] = grid[points[i + 1]];
			}

			grid[points[i]] = tmp;

			foreach (var point in points)
			{
				grid[point].transform.localPosition = map[point];
			}
		}

		public void BuildGrid()
		{
			grid = RectGrid<MatchCell>
				.BeginShape()

				.Rectangle(3, 3)
				.Translate(3, 0)
				.Union()

				.Rectangle(3, 3)
				.Translate(3, 0)
				.Union()

				.Rectangle(3, 3)
				.Translate(0, 3)
				.Union()

				.Rectangle(3, 3)
				.Translate(-6, 0)
				.Union()

				.Rectangle(3, 3)
				.Translate(0, 3)
				.Union()

				.Rectangle(3, 3)
				.Translate(3, 0)
				.Union()

				.Rectangle(3, 3)
				.Translate(3, 0)
				.Union()

				.Rectangle(3, 3)
				.EndShape();

			map = new RectMap(new Vector2(40, 40)*scale)
				.WithWindow(ExampleUtils.ScreenRect)
				.AlignMiddleCenter(grid)
				.To3DXY();

			foreach (var point in grid)
			{
				var cell = Instantiate(cellPrefab);

				cell.transform.parent = gridRoot.transform;
				cell.transform.localScale = Vector3.one*0.15f*scale;
				cell.transform.localPosition = map[point];
				cell.HighlightOn = false;
				cell.Color = ExampleUtils.Colors[(point/3).GetColor4()%4];
				cell.name = point.ToString();
				grid[point] = cell;
			}
		}

		public void Update()
		{
			if (Input.GetKeyDown(KeyCode.R))
			{
				Reset();
				return;
			}

			var worldPoint = ExampleUtils.ScreenToWorld(gridRoot, Input.mousePosition);
			var point = map[worldPoint];

			if (grid.Contains(point))
			{
				var row = FindRow(point);
				var col = FindCol(point);

				foreach (var gridPoint in grid)
				{
					grid[gridPoint].HighlightOn = row.Contains(gridPoint) || col.Contains(gridPoint);
				}

				if (Input.GetKeyDown(KeyCode.LeftArrow))
				{
					SlideRowLeft(row);
				}
				else if (Input.GetKeyDown(KeyCode.RightArrow))
				{
					SlideRowRight(row);
				}
				else if (Input.GetKeyDown(KeyCode.UpArrow))
				{
					SlideRowUp(col);
				}
				else if (Input.GetKeyDown(KeyCode.DownArrow))
				{
					SlideRowDown(col);
				}
			}

			if (Input.GetKeyDown(KeyCode.R))
			{
				Reset();
			}

			if (CheckGrid())
			{
				message.gameObject.SetActive(true);
			}
		}

		private void SlideRowDown(IEnumerable<RectPoint> col)
		{
			RotatePoints(col.ToList());
		}

		private void SlideRowUp(IEnumerable<RectPoint> col)
		{
			RotatePoints(col.Reverse().ToList());
		}

		private void SlideRowRight(IEnumerable<RectPoint> row)
		{
			RotatePoints(row.Reverse().ToList());
		}

		private void SlideRowLeft(IEnumerable<RectPoint> row)
		{
			RotatePoints(row.ToList());
		}

		private IEnumerable<RectPoint> FindRow(RectPoint point)
		{
			return grid.Where(p => p.Y == point.Y);
		}

		private IEnumerable<RectPoint> FindCol(RectPoint point)
		{
			return grid.Where(p => p.X == point.X);
		}

		private bool CheckGrid()
		{
			return grid.All(point => grid[point].TileColor == (TileColor) ((point/3).GetColor4()%4));
		}
	}
}