#include "stdafx.h"
#include "BigThink.h"

#include "Behaviour.h"
#include "BehaviourTree.h"

using namespace std::placeholders;

namespace BigThink
{
	BT::Selector* ItemHandling() {
		constexpr float HpThreshold{ 2.f };

		return
			new BT::Selector({
				new BT::Sequence({
					new BT::Conditional(std::bind(BT_Condition::ItemInInv, _1, eItemType::MEDKIT)),
					new BT::Conditional(std::bind(BT_Condition::HpUnderThreshold, _1, HpThreshold)),
					new BT::Action(std::bind(BT_Action::UseItem, _1, eItemType::MEDKIT))
				}),
				new BT::Sequence({
					new BT::Conditional(std::bind(BT_Condition::ItemInInv, _1, eItemType::FOOD)),
					new BT::Conditional(BT_Condition::CheckMinNeededEnergy),
					new BT::Action(std::bind(BT_Action::UseItem, _1, eItemType::FOOD))
				})
				});
	}

	BT::Sequence* ZombieHandling() {
		constexpr float minShotgunAngleDiff{ .2f };
		constexpr float minPistolAngleDiff{ .1f };
		constexpr float maxShotgunShootRange{ 8.f };
		constexpr float maxPistolShootRange{ 15.f };

		const std::string ShotgunTimer{ "Shotgun" };
		constexpr bool ShotgunDoOnce{ true };

		const std::string PistolTimer{ "Pistol" };
		constexpr bool PistolDoOnce{ true };

		return
			new BT::Sequence({
				new BT::Conditional(BT_Condition::SeeZombie),
				new BT::Sequence({
					new BT::Action(BT_Action::AvoidingZombie),
					new BT::Conditional(BT_Condition::HasWeapon),
					new BT::Action(BT_Action::SetZombieTarget),
					new BT::Sequence({
						new BT::Action(BT_Action::RotateToZombie),
						new BT::Selector({
							new BT::Sequence({
								new BT::Conditional(std::bind(BT_Condition::ItemInInv, _1, eItemType::SHOTGUN)),
								new BT::Conditional(std::bind(BT_Condition::InRange, _1, maxShotgunShootRange)),
								new BT::Action(std::bind(BT_Action::ReadyToShoot, _1, minShotgunAngleDiff)),
								new BT::Conditional(std::bind(BT_Condition::CheckTimer, _1, ShotgunTimer, ShotgunDoOnce)),
								new BT::Action(std::bind(BT_Action::Shoot, _1, eItemType::SHOTGUN)),
								new BT::Action(std::bind(BT_Action::SetTimer, _1, ShotgunTimer, ShotgunDoOnce))
							}),
							new BT::Sequence({
								new BT::Conditional(std::bind(BT_Condition::ItemInInv, _1, eItemType::PISTOL)),
								new BT::Conditional(std::bind(BT_Condition::InRange, _1, maxPistolShootRange)),
								new BT::Action(std::bind(BT_Action::ReadyToShoot, _1, minPistolAngleDiff)),
								new BT::Conditional(std::bind(BT_Condition::CheckTimer, _1, PistolTimer, PistolDoOnce)),
								new BT::Action(std::bind(BT_Action::Shoot, _1, eItemType::PISTOL)),
								new BT::Action(std::bind(BT_Action::SetTimer, _1, PistolTimer, PistolDoOnce))
							})
						})
					})
				})
				});
	}

	BT::Sequence* PurgeZoneHandling() {
		constexpr int searchDegree{ 5 };
		return
			new BT::Sequence({
					new BT::Conditional(BT_Condition::InPurgeZone),
					new BT::Action(std::bind(BT_Action::FindClosestEdge, _1, searchDegree)),
					new BT::Action(BT_Action::GoToTarget)
				});
	}

	BT::PartialSequence* PickUpHandling() {
		return
			new BT::PartialSequence({
				new BT::Conditional(BT_Condition::SeeItem),
				new BT::Action(BT_Action::DisableSpin),
				new BT::Action(BT_Action::SetItemAsTarget),
				new BT::Action(BT_Action::GoToTarget),
				new BT::Selector({
					new BT::Sequence({
						new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, _1, eItemType::GARBAGE)),
						new BT::Action(BT_Action::DestroyItemOnFloor)
					}),
					new BT::Sequence({
						new BT::Conditional(BT_Condition::EmptyValue),
						new BT::Action(BT_Action::SwapItem)
					}),
					new BT::Sequence({
						new BT::Conditional(BT_Condition::InvIsNotFull),
						new BT::Action(BT_Action::PickUpItem)
					}),
					new BT::Sequence({
						new BT::Conditional(BT_Condition::InvIsFull),
						new BT::Selector({
							new BT::Sequence({
								new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem,_1, eItemType::FOOD)),
								new BT::Action(BT_Action::CheckItem)
							}),
							new BT::Sequence({
								new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, _1, eItemType::MEDKIT)),
								new BT::Action(BT_Action::CheckItem)
							}),
							new BT::Sequence({
								new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, _1, eItemType::SHOTGUN)),
								new BT::Action(BT_Action::CheckItem)
							}),
							new BT::Sequence({
								new BT::Conditional(std::bind(BT_Condition::IsTypeOfItem, _1, eItemType::PISTOL)),
								new BT::Action(BT_Action::CheckItem)
							})
						})
					})
				})
				});
	}

	BT::Selector* HouseHandling() {
		constexpr float maxTravelDistance{ 400.f };
		constexpr int searchRadius{ 500 };
		constexpr int randomRadius{ 50 };
		constexpr int searchDegree{ 5 };
		constexpr float InsideOffset{ 5.f };
		constexpr float gridCellSize{ 50.f };

		const std::string BeforeLeavingTimer{ "BeforeLeaving" };
		constexpr bool BeforeLeavingDoOnce{ false };

		const std::string NoHouseTimer{ "NoHouse" };
		constexpr float noHouseTimeout = 5.0f;
		constexpr bool NoHouseDoOnce{ false };

		const std::string HouseExploreTimer{ "HouseExplore" };
		constexpr float houseExploreTimeout = 2.0f;
		constexpr bool HouseExploreDoOnce{ false };

		return new BT::Selector({
			// [1] House visibility update
			new BT::Sequence({
				new BT::Conditional(BT_Condition::SeeHouse),
				new BT::Action(BT_Action::CheckHouses),
				//new BT::Action(BT_Grid::UpdateExplorationGrid),
				new BT::Action(std::bind(BT_Action::SetTimer, _1, NoHouseTimer, NoHouseDoOnce))
			}),

				// [2] Inside house logic
				new BT::PartialSequence({
					new BT::Conditional(BT_Condition::InsideTargetHouse),
					new BT::Action(BT_Action::SetExpireDate),
					new BT::Action(std::bind(BT_Action::SetTimer, _1, BeforeLeavingTimer, BeforeLeavingDoOnce)),
					new BT::Selector({
						new BT::PartialSequence({
							new BT::Conditional(std::bind(BT_Condition::CheckTimer, _1, BeforeLeavingTimer, BeforeLeavingDoOnce)),
							new BT::Selector({
								new BT::PartialSequence({
									new BT::Conditional(BT_Condition::NewHouse),
									new BT::Action(std::bind(BT_Action::GetHouseAsTarget, _1, maxTravelDistance)),
									new BT::Action(BT_Action::EnableSpin),
									new BT::Action(BT_Action::GoToTarget),
								}),
								new BT::PartialSequence({
									new BT::Action(std::bind(BT_Action::TryFindHouse, _1, searchRadius, searchDegree)),
									new BT::Action(BT_Action::EnableSpin),
									new BT::Action(BT_Action::GoToTarget)
								}),
								new BT::PartialSequence({
									new BT::Conditional(BT_Condition::ReExploreHouse),
									new BT::Action(std::bind(BT_Action::GetHouseAsTarget, _1, maxTravelDistance)),
									new BT::Action(BT_Action::EnableSpin),
									new BT::Action(BT_Action::GoToTarget),
								}),
								new BT::PartialSequence({
									new BT::Action(std::bind(BT_Action::FindRandomLocation, _1, randomRadius)),
									new BT::Action(BT_Action::EnableSpin),
									new BT::Action(BT_Action::GoToTarget)
								})
							}),
						}),
						new BT::PartialSequence({
							new BT::Action(std::bind(BT_Action::GetInsideTarget, _1, InsideOffset)),
							new BT::Action(BT_Action::EnableSpin),
							new BT::Action(BT_Action::GoToTarget)
						}),
					})
				}),

				// [3] Outside house behavior
				new BT::Selector({
					new BT::PartialSequence({
						new BT::Conditional(BT_Condition::NewHouse),
						new BT::Action(std::bind(BT_Action::GetHouseAsTarget, _1, maxTravelDistance)),
						new BT::Action(BT_Action::EnableSpin),
						new BT::Action(BT_Action::GoToTarget),
					}),
					new BT::PartialSequence({
						new BT::Action(BT_Grid::FindUnexploredCell),
						new BT::Action(BT_Action::EnableSpin),
						new BT::Action(BT_Action::GoToTarget)
					}),
					new BT::PartialSequence({
						new BT::Conditional(std::bind(BT_Condition::CheckTimer, _1, NoHouseTimer, NoHouseDoOnce)),
						new BT::Action(std::bind(BT_Action::SetTimer, _1, NoHouseTimer, NoHouseDoOnce)),
						new BT::Selector({
							new BT::PartialSequence({
								new BT::Action(BT_Grid::FindUnexploredCell),
								new BT::Action(BT_Action::EnableSpin),
								new BT::Action(BT_Action::GoToTarget)
							}),
							new BT::PartialSequence({
								new BT::Action(std::bind(BT_Action::FindRandomLocation, _1, randomRadius * 2)),
								new BT::Action(BT_Action::EnableSpin),
								new BT::Action(BT_Action::GoToTarget)
							})
						})
					}),
				}),
			});
	}


	
	BT::Selector* HandleBug() {
		const std::string StuckTimer{ "StuckTimer" };
		constexpr float stuckTimeout = 3.0f; // Time in seconds before considering stuck
		constexpr bool StuckDoOnce{ false };
		constexpr float unstuckRange = 10.0f; // Range to pick a random target when stuck

		return
			new BT::Selector({
				new BT::Sequence({
					new BT::Conditional(std::bind(BT_Condition::CheckMovement, _1, stuckTimeout)),
					new BT::Conditional(std::bind(BT_Condition::CheckTimer, _1, StuckTimer, StuckDoOnce)),
					new BT::Action(std::bind(BT_Action::SetTimer, _1, StuckTimer, StuckDoOnce)),
					new BT::Action(std::bind(BT_Action::FindRandomLocation, _1, unstuckRange)),
					new BT::Action(BT_Action::EnableSpin),
					new BT::Action(BT_Action::GoToTarget)
				})
			});
	}
}
