using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LevelGenerator : MonoBehaviour
{
	[Header("Difficulty")]
	[SerializeField]
	LevelDifficultyAsset difficulty;
	LevelDifficultySettingsAsset difficultySettings;

	[Header("Prefabs")]
	[SerializeField]
	private GameObject wallPrefab;

	[SerializeField]
	private GameObject floorPrefab;

	[SerializeField]
	private GameObject goalPrefab;

	[SerializeField]
	private Transform background;

	private const float ROOM_SIZE = 11f;

	private Vector2 cellSize;
	private float backgroundHeight;

	private void Awake()
	{
		Initialize();
		GenerateLevel();
	}

	private void Initialize()
	{
		InitializeDifficulty();
		InitializeMeasurements();
		InitializeBackground();
		InstantiateGoal();
	}

	private void InitializeDifficulty()
	{
		difficultySettings = difficulty.GetDifficultySettings();
	}

	private void InitializeMeasurements()
	{
		cellSize = GetCellSize(difficultySettings.GetMazeDimensions());
		transform.position = new Vector3(-cellSize.x / 2f, 0f, 0f);
		backgroundHeight = GetBackgroundHeight();
	}

	private void InitializeBackground()
	{
		background.localScale = new Vector3(cellSize.x, backgroundHeight, 1f);
	}

	private float GetBackgroundHeight()
	{
		return cellSize.y * difficultySettings.GetMazeCellCount() + ROOM_SIZE * 2f;
	}

	private void GenerateLevel()
	{
		Maze[] cells = GenerateCells();
		LinkCells(cells);
		float yOffset = 0f;
		for(int i = 0; i < cells.Length; i++)
		{
			InstantiateCellGeometry(cells[i], i, ref yOffset);
		}
	}

	private Maze[] GenerateCells()
	{
		int cellCount = difficultySettings.GetMazeCellCount();
		MazeDimensions mazeDimensions = difficultySettings.GetMazeDimensions();
		MazeDimensions endCellDimensions = new MazeDimensions(mazeDimensions.width, 1);
		MazeCoordinates startRoom = endCellDimensions.GetRandomRoom();
		MazeCoordinates endRoom = endCellDimensions.GetRandomBorderRoom(MazeWallEnum.north);

		Maze[] cells = new Maze[cellCount + 2];

		cells[0] = new Maze(endCellDimensions, startRoom, endRoom);

		for (int i = 1; i < cellCount + 1; i++)
		{
			startRoom = mazeDimensions.GetBorderRoom(MazeWallEnum.south, endRoom.x);
			endRoom = mazeDimensions.GetRandomBorderRoom(MazeWallEnum.north);
			cells[i] = new Maze(mazeDimensions, startRoom, endRoom);
		}

		startRoom = endCellDimensions.GetBorderRoom(MazeWallEnum.south, endRoom.x);
		endRoom = endCellDimensions.GetRandomBorderRoom(MazeWallEnum.north);

		cells[cellCount + 1] = new Maze(endCellDimensions, startRoom, endRoom);

		return cells;
	}

	private void LinkCells(Maze[] cells)
	{
		for(int i = 0; i < cells.Length; i++)
		{
			if (i > 0)
			{
				MazeRoom startRoom = cells[i].GetRoom(cells[i].GetStart());
				startRoom.DisableWall(MazeWallEnum.south);
			}

			if (i < cells.Length - 1)
			{
				MazeRoom endRoom = cells[i].GetRoom(cells[i].GetEnd());
				endRoom.DisableWall(MazeWallEnum.north);
			}
		}
	}

	private void InstantiateCellGeometry(Maze cell, int cellIndex, ref float yOffset)
	{
		float roomXPosition = 0f;
		Vector2 roomPosition = Vector2.zero;
		for(int y = 0; y < cell.GetMazeHeight(); y++)
		{
			for(int x = 0; x < cell.GetMazeWidth(); x++)
			{
				MazeRoom currentRoom = cell.GetRoom(x, y);

				roomPosition = new Vector2(roomXPosition, yOffset);

				if(currentRoom.IsActiveWall(MazeWallEnum.south))
				{
					InstantiateFloor(roomPosition);
				}

				if (currentRoom.IsActiveWall(MazeWallEnum.east))
				{
					InstantiateWall(roomPosition);
				}

				if(x == cell.GetMazeWidth() - 1 && currentRoom.IsActiveWall(MazeWallEnum.west))
				{
					InstantiateFarWall(roomPosition);
				}

				if(cellIndex == difficultySettings.GetMazeCellCount() + 1 && currentRoom.IsActiveWall(MazeWallEnum.north))
				{
					InstantiateCeiling(roomPosition);
				}

				roomXPosition += ROOM_SIZE;
			}
			roomXPosition = 0f;
			yOffset += ROOM_SIZE;
		}
	}


	private Vector2 GetRoomPosition(int x, int y, int cell)
	{
		Vector2 roomPosition = new Vector2(x * ROOM_SIZE, cellSize.y * cell + y * ROOM_SIZE);
		if(cell > 0)
		{
			roomPosition -= Vector2.up * ROOM_SIZE;
		}
		return roomPosition;
	}

	private void InstantiateFloor(Vector2 roomPosition)
	{
		InstantiatePrefab(floorPrefab, roomPosition, Vector2.zero);
	}

	private void InstantiateCeiling(Vector3 roomPosition)
	{
		InstantiatePrefab(floorPrefab, roomPosition, Vector2.up * ROOM_SIZE);
	}

	private void InstantiateWall(Vector2 roomPosition)
	{
		InstantiatePrefab(wallPrefab, roomPosition, Vector2.zero);
	}

	private void InstantiateFarWall(Vector3 roomPosition)
	{
		InstantiatePrefab(wallPrefab, roomPosition, Vector2.right * ROOM_SIZE);
	}

	private void InstantiatePrefab(GameObject prefab, Vector2 roomPosition, Vector2 offset)
	{
		GameObject newPrefab = Instantiate(prefab, transform);
		newPrefab.transform.localPosition = roomPosition + offset;
	}

	private Vector2 GetCellSize(MazeDimensions mazeDimensions)
	{
		return new Vector2(mazeDimensions.width * ROOM_SIZE, mazeDimensions.height * ROOM_SIZE); 
	}

	private void InstantiateGoal()
	{
		GameObject goal = Instantiate(goalPrefab, transform);
		goal.transform.localPosition = new Vector3(cellSize.x / 2f, backgroundHeight - ROOM_SIZE / 2f, 0f);
	}
}
