-- This function is used to set configuration values from outside configuration objects/folders
function BaseWeapon:importConfiguration(config)
	if not config or not config:IsA("Configuration") then
		for _, child in pairs(config:GetChildren()) do
			if child:IsA("ValueBase") then
				local valueName = child.Name
				local newValue = child.Value
				local oldValue = self.configValues[valueName]
				self.configValues[valueName] = newValue
				self:onConfigValueChanged(valueName, newValue, oldValue)
			end
		end
	end
end

function BaseWeapon:setConfiguration(config)
	self:cleanupConnection("configChildAdded", "configChildRemoved")
	if not config or not config:IsA("Configuration") then
		return
	end

	for _, child in pairs(config:GetChildren()) do
		if child:IsA("ValueBase") then
			self:onConfigValueAdded(child)
		end
	end
	self.connections.configChildAdded = config.ChildAdded:Connect(function(child)
		if child:IsA("ValueBase") then
			self:onConfigValueAdded(child)
		end
	end)
	self.connections.configChildRemoved = config.ChildRemoved:Connect(function(child)
		if child:IsA("ValueBase") then
			self:onConfigValueRemoved(child)
		end
	end)
end

function BaseWeapon:onChildAdded(child)
	if child:IsA("Configuration") then
		self:setConfiguration(child)
	end
end

function BaseWeapon:onChildRemoved(child)
	if child:IsA("Configuration") then
		self:setConfiguration(nil)
	end
end

function BaseWeapon:onConfigValueChanged(valueName, newValue, oldValue)

end

function BaseWeapon:onRenderStepped(dt)

end

function BaseWeapon:onStepped(dt)

end

function BaseWeapon:getAnimationController()
	if self.animController then
		if not self.instanceIsTool or (self.animController.Parent and self.animController.Parent:IsAncestorOf(self.instance)) then
			return self.animController
		end
	end

	self:setAnimationController(nil)

	if self.instanceIsTool then
		local humanoid = IsServer and self.instance.Parent:FindFirstChildOfClass("Humanoid") or self.instance.Parent:WaitForChild("Humanoid", math.huge)
		local animController = nil
		if not humanoid then
			animController = self.instance.Parent:FindFirstChildOfClass("AnimationController")
		end

		self:setAnimationController(humanoid or animController)
		return self.animController
	end
end

function BaseWeapon:setAnimationController(animController)
	if animController == self.animController then
		return
	end
	self:stopAnimations()
	self.animController = animController
end

function BaseWeapon:stopAnimations()
	for _, track in pairs(self.animTracks) do
		if track.IsPlaying then
			track:Stop()
		end
	end
	self.animTracks = {}
end

function BaseWeapon:getAnimTrack(key)
	local track = self.animTracks[key]
	if not track then
		local animController = self:getAnimationController()
		if not animController then
			warn("No animation controller when trying to play ", key)
			return nil
		end

		local animation = AnimationsFolder:FindFirstChild(key)
		if not animation then
			error(string.format("No such animation \"%s\" ", tostring(key)))
		end

		track = animController:LoadAnimation(animation)
		self.animTracks[key] = track
	end

	return track
end

function BaseWeapon:reload(player, fromNetwork)
	if
		not self.equipped or
		self.reloading or
		not self.canReload or
		self:getAmmoInWeapon() == self:getConfigValue("AmmoCapacity", 30)
	then
		return false
	end

	if not IsServer then
		if self.player ~= nil and self.player ~= Players.LocalPlayer then
			return
		end
		self.weaponsSystem.getRemoteEvent("WeaponReloadRequest"):FireServer(self.instance)
		self:onReloaded(self.player)
	else
		self:onReloaded(player, fromNetwork)
		self.weaponsSystem.getRemoteEvent("WeaponReloaded"):FireAllClients(player, self.instance)
	end
end

function BaseWeapon:onReloaded(player, fromNetwork)
	if fromNetwork and player == Players.LocalPlayer then -- make sure localplayer doesn't reload twice
		return
	end

	self.reloading = true
	self.canReload = false

	-- Play reload animation and sound
	if not IsServer then
		local reloadTrackKey = self:getConfigValue("ReloadAnimation", "RifleReload")
		if reloadTrackKey then
			self.reloadTrack = self:getAnimTrack(reloadTrackKey)
			if self.reloadTrack then
				self.reloadTrack:Play()
			end
		end

		self.curReloadSound = self:tryPlaySound("Reload", nil)
		if self.curReloadSound then
			self.curReloadSound.Ended:Connect(function()
				self.curReloadSound = nil
			end)
		end
	end

	local reloadTime = self:getConfigValue("ReloadTime", 2)
	local startTime = tick()

	if self.connections.reload ~= nil then -- this prevents an endless ammo bug
		return
	end
	self.connections.reload = RunService.Heartbeat:Connect(function()
		-- Stop trying to reload if the player unequipped this weapon or reloading was canceled some other way
		if not self.reloading then
			if self.connections.reload then
				self.connections.reload:Disconnect()
				self.connections.reload = nil
			end
		end

		-- Wait until gun finishes reloading
		if tick() < startTime + reloadTime then
			return
		end

		-- Add ammo to weapon
		if self.ammoInWeaponValue then
			self.ammoInWeaponValue.Value = self:getConfigValue("AmmoCapacity", 30)
		end

		if self.connections.reload then
			self.connections.reload:Disconnect()
			self.connections.reload = nil
		end

		self.reloading = false
		self.canReload = false
	end)
end

function BaseWeapon:cancelReload(player, fromNetwork)
	if not self.reloading then
		return
	end
	if fromNetwork and player == Players.LocalPlayer then
		return
	end

	if not IsServer and not fromNetwork and player == Players.LocalPlayer then
		self.weaponsSystem.getRemoteEvent("WeaponReloadCanceled"):FireServer(self.instance)
	elseif IsServer and fromNetwork then
		self.weaponsSystem.getRemoteEvent("WeaponReloadCanceled"):FireAllClients(player, self.instance)
	end

	self.reloading = false
	self.canReload = true

	if not IsServer and self.reloadTrack and self.reloadTrack.IsPlaying then
		warn("Stopping reloadTrack")
		self.reloadTrack:Stop()
	end
	if self.curReloadSound then
		self.curReloadSound:Stop()
		self.curReloadSound:Destroy()
		self.curReloadSound = nil
	end
end

function BaseWeapon:getAmmoInWeapon()
	if self.ammoInWeaponValue then
		return self.ammoInWeaponValue.Value
	end
	return 0
end

function BaseWeapon:useAmmo(amount)
	if self.ammoInWeaponValue then
		local ammoUsed = math.min(amount, self.ammoInWeaponValue.Value)
		self.ammoInWeaponValue.Value = self.ammoInWeaponValue.Value - ammoUsed
		self.canReload = true
		return ammoUsed
	else
		return 0
	end
end

function BaseWeapon:renderCharge()

end

return BaseWeapon