// this functions connects regions by creating tunnels between each pair of them
    void connectRegions()
    {
        mapTiles = GameObject.FindGameObjectsWithTag("Tile");
//        GameObject[] mapTiles = new GameObject[MapWidth * MapHeight];
//        Array.Copy(GameObject.FindGameObjectsWithTag("Tile"), mapTiles, MapWidth * MapHeight);

        IComparer myComparer = new MapTilesComparer();

        Array.Sort(mapTiles, myComparer);

        int  startX, startY, endX, endY, x, y, stepX, stepY;
        bool continueX, continueY;

        // connect regions pair by pair
        for (int i = 1; i < regions.Count - 1; i++)
        {
            // take the coordinates of the first node of each of the 2 regions
            startX = regions[i][0].XCoordinateInGrid;
            startY = regions[i][0].ZCoordinateInGrid;
            endX   = regions[i + 1][0].XCoordinateInGrid;
            endY   = regions[i + 1][0].ZCoordinateInGrid;

            if (startX < endX)
            {
                stepX = 1; // go right
            }
            else
            {
                stepX = -1; // go left
            }
            if (startY < endY)
            {
                stepY = 1; // go up
            }
            else
            {
                stepY = -1; // go down
            }
            x         = startX;
            y         = startY;
            continueX = true;
            continueY = true;
            while (continueX || continueY)
            {
                // if a target coordinate is reached do not continue in this direction
                if (x == endX)
                {
                    continueX = false;
                }
                if (y == endY)
                {
                    continueY = false;
                }

                if (continueX)
                {
                    x += stepX;
                    if (mapTiles[MapWidth * y + x])
                    {
                        if (mapTiles[MapWidth * y + x].layer == 9) // if it is a wall
                        {
                            Vector3 _position = new Vector3((float)(x), (float)(y), 0);

                            int index = mapTiles[MapWidth * y + x].transform.GetSiblingIndex();
                            // make the wall tile a floor tile
                            DestroyImmediate(mapTiles[MapWidth * y + x]);
                            createFloorTile(x, y);
                        }
                    }
                }

                if (continueY)
                {
                    y += stepY;
                    if (mapTiles[MapWidth * y + x])
                    {
                        if (mapTiles[MapWidth * y + x].layer == 9)
                        {
                            Vector3 _position = new Vector3((float)(x), (float)(y), 0);

                            int index = mapTiles[MapWidth * y + x].transform.GetSiblingIndex();
                            DestroyImmediate(mapTiles[MapWidth * y + x]);
                            GameObject floorTile = createFloorTile(x, y);
                        }
                    }
                }
            }
        }
    }
    // need optimization for finding neighbours: a for loop for all the adjacent nodes
    void smoothMap()
    {
//        GameObject[] mapTiles = GameObject.FindGameObjectsWithTag("Tile");

        IComparer myComparer = new MapTilesComparer();

        Array.Sort(mapTiles, myComparer);

        int floorTilesNum, x, y;

        foreach (GridNode node in regions[0])
        {
            x = node.XCoordinateInGrid;
            y = node.ZCoordinateInGrid;

            if (mapTiles[y * MapWidth + x].layer == 9)
            {
                floorTilesNum = 0;

                if (x + 1 < MapWidth)
                {
                    if (mapTiles[y * MapWidth + (x + 1)].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (x - 1 >= 0)
                {
                    if (mapTiles[y * MapWidth + (x - 1)] && mapTiles[y * MapWidth + (x - 1)].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (y + 1 < MapHeight)
                {
                    if (mapTiles[(y + 1) * MapWidth + x] && mapTiles[(y + 1) * MapWidth + x].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (y - 1 >= 0)
                {
                    if (mapTiles[(y - 1) * MapWidth + x] && mapTiles[(y - 1) * MapWidth + x].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (x + 1 < MapWidth && y + 1 < MapHeight)
                {
                    if (mapTiles[(y + 1) * MapWidth + (x + 1)] && mapTiles[(y + 1) * MapWidth + (x + 1)].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (x - 1 >= 0 && y + 1 < MapHeight)
                {
                    if (mapTiles[(y + 1) * MapWidth + (x - 1)] && mapTiles[(y + 1) * MapWidth + (x - 1)].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (x + 1 < MapWidth && y - 1 >= 0)
                {
                    if (mapTiles[(y - 1) * MapWidth + (x + 1)] && mapTiles[(y - 1) * MapWidth + (x + 1)].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (x - 1 >= 0 && y - 1 >= 0)
                {
                    if (mapTiles[(y - 1) * MapWidth + (x - 1)] && mapTiles[(y - 1) * MapWidth + (x - 1)].layer == 8)
                    {
                        floorTilesNum++;
                    }
                }

                if (floorTilesNum >= 5)
                {
                    DestroyImmediate(mapTiles[y * MapWidth + x]);
                    createFloorTile(x, y);
                }
            }
        }
    }
    // this function creates 2 exits for the main rooms and 1 for the starting room
    void createExits()
    {
        mapTiles = GameObject.FindGameObjectsWithTag("Tile");
//        GameObject[] mapTiles = new GameObject[MapWidth * MapHeight];
//        Array.Copy(GameObject.FindGameObjectsWithTag("Tile"), mapTiles, MapWidth * MapHeight);

        Debug.Log("MAPTILES COUNT BEFORE: " + mapTiles.Length);
        IComparer myComparer = new MapTilesComparer();

        Array.Sort(mapTiles, myComparer);

        int x, y;

        if (SceneManager.GetActiveScene().name == "main")
        {
            // get the coordinates of the first node of the first walkable region
            x = regions[1][0].XCoordinateInGrid;
            y = regions[1][0].ZCoordinateInGrid;

            for (int j = y - 1; j >= 0; j--)
            {
                Vector3 _position = new Vector3((float)(x), (float)(j), 0);

                int index = mapTiles[MapWidth * j + x].transform.GetSiblingIndex();

                // destroy the wall tile first
                DestroyImmediate(mapTiles[MapWidth * j + x]);

                // instantiate the floor tile
                createFloorTile(x, j);
//                floorTile.transform.SetSiblingIndex(index);

                if (j == 0) // if it is the downmost node
                {
                    // spawn player and companion

                    _position.y++;
                    GameObject p = Instantiate(player, _position, Quaternion.identity);
                    GameObject.FindGameObjectWithTag("MainCamera").transform.parent        = p.transform;
                    GameObject.FindGameObjectWithTag("MainCamera").transform.localPosition = new Vector3(0, 0, -1);

                    if (companionHealth > 0)
                    {
                        _position.y--;
                        GameObject ai = Instantiate(AI, _position, Quaternion.identity);
                        ai.GetComponentInChildren <MeshRenderer>().sortingOrder = 2;
                    }
                }
            }
        }

        // get the coordinates of the last node of the last walkable region
        x = regions.Last().Last().XCoordinateInGrid;
        y = regions.Last().Last().ZCoordinateInGrid;

        for (int j = y + 1; j < MapHeight; j++)
        {
            Vector3 _position = new Vector3((float)(x), (float)(j), 0);

            int index = mapTiles[MapWidth * j + x].transform.GetSiblingIndex();
            // destroy the wall tile first
            DestroyImmediate(mapTiles[MapWidth * j + x]);

            // instantiate the floor tile
            GameObject floorTile = createFloorTile(x, j);

//            floorTile.transform.SetSiblingIndex(index);

            if (j == MapHeight - 1) // if it is the upmost node
            {
                // create exit
                floorTile.AddComponent <BoxCollider2D>();
                floorTile.GetComponent <BoxCollider2D>().isTrigger = true;
                floorTile.AddComponent <Door>();
            }
        }
    }
    // this function changes sprites of wall tiles that are above walkable floor tiles in order to make the map more realistic
    void createEdgeWalls()
    {
//        Debug.Log("REGIONS COUNT INSIDE CREATE EDGE WALLS: " + regions.Count);
//        Debug.Log("NODES COUNT INSIDE REGION[0] INSIDE CREATE EDGE WALLS: " + regions[0].Count);


//        Array.Copy(GameObject.FindGameObjectsWithTag("Tile"), mapTiles, MapWidth * MapHeight);

        IComparer myComparer = new MapTilesComparer();

        Array.Sort(mapTiles, myComparer);

//        Debug.Log("MAP TILES COUNT: " + mapTiles.Length);

//        foreach (var tile in mapTiles)
//        {
//            Debug.Log("Tile: x-> " + tile.transform.position.x + ", y-> " + tile.transform.position.y + ", LAYER: " +
//                      tile.layer);
//        }

//        Debug.Log("MAPTILES COUNT AFTER: " + mapTiles.Length);

        int x, y;

        foreach (GridNode node in regions[0])
        {
            x = node.XCoordinateInGrid;
            y = node.ZCoordinateInGrid;
            if (y - 1 >= 0)
            {
                GameObject tileBelow = mapTiles[(y - 1) * MapWidth + x];
                if (tileBelow.layer == 8)
                {
                    mapTiles[MapWidth * y + x].GetComponent <SpriteRenderer>().sprite   = wallTiles[Random.Range(0, wallTiles.Length)];
                    mapTiles[MapWidth * y + x].GetComponent <SpriteRenderer>().material = wallMaterial;
                }
            }
        }


        bool       changed;
        GameObject currWallTile;

        foreach (GridNode node in regions[0])
        {
            x = node.XCoordinateInGrid;
            y = node.ZCoordinateInGrid;

            currWallTile = mapTiles[y * MapWidth + x];
//            Debug.Log("CURRENT WALL TILE COORDINATES: " + x + ", " + y);

            changed = false;
            if (x + 1 < MapWidth && y - 1 >= 0)
            // +-   +: wall, -: floor
            // --
            {
                GameObject tileBelow     = mapTiles[(y - 1) * MapWidth + x];
                GameObject tileRight     = mapTiles[y * MapWidth + (x + 1)];
                GameObject tileDownRight = mapTiles[(y - 1) * MapWidth + (x + 1)];

                if (tileBelow.layer == 8 && tileRight.layer == 8 && tileDownRight.layer == 8)
                {
                    GameObject floorTile = createFloorTile(x, y);
                    floorTile.GetComponent <SpriteRenderer>().sortingOrder = -1;

//                    currWallTile.GetComponent<SpriteRenderer>().sprite = cornerWallTile;
                    currWallTile = createCornerWallTile(currWallTile);


                    currWallTile.gameObject.transform.Rotate(0, 0, -90);
                    changed = true;
                }
            }

            if (x + 1 < MapWidth && y + 1 < MapHeight && changed == false)
            // --
            // +-
            {
                GameObject tileUp      = mapTiles[(y + 1) * MapWidth + x];
                GameObject tileRight   = mapTiles[y * MapWidth + (x + 1)];
                GameObject tileUpRight = mapTiles[(y + 1) * MapWidth + (x + 1)];

                if (tileUp.layer == 8 && tileRight.layer == 8 && tileUpRight.layer == 8)
                {
                    GameObject floorTile = createFloorTile(x, y);
                    floorTile.GetComponent <SpriteRenderer>().sortingOrder = -1;

                    currWallTile = createCornerWallTile(currWallTile);
                    changed      = true;
                }
            }

            if (x - 1 >= 0 && y - 1 >= 0 && changed == false)
            // -+
            // --
            {
                GameObject tileLeft     = mapTiles[y * MapWidth + (x - 1)];
                GameObject tileDown     = mapTiles[(y - 1) * MapWidth + x];
                GameObject tileDownLeft = mapTiles[(y - 1) * MapWidth + (x - 1)];

                if (tileLeft.layer == 8 && tileDown.layer == 8 && tileDownLeft.layer == 8)
                {
                    GameObject floorTile = createFloorTile(x, y);
                    floorTile.GetComponent <SpriteRenderer>().sortingOrder = -1;

//                    Debug.Log("PASSED");
                    currWallTile = createCornerWallTile(currWallTile);

                    currWallTile.gameObject.transform.Rotate(0, 0, 180);
                    changed = true;
                }
            }

            if (x - 1 >= 0 && y + 1 < MapHeight && changed == false)
            // --
            // -+
            {
                GameObject tileLeft   = mapTiles[y * MapWidth + (x - 1)];
                GameObject tileUp     = mapTiles[(y + 1) * MapWidth + x];
                GameObject tileUpLeft = mapTiles[(y + 1) * MapWidth + (x - 1)];

                if (tileLeft.layer == 8 && tileUp.layer == 8 && tileUpLeft.layer == 8)
                {
                    GameObject floorTile = createFloorTile(x, y);
                    floorTile.GetComponent <SpriteRenderer>().sortingOrder = -1;
                    currWallTile = createCornerWallTile(currWallTile);

                    currWallTile.gameObject.transform.Rotate(0, 0, 90);
                    changed = true;
                }
            }
        }
    }