using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;


namespace RimWorld{

public enum SeedTargFindMode : byte
{
	ReproduceSeed,
	Cluster,
	MapEdge
}

public static class GenPlantReproduction
{
	public static bool TrySpawnSeed( IntVec3 cell, ThingDef plantDef, SeedTargFindMode mode, Thing plant = null )
	{
		IntVec3 seedTarg;
		if( !TryFindSeedTargFor( plantDef, cell, mode, out seedTarg ) )
		{
			//Nowhere to send the seed
			return false; 
		}

		Seed seed = (Seed)ThingMaker.MakeThing( plantDef.plant.seedDef);
		GenSpawn.Spawn ( seed, cell, Rot4.Random);
		seed.Launch( plant, seedTarg );

		if( DebugSettings.fastEcology )
		{
			seed.ForceInstantImpact();
		}

		return true;
	}

	public static bool TryFindSeedTargFor( ThingDef plantDef, IntVec3 root, SeedTargFindMode mode, out IntVec3 foundCell )
	{
		float radius = -1;
		if( mode == SeedTargFindMode.ReproduceSeed )
			radius = plantDef.plant.seedShootRadius;
		else if( mode == SeedTargFindMode.Cluster )
			radius = plantDef.plant.WildClusterRadiusActual;
		else if( mode == SeedTargFindMode.MapEdge )
			radius = 40;

		//Gather some data about the area around root
		int numFoundPlants = 0;
		int numFoundPlantsMyDef = 0;
		float totalFertility = 0;
		CellRect rect = CellRect.CenteredOn( root, Mathf.RoundToInt(radius) );
		rect.ClipInsideMap();
		for (int z = rect.minZ; z <= rect.maxZ; z++)
		{
			for (int x = rect.minX; x <= rect.maxX; x++)
			{
				var c = new IntVec3(x,0,z);
				Plant p = c.GetPlant();
				if( p != null )
				{
					numFoundPlants++;

					if( p.def == plantDef )
						numFoundPlantsMyDef++;
				}

				totalFertility += c.GetTerrain().fertility;
			}
		}

		//Determine theoretical number of desired plants of any type
		float numDesiredPlants = totalFertility * Find.Map.Biome.plantDensity;
		bool full = numFoundPlants > numDesiredPlants;
		bool overloaded = numFoundPlants > numDesiredPlants * 1.25f;

		if( overloaded )
		{
			foundCell = IntVec3.Invalid;
			return false;
		}

		//Determine num desired plants of my def
		BiomeDef curBiome = Find.Map.Biome;
		float totalCommonality = curBiome.AllWildPlants.Sum( pd=>curBiome.CommonalityOfPlant(pd) );
		float minProportion = curBiome.CommonalityOfPlant(plantDef) / totalCommonality;
		float maxProportion = (curBiome.CommonalityOfPlant(plantDef) *plantDef.plant.wildCommonalityMaxFraction) / totalCommonality;

		//Too many plants of my type nearby - don't reproduce
		float maxDesiredPlantsMyDef = numDesiredPlants * maxProportion;
		if( numFoundPlantsMyDef > maxDesiredPlantsMyDef )
		{
			foundCell = IntVec3.Invalid;
			return false;
		}

		//Too many plants nearby for the biome/total fertility - don't reproduce
		//UNLESS there are way too few of my kind of plant
		float minDesiredPlantsMyDef = numDesiredPlants * minProportion;
		bool desperateForPlantsMyDef = numFoundPlantsMyDef < minDesiredPlantsMyDef * 0.5f;
		if( full && !desperateForPlantsMyDef )
		{
			foundCell = IntVec3.Invalid;
			return false;
		}

		//We need to plant something!
		System.Predicate<IntVec3> seedTargValidator = c=>
		{
			if( !plantDef.CanEverPlantAt(c) )
				return false;

			if( !GenPlant.SnowAllowsPlanting(c) )
				return false;

			if( !root.InHorDistOf( c, radius) )
				return false;

			if( !GenSight.LineOfSight( root, c, skipFirstCell: true ) )
				return false;

			return true;
		};

		return CellFinder.TryFindRandomCellNear( root,
											Mathf.CeilToInt(radius),
											seedTargValidator,
											out foundCell );
	}
}}

