//----------------------------------------------//
// Gamelogic Grids                              //
// http://www.gamelogic.co.za                   //
// Copyright (c) 2014 Gamelogic (Pty) Ltd       //
//----------------------------------------------//

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

public class DFLightsOutHex : GLMonoBehaviour, IResetable
{
	#region Constants
	private const int Symmetry2 = 0;
	private const int Symmetry3 = 1;
	private const int Symmetry6 = 2;

	private readonly Vector2 hexDimensions = new Vector2(74, 84);
	private readonly Vector2 cellSize = new Vector2(80, 80);
	private readonly Color offColor = ExampleUtils.colors[4];
	private readonly Color onColor = ExampleUtils.colors[6];
	#endregion

	#region Public Fields
	public dfPanel cellPrefab;
	public dfPanel root;
	public dfLabel winText;
	#endregion

	#region Private Fields
	private PointyHexGrid<DFCell> grid;
	private IMap3D<PointyHexPoint> map;

	private bool gameOver;
	#endregion

	#region Unity Callbacks
	public void Start()
	{
		Reset();
	}

	public void Update()
	{
		if (gameOver)
		{
			return;
		}

		if (!grid.Any(p => grid[p].On))
		{
			winText.gameObject.SetActive(true);
			gameOver = true;
		}
	}
	#endregion

	#region Public interface
	public void Reset()
	{
		winText.gameObject.SetActive(false);
		root.transform.DestroyChildren();
		BuildGrid();
		InitGame();

		gameOver = false;
	}

	public void OnClick(Vector3 relativePosition)
	{
		if (gameOver)
		{
			return;
		}

		PointyHexPoint hexPoint = map[relativePosition];

		if (grid.Contains(hexPoint))
		{
			ToggleCellAt(hexPoint);
		}
	}
	#endregion

	#region Implementation
	private void BuildGrid()
	{
		grid = PointyHexGrid<DFCell>.Hexagon(5);
		var panelRect = new Rect(0, 0, root.Width, root.Height);
		
		map = new PointyHexMap(hexDimensions)
			.ReflectAboutX() 
			.AnchorCellBottomLeft()
			.WithWindow(panelRect)
			.AlignMiddleCenter(grid)
			.To3DXY();
		
		foreach(PointyHexPoint point in grid)
		{
			var control = Instantiate(cellPrefab);
			root.AddControl(control);

			var cell = control.GetComponent<DFCell>();
			var worldPoint = map[point];
			cell.Panel.RelativePosition = worldPoint;
			cell.SetText(point.ToString());
			cell.SetColors(onColor, offColor);

			grid[point] = cell;
		}
	}
	
	private void InitGame()
	{
		int pattern = Random.Range(0, 4);
		
		switch(pattern)
		{
			case 0:
				InitPattern1();
				break;
			case 1:
				InitPattern2();
				break;			
			case 2:
				InitPattern1();
				InitPattern2();
			break;
			case 3:
				InitPattern3();
			break;
		}		
		
		if(grid.All(p => !grid[p].On))
		{
			Reset();
		}
	}
	
	private void InitPattern2()
	{
		int start = Random.Range(0, 3);
		int end = Random.Range(start, 3);
		int symmetry = Random.Range(0, 3); 
		
		switch(symmetry)
		{
			case Symmetry6:
				for(int i = start; i <= end; i++)
				{			
					ToggleCellAt((PointyHexPoint.East + PointyHexPoint.NorthEast) * i);
					ToggleCellAt((PointyHexPoint.West + PointyHexPoint.SouthWest) * i);			
					ToggleCellAt((PointyHexPoint.NorthEast + PointyHexPoint.NorthWest) * i);
					ToggleCellAt((PointyHexPoint.SouthWest + PointyHexPoint.SouthEast) * i);
					ToggleCellAt((PointyHexPoint.NorthWest + PointyHexPoint.West) * i);
					ToggleCellAt((PointyHexPoint.SouthEast + PointyHexPoint.East) * i);
				}
			break;
			case Symmetry3:
				for(int i = start; i <= end; i++)
				{			
					ToggleCellAt((PointyHexPoint.East + PointyHexPoint.NorthEast) * i);
					ToggleCellAt((PointyHexPoint.SouthWest + PointyHexPoint.SouthEast) * i);
					ToggleCellAt((PointyHexPoint.NorthWest + PointyHexPoint.West) * i);
		
				}
			break;
			case Symmetry2:
				for(int i = start; i <= end; i++)
				{			
					ToggleCellAt((PointyHexPoint.East + PointyHexPoint.NorthEast) * i);
					ToggleCellAt((PointyHexPoint.West + PointyHexPoint.SouthWest) * i);			
				}
			break;
		}
	}
	
	private void InitPattern1()
	{	
		int start = Random.Range(0, 5);
		int end = Random.Range(start, 5);
		int symmetry = Random.Range(0, 3); 
		
		switch(symmetry)
		{
			case Symmetry6:
				for(int i = start; i <= end; i++)
				{			
					ToggleCellAt(PointyHexPoint.East * i);
					ToggleCellAt(PointyHexPoint.West * i);			
					ToggleCellAt(PointyHexPoint.NorthEast * i);
					ToggleCellAt(PointyHexPoint.SouthWest * i);
					ToggleCellAt(PointyHexPoint.NorthWest * i);
					ToggleCellAt(PointyHexPoint.SouthEast * i);
				}
			break;
			case Symmetry3:
				for(int i = start; i <= end; i++)
				{			
					ToggleCellAt(PointyHexPoint.East * i);
					ToggleCellAt(PointyHexPoint.SouthWest * i);
					ToggleCellAt(PointyHexPoint.NorthWest * i);
		
				}
			break;
			case Symmetry2:
				for(int i = start; i <= end; i++)
				{			
					ToggleCellAt(PointyHexPoint.East * i);
					ToggleCellAt(PointyHexPoint.West * i);			
				}
			break;
		}
	}
	
	private void InitPattern3()
	{
		var randomPoints = grid.SampleRandom(2);
		var pattern = new HashSet <PointyHexPoint>();
		int symmetry = Random.Range(0, 3); 
		
		foreach (var pointyHexPoints in randomPoints.Select(point1 => grid.Where(p => (p - point1).Magnitude() <= 3).SampleRandom(2)).Select(randomGroup => randomGroup as IList<PointyHexPoint> ?? randomGroup.ToList()))
		{
			pattern.AddRange(pointyHexPoints);
			
			switch(symmetry)
			{
				case Symmetry6:
				
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate60()));
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate120()));
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate180()));
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate240()));
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate300()));
				
					break;
					
				case Symmetry3:			
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate120()));
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate240()));
				
					break;
					
				case Symmetry2:			
					pattern.AddRange(pointyHexPoints.Select(p => p.Rotate180()));			
					break;
			}
		}
		
		foreach (var point in pattern)
		{
			ToggleCellAt(point);
		}
	}
	
	private void ToggleCellAt(PointyHexPoint hexPoint)
	{
		foreach(PointyHexPoint point in grid.GetNeighbors(hexPoint))
		{
			grid[point].On = !grid[point].On;
		}
	}
	#endregion
}