/// <summary>
 /// Set up a new PlacedTileObject.
 /// </summary>
 /// <param name="tileObjectSO"></param>
 /// <param name="origin"></param>
 /// <param name="dir"></param>
 public void Setup(TileObjectSo tileObjectSO, Vector2Int origin, Direction dir)
 {
     this.tileObjectSO  = tileObjectSO;
     this.origin        = origin;
     this.dir           = dir;
     adjacencyConnector = GetComponent <IAdjacencyConnector>();
 }
        /// <summary>
        /// Checks whether an object can be build on top of a certain plenum
        /// </summary>
        /// <param name="map"></param>
        /// <param name="position"></param>
        /// <param name="plenumAttachment"></param>
        /// <param name="dir"></param>
        /// <returns></returns>
        private static bool CanBuildOnPlenum(TileMap map, Vector3 position, TileObjectSo plenumAttachment, Direction dir)
        {
            TileObject plenumObject = map.GetTileObject(TileLayer.Plenum, position);

            // No plenum means we cannot build anything on top
            if (plenumObject.IsCompletelyEmpty())
            {
                return(false);
            }

            // Only allow wires and machines on catwalks
            if (plenumObject.GetPlacedObject(0).name.Contains("Catwalk") && (plenumAttachment.layer != TileLayer.Wire &&
                                                                             plenumAttachment.layer != TileLayer.FurnitureBase))
            {
                return(false);
            }

            // Can only build on a Plenum and not Catwalks or Lattices
            if (!plenumObject.GetPlacedObject(0).name.Contains("Plenum") && !plenumObject.GetPlacedObject(0).name.Contains("Catwalk"))
            {
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Checks whether a wall attachment can be placed and if it collides with a nearby wall.
        /// </summary>
        /// <param name="map"></param>
        /// <param name="position"></param>
        /// <param name="wallAttachment"></param>
        /// <param name="dir"></param>
        /// <returns></returns>
        private static bool CanBuildWallAttachment(TileMap map, Vector3 position, TileObjectSo wallAttachment, Direction dir)
        {
            TileObject wallObject = map.GetTileObject(TileLayer.Turf, position);

            // Cannot build when there isn't a wall
            if (wallObject.IsCompletelyEmpty() || wallObject.GetPlacedObject(0).GetGenericType() != TileObjectGenericType.Wall)
            {
                return(false);
            }

            // No low wall mounts on windows
            if (wallObject.GetPlacedObject(0).GetName().Contains("window") && wallAttachment.layer == TileLayer.LowWallMount)
            {
                return(false);
            }

            // Cannot build wall mount if it collides with the next wall
            PlacedTileObject[] adjacentObjects = map.GetNeighbourObjects(TileLayer.Turf, 0, position);
            if (adjacentObjects[(int)dir] && adjacentObjects[(int)dir].GetGenericType() == TileObjectGenericType.Wall)
            {
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Creates a new PlacedTileObject from a TileObjectSO at a given position and direction. Uses NetworkServer.Spawn() if a server is running.
        /// </summary>
        /// <param name="worldPosition"></param>
        /// <param name="origin"></param>
        /// <param name="dir"></param>
        /// <param name="tileObjectSO"></param>
        /// <returns></returns>
        public static PlacedTileObject Create(Vector3 worldPosition, Vector2Int origin, Direction dir, TileObjectSo tileObjectSO)
        {
            GameObject placedGameObject = EditorAndRuntime.InstantiatePrefab(tileObjectSO.prefab);

            placedGameObject.transform.SetPositionAndRotation(worldPosition, Quaternion.Euler(0, TileHelper.GetRotationAngle(dir), 0));

            // Alternative name is required for walls as they can occupy the same tile
            if (TileHelper.ContainsSubLayers(tileObjectSO.layer))
            {
                placedGameObject.name += "_" + TileHelper.GetDirectionIndex(dir);
            }

            PlacedTileObject placedObject = placedGameObject.GetComponent <PlacedTileObject>();

            if (placedObject == null)
            {
                placedObject = placedGameObject.AddComponent <PlacedTileObject>();
            }

            placedObject.Setup(tileObjectSO, origin, dir);

            if (NetworkServer.active)
            {
                if (!NetworkClient.prefabs.ContainsValue(placedGameObject))
                {
                    Debug.LogWarning("Prefab was not found in the Spawnable list. Please add it.");
                }
                NetworkServer.Spawn(placedGameObject);
            }
            return(placedObject);
        }
        /// <summary>
        /// Main function for verifying if a tileObjectSO can be placed at a given location.
        /// </summary>
        /// <param name="map"></param>
        /// <param name="position"></param>
        /// <param name="subLayerIndex"></param>
        /// <param name="tileObjectSO"></param>
        /// <param name="dir"></param>
        /// <returns></returns>
        public static bool CanBuild(TileMap map, Vector3 position, int subLayerIndex, TileObjectSo tileObjectSO, Direction dir)
        {
            TileManager tileManager = TileManager.Instance;
            TileLayer   placedLayer = tileObjectSO.layer;

            TileObject[] tileObjects = new TileObject[TileHelper.GetTileLayers().Length];

            foreach (TileLayer layer in TileHelper.GetTileLayers())
            {
                tileObjects[(int)layer] = map.GetTileObject(layer, position);
            }

            // Cannot build anything unless a plenum is placed
            if (placedLayer != TileLayer.Plenum && !CanBuildOnPlenum(map, position, tileObjectSO, dir))
            {
                return(false);
            }

            // No wall mounts on non-walls
            if (tileObjects[(int)TileLayer.Turf].IsCompletelyEmpty() &&
                (placedLayer == TileLayer.LowWallMount || placedLayer == TileLayer.HighWallMount))
            {
                return(false);
            }

            switch (placedLayer)
            {
            case TileLayer.LowWallMount:
            case TileLayer.HighWallMount:
            {
                if (!CanBuildWallAttachment(map, position, tileObjectSO, dir))
                {
                    return(false);
                }
                break;
            }

            // No furniture inside walls
            case TileLayer.FurnitureBase:
            case TileLayer.FurnitureTop:
            case TileLayer.Overlay:
            {
                TileObject wallObject = map.GetTileObject(TileLayer.Turf, position);
                if (!wallObject.IsCompletelyEmpty() && wallObject.GetPlacedObject(0).GetGenericType() == TileObjectGenericType.Wall)
                {
                    return(false);
                }
                break;
            }

            // No walls on furniture
            case TileLayer.Turf when tileObjectSO.genericType == TileObjectGenericType.Wall && (!tileObjects[(int)TileLayer.FurnitureBase].IsCompletelyEmpty() ||
                                                                                                !tileObjects[(int)TileLayer.FurnitureTop].IsCompletelyEmpty() ||
                                                                                                !tileObjects[(int)TileLayer.Overlay].IsCompletelyEmpty()):
                return(false);
            }


            return(true);
        }