/// <summary>
 /// Remove a population from the RPM.
 /// </summary>
 public void RemovePopulation(Population population)
 {
     if (!Populations.Contains(population))
     {
         return;
     }
     Populations.Remove(population);
     TypesOfTerrain.Remove(population);
     openID.Enqueue(PopulationToID[population]);
     PopulationByID.Remove(PopulationToID[population]); // free ID
     PopulationToID.Remove(population);                 // free ID
     CleanupAccessMapForRecycledID();
 }
    /// <summary>
    /// Add a population to the RPM.
    /// </summary>
    public void AddPopulation(Population population)
    {
        if (!Populations.Contains(population))
        {
            // ignore their old id and assign it a new one
            int id = openID.Dequeue();

            // since IDs after maxPopulation-1 are recycled ids, we need to do clean up old values
            if (id == lastRecycledID)
            {
                CleanupAccessMapForRecycledID();
            }
            PopulationToID.Add(population, id);
            PopulationByID.Add(id, population);
            Populations.Add(population);

            TypesOfTerrain.Add(population, new int[(int)TileType.TypesOfTiles]);
            // generate the map with the new id
            GenerateMap(population);
        }
    }
    /// <summary>
    /// Populate the access map for a population with depth first search.
    /// </summary>
    /// <param name="population">The population to be generated, assumed to be in Populations</param>
    /// <remarks>When this is called that means the terrain had changed for sure</remarks>
    private void GenerateMap(Population population)
    {
        Stack <Vector3Int>   stack        = new Stack <Vector3Int>();
        HashSet <Vector3Int> accessible   = new HashSet <Vector3Int>();
        HashSet <Vector3Int> unaccessible = new HashSet <Vector3Int>();
        Vector3Int           cur;
        List <Vector3Int>    newAccessibleLocations = new List <Vector3Int>();
        List <Vector3Int>    newLiquidLocations     = new List <Vector3Int>();
        List <float[]>       newLiquidCompositions  = new List <float[]>();

        if (!this.AccessibleArea.ContainsKey(population))
        {
            this.AccessibleArea.Add(population, new List <Vector3Int>());
        }

        // Number of shared tiles
        long[]             SharedTiles         = new long[maxPopulation];
        TileDataController gridSystemReference = GameManager.Instance.m_tileDataController;

        // starting location
        Vector3Int location = gridSystemReference.WorldToCell(population.transform.position);

        stack.Push(location);

        // Clear TypesOfTerrain for given population
        this.TypesOfTerrain[population] = new int[(int)TileType.TypesOfTiles];

        // iterate until no tile left in list, ends in iteration 1 if population.location is not accessible
        while (stack.Count > 0)
        {
            // next point
            cur = stack.Pop();

            if (accessible.Contains(cur) || unaccessible.Contains(cur))
            {
                // checked before, move on
                continue;
            }
            // Check tiles that are under construction, make them inaccessible
            //if (this.buildBufferManager.IsConstructing(cur.x,cur.y))
            //{
            //    unaccessible.Add(cur);
            //    population.HasAccessibilityChanged = true;
            //    continue;
            //}
            // check if tilemap has tile and if population can access the tile (e.g. some cannot move through water)
            GameTile tile = gridSystemReference.GetGameTileAt(cur);
            // Get liquid tile info
            if (tile != null && tile.type == TileType.Liquid)
            {
                float[] composition = new float[] { 0, 0, 0 };
                LiquidbodyController.Instance.GetLiquidContentsAt(cur, out composition, out bool constructing);

                if (!this.populationAccessibleLiquidCompositions.ContainsKey(population))
                {
                    this.populationAccessibleLiquidCompositions.Add(population, new List <float[]>());
                }

                if (!this.populationAccessibleLiquidLocations.ContainsKey(population))
                {
                    this.populationAccessibleLiquidLocations.Add(population, new List <Vector3Int>());
                }

                newLiquidCompositions.Add(composition);
                newLiquidLocations.Add(cur);
            }

            if (tile != null && population.Species.AccessibleTerrain.Contains(tile.type))
            {
                // save the accessible location
                accessible.Add(cur);

                // save to accessible location
                newAccessibleLocations.Add(cur);

                TypesOfTerrain[population][(int)tile.type]++;

                if (!AccessMap.ContainsKey(cur))
                {
                    AccessMap.Add(cur, 0L);
                }
                AccessMap[cur] |= 1L << PopulationToID[population];

                // Collect info on how the population's space overlaps with others
                for (int i = 0; i < Populations.Count; i++)
                {
                    SharedTiles[i] += (AccessMap[cur] >> PopulationToID[Populations[i]]) & 1L;
                }

                // check all 4 tiles around, may be too expensive/awaiting optimization
                stack.Push(cur + Vector3Int.left);
                stack.Push(cur + Vector3Int.up);
                stack.Push(cur + Vector3Int.right);
                stack.Push(cur + Vector3Int.down);
            }
            else
            {
                // save the Vector3Int since it is already checked
                unaccessible.Add(cur);
            }

            population.HasAccessibilityChanged = true;
        }

        // Amount of accessible area
        //Spaces[population] = accessible.Count;

        // Store the info on overlapping space
        int id = PopulationToID[population];

        SharedSpaces[id] = SharedTiles;

        // Update the new info for pre-existing populations
        for (int i = 0; i < SharedSpaces[id].Length; i++)
        {
            if (PopulationByID.ContainsKey(i) && SharedSpaces[id][i] != 0)
            {
                SharedSpaces[i][id] = SharedSpaces[id][i];
            }
        }

        // Update space
        if (population.HasAccessibilityChanged)
        {
            this.AccessibleArea[population] = newAccessibleLocations;
            this.populationAccessibleLiquidCompositions[population] = newLiquidCompositions;
            this.populationAccessibleLiquidLocations[population]    = newLiquidLocations;
        }
    }