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

namespace TallGuy
{
    public class GameManager : MonoBehaviour
    {
        [SerializeField] private Texture2D[] shapes;
        [SerializeField] private Color[] colors;

        public Material refMaterial;
        private Material[] materials;

        public Transform tilesParent;
        public GameObject tilePrefab;
        private TileHandler[,] tiles;

        public Transform playersParent, playerSpawnRegion;
        private PlayerHandler[] players;
        public GameObject playerPrefab;
        public JoystickVRPN inputEvents;

        const float playerSize = 1;
        
        private void Awake()
        {
            CreateTiles();
            CreateMaterials();

            playerSpawnRegion.localScale = new Vector3(maxSeatsPerRow * playerSize, 1, numRows * playerSize) * 1.05f / 2;
            playerSpawnRegion.gameObject.SetActive(false);
        }

        private void CreateMaterials()
        {
            materials = new Material[shapes.Length];
            for (int i = 0; i < shapes.Length; i++)
            {
                materials[i] = new Material(refMaterial);
                materials[i].mainTexture = shapes[i];
            }
        }

        private void HandleButtonChange(int seatID, int buttonID, bool state)
        {
            if (players[seatID] == null)
            {
                Vector2 normalizedSeatCoords = GetNormalizedSeatCoords(seatID);
                normalizedSeatCoords = new Vector2(normalizedSeatCoords.y, normalizedSeatCoords.x); // Rows and Columns were in eachother's place
                normalizedSeatCoords = new Vector2(1 - normalizedSeatCoords.x, 1 - normalizedSeatCoords.y); // Then rows and columns needed mirroring
                normalizedSeatCoords -= Vector2.one * 0.5f;  // Rescale to -0.5, 0.5

                players[seatID] = SpawnPlayer(seatID, normalizedSeatCoords);
            }

            players[seatID].HandleInput(buttonID, state);
        }

        const int maxSeatsPerRow = 22;
        const int numRows = 8;
        private Vector2 GetNormalizedSeatCoords(int seatID)
        {
            if (seatID < 13)
                return new Vector2(0, 1f * seatID / 13);
            if (seatID < 29)
                return new Vector2(1 / 7f, 1f * (seatID - 13) / (29 - 13));
            if (seatID < 49)
                return new Vector2(2 / 7f, 1f * (seatID - 29) / (49 - 29));
            if (seatID < 70)
                return new Vector2(3 / 7f, 1f * (seatID - 49) / (70 - 49));
            if (seatID < 92)
                return new Vector2(4 / 7f, 1f * (seatID - 70) / (92 - 70));
            if (seatID < 112)
                return new Vector2(5 / 7f, 1f * (seatID - 92) / (112 - 92));
            if (seatID < 125)
                return new Vector2(6 / 7f, 1f * (seatID - 112) / (125 - 112));
            if (seatID < 132)
                return new Vector2(1, 1f * (seatID - 125) / (132 - 125));

            return Vector2.zero;
        }

        private PlayerHandler SpawnPlayer(int playerID, Vector2 normalizedCoords)
        {
            GameObject gO = Instantiate(playerPrefab, playersParent);

            Vector3 wantedPos = playerSpawnRegion.TransformPoint(new Vector3(normalizedCoords.x, 0, normalizedCoords.y));

            PlayerHandler pH = gO.GetComponent<PlayerHandler>();

            int materialCD = playerID % materials.Length;
            int colorCD = (playerID / materials.Length) % colors.Length;

            pH.Initialize(playerID, materials[materialCD], colors[colorCD], wantedPos, playerSize);

            return pH;
        }

        private void Start()
        {
            InitializeTiles();
            players = new PlayerHandler[inputEvents.DomeSeats.deviceRange];
            inputEvents.OnButtonChange.AddListener(HandleButtonChange);
        }

        private void InitializeTiles()
        {
           for (int i = 0; i < tiles.GetLength(0); i++)
               for (int j = 0; j < tiles.GetLength(1); j++)
               {
                    TileHandler tH = tiles[i, j];

                    bool isSouthFire = i > 8 * numTilesHorizontal / 20f && i < 12 * numTilesHorizontal / 20f && j < numTilesVertical / 10f;
                    bool isSouthWestFire = i < 2 * numTilesHorizontal / 20f && j < numTilesVertical / 10f;
                    bool isSouthEastFire = i > 18 * numTilesHorizontal / 20f && j < numTilesVertical / 10f;

                    bool isFire = isSouthFire || isSouthWestFire || isSouthEastFire;

                    bool isSouthWater = i > 8 * numTilesHorizontal / 20f && i < 12 * numTilesHorizontal / 20f && j > 8 * numTilesVertical / 10f;
                    bool isSouthWestWater = i > 3 * numTilesHorizontal / 20f && i < 5 * numTilesHorizontal / 20f && j > 3 * numTilesVertical / 10f && j < 4 * numTilesVertical / 10;
                    bool isSouthEastWater = i > 13 * numTilesHorizontal / 20f && i < 15 * numTilesHorizontal / 20f && j > 4 * numTilesVertical / 10f && j < 5 * numTilesVertical / 10;

                    bool isWater = isSouthWater || isSouthWestWater || isSouthEastWater;

                    TileHandler.State wantedState = isFire ? TileHandler.State.Fiah : isWater ? TileHandler.State.Watah : TileHandler.State.Land;
                    tH.SetState(wantedState);
               }
        }

        private float gridWidth = 30, gridHeight = 20;
        private float tileSize = 0.1f;

        int numTilesHorizontal, numTilesVertical;

        private void CreateTiles()
        {
            tilesParent.transform.position = Vector3.forward * gridHeight / 2;

            numTilesHorizontal = Mathf.CeilToInt(gridWidth / tileSize);
            numTilesVertical = Mathf.CeilToInt(gridHeight / tileSize);

            tiles = new TileHandler[numTilesHorizontal, numTilesVertical];

            for (int i = 0; i < numTilesHorizontal; i++)
                for (int j = 0; j < numTilesVertical; j++)
                {
                    GameObject gO = Instantiate(tilePrefab, tilesParent);
                    gO.name = string.Format("Tile ({0}, {1})", i, j);
                    TileHandler tH = gO.GetComponent<TileHandler>();
                    float x = Mathf.Lerp(-gridWidth / 2, gridWidth / 2, (float)i / numTilesHorizontal);
                    float z = Mathf.Lerp(-gridHeight / 2, gridHeight / 2, (float)j / numTilesVertical);
                    tH.transform.localPosition = new Vector3(x, 0, z);
                    tH.transform.localScale = Vector3.one * tileSize;
                    tiles[i, j] = tH;
                }
        }
    }
}