--to do
--go towards boxes
--account for arrows and paladins
--account for boss size

local MoveDirections = {Vector3.new(5,0,0),Vector3.new(-5,0,0),Vector3.new(0,0,5),Vector3.new(0,0,-5)}
local GasDirections = {Vector3.new(5,0,0),Vector3.new(-5,0,0),Vector3.new(0,0,5),Vector3.new(0,0,-5),Vector3.new(5,0,5),Vector3.new(-5,0,5),Vector3.new(5,0,-5),Vector3.new(-5,0,-5)}

return function(Map)
	local Pathfinding = require(game.ReplicatedStorage.PathfindFunctions)(Map)
	local Movement = require(game.ReplicatedStorage.EnemyAi.Movement)(Map)
	local Self = {}
	local ExploredPositions = {}
	local OutsidePositions = {}
	
	Self.CheckSquare = function(Position)
		if table.find(Map,Vector3.new(Position.X, 0, Position.Z)) then
			return true
		end
		return false
	end
	
	Self.GetDangerSpots = function(Enemies)
		local Spots = {}
		for i, Enemy in pairs (Enemies) do
			if Enemy.IsAttacking then
				for i, v in pairs(MoveDirections) do 
					table.insert(Spots, Enemy.Position - Vector3.new(0, Enemy.Position.Y) + v)
				end
			end
		end
		for i, Projectile in pairs(workspace.ActiveProjectiles:GetChildren()) do
			local NewPos = Projectile.PrimaryPart.Position + Projectile.MoveDirection.Value
			local FlooredSpot = Vector3.new(math.floor(NewPos.X/5)*5, 0,math.floor(NewPos.Z/5)*5)
			table.insert(Spots, FlooredSpot)
			
		end
		for i, Trap in pairs(workspace.ActiveTraps:GetChildren()) do
			table.insert(Spots, Trap.PrimaryPart.Position)
			if Trap.PrimaryPart.Size.X >= 12 then
				for i, v in pairs(GasDirections) do
					table.insert(Spots, Trap.PrimaryPart.Position + v)
				end
			end
		end
		return Spots
	end
	
	Self.GetAttackersOfSpot = function(Position)
		local Attackers = {}
		for i, Model in pairs(workspace.ActiveEnemies:GetChildren()) do 
			if (Model.PrimaryPart.Position - Position).Magnitude <= 7 then
				table.insert(Attackers, Model)
			end
		end
		return Attackers
	end
	
	Self.GetEnemies = function()
		local EnemyTable = {}
		for i ,v in pairs(workspace.ActiveEnemies:GetChildren()) do 
			local SpotData = {}
			SpotData.Position = v.PrimaryPart.Position
			SpotData.IsAttacking = v.PrimaryPart.BrickColor == BrickColor.new("Really red")
			table.insert(EnemyTable, SpotData)
		end
		return EnemyTable
	end
	
	Self.GetEnemyAtPosition = function(Position, EnemyTable)
		for i, v in pairs(EnemyTable) do 
			if Vector3.new(v.Position.X, 0, v.Position.Z) == Vector3.new(Position.X, 0, Position.Z) then
				return v
			end
		end
		return false
	end
	
	Self.CheckIfDangerSpot = function(Position, DangerSpots)
		for i, v in pairs(DangerSpots) do
			if Vector3.new(v.X, 0, v.Z) == Vector3.new(Position.X, 0, Position.Z) then
				return v
			end
		end
		return false
	end
	
	Self.GetBestCostingSpot = function(Spots)
		local BestCost = -math.huge
		local BestSpots = {}
		for i, v in pairs(Spots) do
			if v.Cost > BestCost then
				BestCost = v.Cost
				BestSpots = {}
				table.insert(BestSpots, v)
			elseif v.Cost == BestCost then
				table.insert(BestSpots, v)
			end
			
		end
		return BestSpots[math.random(1, #BestSpots)]
	end
	
	Self.PositionReachedAmount = function(Position)
		local Amount = 0
		for i, v in pairs(ExploredPositions) do 
			if Vector3.new(v.X, 0, v.Z) == Position - Vector3.new(Position.Y) then
				Amount += 1
			end
		end
		return Amount
	end
	
	Self.UpdateEnemies = function(EnemyTable)
		local NewTable = {}
		for i, v in pairs(EnemyTable) do 
			if v.IsAttacking then
				local MoveDirection = Movement.GetNearestPlayerDirection(v.Position, false, false, nil, true)
				local NewData = {}
				NewData.Position = v.Position + MoveDirection
				-- you could check but this is the lazy way
				NewData.IsAttacking = true
			end
		end
		return NewTable
	end
	
	Self.GetOutsideSquares = function()
		local Squares = {}
		for i, Spot in pairs(Map) do
			for i, v in pairs(MoveDirections) do 
				if not Self.CheckSquare(Spot+v) then
					table.insert(Squares, Spot)
					break
				end
			end
		end
		OutsidePositions = Squares
	end
	
	Self.IsOutsideSquare = function(Position, Squares)
		if table.find(OutsidePositions, Vector3.new(Position.X,0,Position.Z)) then
			return true
		else
			return false
		end
	end
	
	Self.GetMoveInfo = function(EnemyTable, Depth, playerPos, SentDirections)
		local Spots = {}
		local PlayerPos = playerPos
		local DangerSpots = Self.GetDangerSpots(EnemyTable)
		local Loot = workspace.Loot:GetChildren()
		local Closestloot  = nil
		local LootPos = nil
		local ClosestLootDistance = math.huge
		if #Loot~=0 then
			for i, v in pairs(workspace.Loot:GetChildren()) do 
				local Distance = (v.PrimaryPart.Position - PlayerPos).Magnitude
				if Distance < ClosestLootDistance then
					Closestloot = v
					ClosestLootDistance= Distance
				end
			end
			LootPos = Closestloot.PrimaryPart.Position
		end
		
		
		local NewPos
		for i, v in pairs(MoveDirections) do
			DangerSpots = Self.GetDangerSpots(EnemyTable)
			--PlayerPos = game.Players.LocalPlayer.Character.PrimaryPart.Position
			
			local OriginPos = PlayerPos
			local AttackDirection = OriginPos + v
			
			--something wrong
		
			NewPos = (not Self.CheckSquare(AttackDirection) or Self.GetEnemyAtPosition(AttackDirection, EnemyTable))and OriginPos or AttackDirection
			
			
			local Directions 
			if SentDirections then
				Directions = SentDirections
				table.remove(Directions, 1)
			end
			
			local Directions = (not SentDirections and LootPos) and Pathfinding.GetDirections(PlayerPos, LootPos)
			
			-- could check if there are still boxes
			
			local IsDangerous = Self.CheckIfDangerSpot(NewPos, DangerSpots)
			local Enemy = Self.GetEnemyAtPosition(AttackDirection, EnemyTable)
			local TimesPositionReached = Self.PositionReachedAmount(NewPos) 
			local HasLoot = Closestloot and (Closestloot.PrimaryPart.Position - NewPos).Magnitude<3
			local IsOutsideSquare = Self.IsOutsideSquare(NewPos)
			local IsDirectionOfCrate = Directions and Directions[1] == v
			
			-- make it realize that an enemy dies when it's attacked at 0 health
			-- make this account for paladins
			local AttackersOfPlayerSpot = Self.GetAttackersOfSpot(PlayerPos)
			local CanAttackThoughRed = #AttackersOfPlayerSpot== 1 and AttackersOfPlayerSpot[1].Health.Value == 1 and AttackersOfPlayerSpot[1].PrimaryPart.Position == AttackDirection + Vector3.new(0, -AttackDirection.Y + AttackersOfPlayerSpot[1].PrimaryPart.Position.Y)
			local Cost = (IsDangerous and not CanAttackThoughRed) and-10 or 0
			Cost += TimesPositionReached*-0.1
			Cost += IsOutsideSquare and -1 or 0
			Cost += (Enemy and not IsDangerous) and 2 or 0 
			Cost += CanAttackThoughRed and 2 or 0
			Cost += IsDirectionOfCrate and 4 or 0
			Cost += HasLoot and 4 or 0
			
			
			if Depth>0 then
				Cost += Self.GetMoveInfo(Self.UpdateEnemies(EnemyTable), Depth-1, PlayerPos + v, Directions).Cost
			end
		
			local Data = {}
			Data.Direction = v
			Data.Cost = Cost
			table.insert(Spots, Data)
		end
		
		
		local BestSpot = Self.GetBestCostingSpot(Spots)
	
		table.insert(ExploredPositions, PlayerPos)
		
	
		
		return BestSpot
	end
	
	
	Self.MovePlayer = function()
		local PlayerPos = game.Players.LocalPlayer.Character.PrimaryPart.Position
		PlayerPos = Vector3.new(math.floor(PlayerPos.X + 0.5), 0, math.floor(PlayerPos.Z + 0.5))
		Self.GetOutsideSquares()
		local MoveInfo = Self.GetMoveInfo(Self.GetEnemies(), 0, PlayerPos)
		game.ReplicatedStorage.Events.Move:FireServer(MoveInfo.Direction.Unit)

	end
	
	
	return Self
end
