local S = minetest.get_translator("rp_mobs_mobs")

local mod_nav = minetest.get_modpath("rp_nav")

local top_grass = {
	["rp_default:grass"] = "air",
	["rp_default:tall_grass"] = "rp_default:grass",
	["rp_default:swamp_grass"] = "air",
	["rp_default:dry_grass"] = "air",
}
local dirt_cover = {
	["rp_default:dirt_with_swamp_grass"] = "rp_default:swamp_dirt",
	["rp_default:dirt_with_dry_grass"] = "rp_default:dirt",
	["rp_default:dirt_with_grass"] = "rp_default:dirt",
}

-- Time between random call sounds (ms)
local RANDOM_SOUND_TIMER_MIN = 10000
local RANDOM_SOUND_TIMER_MAX = 60000

-- Time to wait between eating grass (ms)
local EAT_GRASS_TIMER_MIN = 45000
local EAT_GRASS_TIMER_MAX = 75000

-- Mimimum time sheep has to stand still before eating (s)
local EAT_GRASS_STAND_TIMER = 0.7

-- Minimum time to wait before wool can regrow (s).
-- Timer starts after sheep is shorn.
local WOOL_REGROW_TIMER = 45.0

-- Wool drop count if shorn
local WOOL_SHEAR_MIN = 1
local WOOL_SHEAR_MAX = 3

-- Wool drop count if killed
local WOOL_DROP_MIN = 1
local WOOL_DROP_MAX = 1

-- Distance in which sheep can "see" player holding food
local VIEW_RANGE = 5

-- What the sheep eats
local FOOD = {{ name = "rp_farming:wheat", points = 1 }}

local task_queue_roam_settings = {
	walk_speed = 1,
	liquid_rise_speed = 2,
	jump_strength = 4.6,
	jump_clear_height = 1,
	fall_height = 4,
	max_fall_damage_add_percent_drop_on = 10,
	walk_duration_min = 2000,
	walk_duration_max = 6000,
	find_land_duration_min = 7000,
	find_land_duration_max = 10000,
	find_safe_land_duration_min = 1000,
	find_safe_land_duration_max = 1100,
	idle_duration_min = 2000,
	idle_duration_max = 4000,
	find_land_length = 20,
	follow_max_distance = VIEW_RANGE,
	follow_reach_distance = 2,
	follow_give_up_time = 10.0,
	no_follow_time = 6.0,
}

local microtask_eat_grass = function()
	return rp_mobs.create_microtask({
		label = "Eat blocks and regrow wool",
		on_start = function(self, mob)
			self.statedata.stand_timer = 0
			self.statedata.eat_grass_timer = 0
			self.statedata.eat_grass_timer_goal = math.random(EAT_GRASS_TIMER_MIN, EAT_GRASS_TIMER_MAX)/1000
		end,
		on_step = function(self, mob, dtime)
			self.statedata.eat_grass_timer = self.statedata.eat_grass_timer + dtime
			if not mob._custom_state.wool_regrow_timer then
				mob._custom_state.wool_regrow_timer = 0
			end
			if mob._custom_state.shorn then
				mob._custom_state.wool_regrow_timer = mob._custom_state.wool_regrow_timer + dtime
			end

			if self.statedata.eat_grass_timer < self.statedata.eat_grass_timer_goal then
				return
			end

			local mobpos = mob.object:get_pos()

			local plantpos = {x=mobpos.x, y=mobpos.y-1, z=mobpos.z}
			local groundpos = {x=mobpos.x, y=mobpos.y-2, z=mobpos.z}
			local np = minetest.get_node(plantpos)
			local ng = minetest.get_node(groundpos)

			local vel = mob.object:get_velocity()
			-- Don't eat while moving
			if vector.length(vel) > 0.5 then
				self.statedata.stand_timer = 0
				return
			else
				self.statedata.stand_timer = self.statedata.stand_timer + dtime
			end
			-- Needs to have stood for a minimum amount of time before eating
			if self.statedata.stand_timer <= EAT_GRASS_STAND_TIMER then
				return
			end

			local eaten = false
			-- Eat grass
			if np.name == "air" then
				-- Eat grass from dirt node
				if dirt_cover[ng.name] then
					minetest.set_node(groundpos, {name = dirt_cover[ng.name]})
					eaten = true
				end
			elseif top_grass[np.name] then
				-- If grass plant on top, eat it first
				minetest.set_node(plantpos, {name = top_grass[np.name]})
				eaten = true
			end

			if eaten then
				self.statedata.eat_grass_timer_goal = math.random(EAT_GRASS_TIMER_MIN, EAT_GRASS_TIMER_MAX)/1000
				self.statedata.eat_grass_timer = 0
			end

			-- Regrow wool
			if eaten and mob._custom_state.shorn and mob._custom_state.wool_regrow_timer >= WOOL_REGROW_TIMER then
				mob.object:set_properties(
				{
					textures = {"mobs_sheep.png"},
				})
				mob._custom_state.shorn = false
				mob._custom_state.wool_regrow_timer = 0
			end
		end,
		is_finished = function(self, mob)
			return self.statedata.eaten
		end,
	})
end

-- Decide when to eat grass
local eat_decider = function(task_queue, mob)
	local task = rp_mobs.create_task({label="eat grass"})
	local mtask = microtask_eat_grass()
	rp_mobs.add_microtask_to_task(mob, mtask, task)

	rp_mobs.add_task_to_task_queue(task_queue, task)
end

rp_mobs.register_mob("rp_mobs_mobs:sheep", {
	description = S("Sheep"),
	tags = { animal = 1, child_exists = 1, peaceful = 1 },
	drops = {
		{name="rp_mobs_mobs:meat_raw", chance=1, min=2, max=4},
	},
	drop_func = function(self)
		-- Drop wool if a non-shorn adult
		if (not self._child) and (self._custom_state and (not self._custom_state.shorn)) then
			local count = math.random(WOOL_DROP_MIN, WOOL_DROP_MAX)
			return { "rp_mobs_mobs:wool "..count }
		end
		return {}
	end,
	default_sounds = {
		death = "mobs_sheep",
		damage = "mobs_sheep",
		eat = "mobs_eat",
		call = "mobs_sheep",
		give_birth = "mobs_sheep",
		horny = "mobs_sheep",
	},
	animations = {
		["idle"] = { frame_range = { x = 0, y = 60 }, default_frame_speed = 15 },
		["dead_static"] = { frame_range = { x = 0, y = 0 } },
		["walk"] = { frame_range = { x = 61, y = 80 }, default_frame_speed = 15 },
		["run"] = { frame_range = { x = 61, y = 80 }, default_frame_speed = 25 },
	},
	front_body_point = vector.new(0, -0.4, 0.6),
	dead_y_offset = 0.6,
	entity_definition = {
		initial_properties = {
			hp_max = 7,
			physical = true,
			collisionbox = {-0.49, -1, -0.49, 0.49, 0.1, 0.49},
			selectionbox = {-0.4, -1, -0.6, 0.4, 0.1, 0.7, rotate = true},
			visual = "mesh",
			mesh = "mobs_sheep.x",
			textures = { "mobs_sheep.png" },
			makes_footstep_sound = true,
			stepheight = 0.26,
		},
		on_activate = function(self, staticdata)
			rp_mobs.init_mob(self)
			rp_mobs.restore_state(self, staticdata)
			if self._custom_state.shorn then
				self.object:set_properties({
					-- NOTE: If this is updated, rp_mobs_legacy must be updated as well!
					textures = {"mobs_sheep_shaved.png"},
				})
			end

			rp_mobs.init_fall_damage(self, true)
			rp_mobs.init_breath(self, true, {
				breath_max = 10,
				drowning_point = vector.new(0, -0.1, 0.49)
			})
			rp_mobs.init_node_damage(self, true, {
				node_damage_points={vector.new(0, -0.3, 0)},
			})

			self.object:set_acceleration(rp_mobs.GRAVITY_VECTOR)
			rp_mobs.init_tasks(self)
			rp_mobs.add_task_queue(self, rp_mobs_mobs.task_queues.land_roam(task_queue_roam_settings))
			rp_mobs.add_task_queue(self, rp_mobs_mobs.task_queues.food_breed_follow_scan(VIEW_RANGE, FOOD))
			rp_mobs.add_task_queue(self, rp_mobs.create_task_queue(eat_decider))
			rp_mobs.add_task_queue(self, rp_mobs_mobs.task_queues.call_sound(RANDOM_SOUND_TIMER_MIN, RANDOM_SOUND_TIMER_MAX))

		end,
		get_staticdata = rp_mobs.get_staticdata_default,
		on_step = function(self, dtime, moveresult)
			rp_mobs.handle_dying(self, dtime, moveresult, rp_mobs_mobs.get_dying_step(true, false))
			rp_mobs.scan_environment(self, dtime, -0.3)
			rp_mobs.handle_environment_damage(self, dtime, moveresult)
			rp_mobs.handle_tasks(self, dtime, moveresult)
			rp_mobs.advance_child_growth(self, dtime)
			rp_mobs.handle_breeding(self, dtime)
		end,
		_on_capture = function(self, capturer)
			rp_mobs.attempt_capture(self, capturer, { ["rp_mobs:net"] = 5, ["rp_mobs:lasso"] = 60 })
		end,
		on_rightclick = function(self, clicker)
			local item = clicker:get_wielded_item()
			local itemname = item:get_name()

			-- Demagnetize magnocompass if sheep has wool
			if mod_nav then
				local compass_group = minetest.get_item_group(itemname, "nav_compass")
				if compass_group > 0 then
					if compass_group == 2 and (not self._custom_state.shorn) then
						item = nav.demagnetize_compass(item, clicker:get_pos())
						clicker:set_wielded_item(item)
						return
					end
					return
				end
			end

			-- Are we feeding?
			if rp_mobs.feed_tame_breed(self, clicker, FOOD, 8, 8) then
				-- Update wool status if shorn
				if not self._custom_state.shorn then
					self.object:set_properties({
						textures = {"mobs_sheep.png"},
					})
				end
				return
			end

			-- Are we shearing wool?
			if minetest.get_item_group(itemname, "shears") > 0 then
				if (not self._custom_state.shorn) and (not self._child) then
					self._custom_state.shorn = true
					local pos = self.object:get_pos()
					pos.y = pos.y + 0.5
					local count = math.random(WOOL_SHEAR_MIN, WOOL_SHEAR_MAX)
					local obj = rp_mobs.spawn_mob_drop(pos, ItemStack("mobs:wool "..count))
					minetest.sound_play({name = "default_shears_cut", gain = 0.5}, {pos = clicker:get_pos(), max_hear_distance = 8}, true)
					if not minetest.is_creative_enabled(clicker:get_player_name()) then
						local def = item:get_definition()
						local cuts = minetest.get_item_group(itemname, "sheep_cuts")
						if cuts > 0 then
							item:add_wear_by_uses(cuts)
						else
							if def and def.tool_capabilities and def.tool_capabilities.snappy then
								item:add_wear_by_uses(def.tool_capabilities.snappy.uses)
							end
						end
					end
					clicker:set_wielded_item(item)
					self.object:set_properties({
						-- NOTE: If this is updated, rp_mobs_legacy must be updated as well!
						textures = {"mobs_sheep_shaved.png"},
					})
					self._custom_state.wool_regrow_timer = 0
					achievements.trigger_achievement(clicker, "shear_time")
				end
				return
			end

			-- Are we capturing?
			rp_mobs.call_on_capture(self, clicker)
		end,

		on_death = rp_mobs.on_death_default,
		on_punch = rp_mobs.on_punch_default,
	},
})

rp_mobs.register_mob_item("rp_mobs_mobs:sheep", "mobs_sheep_inventory.png", nil, function(mob, itemstack)
	if mob._custom_state.shorn then
		local meta = itemstack:get_meta()
		meta:set_string("inventory_image", "mobs_sheep_shaved_inventory.png")
		meta:set_string("wield_image", "mobs_sheep_shaved_inventory.png")
	end
	return itemstack
end)
