----------------
-- Craftitems --
----------------

local random = math.random

local walkable_nodes = {}

local color = minetest.colorize

minetest.register_on_mods_loaded(function()
	for name in pairs(minetest.registered_nodes) do
		if name ~= "air" and name ~= "ignore" then
			if minetest.registered_nodes[name].walkable then
				table.insert(walkable_nodes, name)
			end
		end
	end
end)

local function correct_name(str)
	if str then
		if str:match(":") then str = str:split(":")[2] end
		return (string.gsub(" " .. str, "%W%l", string.upper):sub(2):gsub("_", " "))
	end
end

local function register_egg(name, def)

	minetest.register_entity(def.mob .. "_egg_sprite", {
		hp_max = 1,
		physical = true,
		collisionbox = {0, 0, 0, 0, 0, 0},
		visual = "sprite",
		visual_size = {x = 0.5, y = 0.5},
		textures = {"animalia_egg.png"},
		initial_sprite_basepos = {x = 0, y = 0},
		is_visible = true,
		on_step = function(self)
			local pos = self.object:get_pos()
			local objects = minetest.get_objects_inside_radius(pos, 1.5)
			local cube = minetest.find_nodes_in_area(
				vector.new(pos.x - 0.5, pos.y - 0.5, pos.z - 0.5),
				vector.new(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5),
				walkable_nodes)
			if #objects >= 2 then
				if objects[2]:get_armor_groups().fleshy then
					objects[2]:punch(self.object, 2.0, {full_punch_interval = 0.1, damage_groups = {fleshy = 1}}, nil)
				end
			end
			if #cube >= 1 then
				minetest.add_particlespawner({
					amount = 6,
					time = 0.25,
					minpos = {x = pos.x - 7/16, y = pos.y - 5/16, z = pos.z - 7/16},
					maxpos = {x = pos.x + 7/16, y = pos.y - 5/16, z = pos.z + 7/16},
					minvel = vector.new(-1, 2, -1),
					maxvel = vector.new(1, 5, 1),
					minacc = vector.new(0, -9.81, 0),
					maxacc = vector.new(0, -9.81, 0),
					collisiondetection = true,
					texture = "animalia_egg_fragment.png",
				})
				if random(1, 3) < 2 then
					local object = minetest.add_entity(pos, def.mob)
					local ent = object:get_luaentity()
					ent.growth_scale = 0.7
					animalia.initialize_api(ent)
					animalia.protect_from_despawn(ent)
					self.object:remove()
				else
					self.object:remove()
				end
			end
		end
	})

	local function mobs_shoot_egg(item, player)
		local pos = player:get_pos()

		minetest.sound_play("default_place_node_hard", {
			pos = pos,
			gain = 1.0,
			max_hear_distance = 5,
		})

		local vel = 19
		local gravity = 9

		local obj = minetest.add_entity({
			x = pos.x,
			y = pos.y +1.5,
			z = pos.z
		}, def.mob .. "_egg_sprite")

		local ent = obj:get_luaentity()
		local dir = player:get_look_dir()

		ent.velocity = vel -- needed for api internal timing
		ent.switch = 1 -- needed so that egg doesn't despawn straight away

		obj:set_velocity({
			x = dir.x * vel,
			y = dir.y * vel,
			z = dir.z * vel
		})

		obj:set_acceleration({
			x = dir.x * -3,
			y = -gravity,
			z = dir.z * -3
		})

		-- pass player name to egg for chick ownership
		local ent2 = obj:get_luaentity()
		ent2.playername = player:get_player_name()

		item:take_item()

		return item
	end

	minetest.register_craftitem(name, {
		description = def.description,
		inventory_image = def.inventory_image .. ".png",
		on_use = mobs_shoot_egg,
		groups = {food_egg = 1, flammable = 2},
	})

	minetest.register_craftitem(name .. "_fried", {
		description = "Fried " .. def.description,
		inventory_image = def.inventory_image .. "_fried.png",
		on_use = minetest.item_eat(4),
		groups = {food_egg = 1, flammable = 2},
	})

	minetest.register_craft({
		type  =  "cooking",
		recipe  = name,
		output = name .. "_fried",
	})
end

-----------
-- Drops --
-----------

minetest.register_craftitem("animalia:leather", {
	description = "Leather",
	inventory_image = "animalia_leather.png",
	groups = {flammable = 2, leather = 1},
})

minetest.register_craftitem("animalia:feather", {
	description = "Feather",
	inventory_image = "animalia_feather.png",
	groups = {flammable = 2, feather = 1},
})

-- Meat --

minetest.register_craftitem("animalia:beef_raw", {
	description = "Raw Beef",
	inventory_image = "animalia_beef_raw.png",
	on_use = minetest.item_eat(1),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craftitem("animalia:beef_cooked", {
	description = "Steak",
	inventory_image = "animalia_beef_cooked.png",
	on_use = minetest.item_eat(8),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craft({
	type  =  "cooking",
	recipe  = "animalia:beef_raw",
	output = "animalia:beef_cooked",
})

minetest.register_craftitem("animalia:mutton_raw", {
	description = "Raw Mutton",
	inventory_image = "animalia_mutton_raw.png",
	on_use = minetest.item_eat(1),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craftitem("animalia:mutton_cooked", {
	description = "Cooked Mutton",
	inventory_image = "animalia_mutton_cooked.png",
	on_use = minetest.item_eat(6),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craft({
	type  =  "cooking",
	recipe  = "animalia:mutton_raw",
	output = "animalia:mutton_cooked",
})

minetest.register_craftitem("animalia:porkchop_raw", {
	description = "Raw Porkchop",
	inventory_image = "animalia_porkchop_raw.png",
	on_use = minetest.item_eat(1),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craftitem("animalia:porkchop_cooked", {
	description = "Cooked Porkchop",
	inventory_image = "animalia_porkchop_cooked.png",
	on_use = minetest.item_eat(7),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craft({
	type  =  "cooking",
	recipe  = "animalia:porkchop_raw",
	output = "animalia:porkchop_cooked",
})

minetest.register_craftitem("animalia:poultry_raw", {
	description = "Raw Poultry",
	inventory_image = "animalia_poultry_raw.png",
	on_use = minetest.item_eat(1),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craftitem("animalia:poultry_cooked", {
	description = "Cooked Poultry",
	inventory_image = "animalia_poultry_cooked.png",
	on_use = minetest.item_eat(6),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craft({
	type  =  "cooking",
	recipe  = "animalia:poultry_raw",
	output = "animalia:poultry_cooked",
})

minetest.register_craftitem("animalia:venison_raw", {
	description = "Raw Venison",
	inventory_image = "animalia_venison_raw.png",
	on_use = minetest.item_eat(1),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craftitem("animalia:venison_raw_cooked", {
	description = "Venison Steak",
	inventory_image = "animalia_venison_cooked.png",
	on_use = minetest.item_eat(10),
	groups = {flammable = 2, meat = 1, food_meat = 1},
})

minetest.register_craft({
	type  =  "cooking",
	recipe  = "animalia:venison_raw",
	output = "animalia:venison_cooked",
})

register_egg("animalia:chicken_egg", {
	description = "Chicken Egg",
	inventory_image = "animalia_egg",
	mob = "animalia:chicken"
})

register_egg("animalia:turkey_egg", {
	description = "Turkey Egg",
	inventory_image = "animalia_egg",
	mob = "animalia:turkey"
})

----------
-- Misc --
----------

minetest.register_craftitem("animalia:bucket_milk", {
	description = "Bucket of Milk",
	inventory_image = "animalia_milk_bucket.png",
	stack_max = 1,
	on_use = minetest.item_eat(8, "bucket:bucket_empty"),
	groups = {food_milk = 1, flammable = 3},
})

local function grow_crops(pos, nodename)
	local checkname = nodename:sub(1, string.len(nodename) - 1)
	if minetest.registered_nodes[checkname .. "1"]
	and minetest.registered_nodes[checkname .. "2"]
	and minetest.registered_nodes[checkname .. "2"].drawtype == "plantlike" then -- node is more than likely a plant
		local stage = tonumber(string.sub(nodename, -1)) or 0
		local newname = checkname .. (stage + 1)
		if minetest.registered_nodes[newname] then
			local def = minetest.registered_nodes[newname]
			def = def and def.place_param2 or 0
			minetest.set_node(pos, {name = newname, param2 = def})
			minetest.add_particlespawner({
				amount = 6,
				time = 0.1,
				minpos = vector.subtract(pos, 0.5),
				maxpos = vector.add(pos, 0.5),
				minvel = {
					x = -0.5,
					y = 0.5,
					z = -0.5
				},
				maxvel = {
					x = 0.5,
					y = 1,
					z = 0.5
				},
				minacc = {
					x = 0,
					y = 2,
					z = 0
				},
				maxacc = {
					x = 0,
					y = 4,
					z = 0
				},
				minexptime = 0.5,
				maxexptime = 1,
				minsize = 1,
				maxsize = 2,
				collisiondetection = false,
				vertical = false,
				use_texture_alpha = true,
				texture = "creatura_particle_green.png",
				glow = 6
			})
		end
	end
end

local guano_fert = minetest.settings:get_bool("guano_fertilization")

minetest.register_craftitem("animalia:bucket_guano", {
	description = "Bucket of Guano",
	inventory_image = "animalia_guano_bucket.png",
	stack_max = 1,
	groups = {flammable = 3},
	on_place = function(itemstack, placer, pointed_thing)
		local pos = pointed_thing.above
		if pos then
			local under = minetest.get_node(pointed_thing.under)
			local node = minetest.registered_nodes[under.name]
			if node and node.on_rightclick then
				return node.on_rightclick(pointed_thing.under, under, placer,
										  itemstack)
			end
			if pos
			and not minetest.is_protected(pos, placer:get_player_name()) then
				if guano_fert then
					local nodes = minetest.find_nodes_in_area_under_air(
						vector.subtract(pos, 5),
						vector.add(pos, 5),
						{"group:grass", "group:plant", "group:flora"}
					)
					if #nodes > 0 then
						for n = 1, #nodes do
							grow_crops(nodes[n], minetest.get_node(nodes[n]).name)
						end
						local replace = itemstack:get_meta():get_string("original_item")
						if not replace
						or replace == "" then
							replace = "bucket:bucket_empty"
						end
						itemstack:set_name(replace)
					end
				else
					minetest.set_node(pos, {name = "animalia:guano"})
					local replace = itemstack:get_meta():get_string("original_item")
					if not replace
					or replace == "" then
						replace = "bucket:bucket_empty"
					end
					itemstack:set_name(replace)
				end
			end
		end
		return itemstack
	end
})

minetest.register_node("animalia:nest_song_bird", {
	description = "Song Bird Nest",
	paramtype = "light",
	drawtype = "mesh",
	mesh = "animalia_nest.obj",
	tiles = {"animalia_nest.png"},
	sunlight_propagates = true,
	stack_max = 1,
	groups = {snappy = 3, flammable = 3},
	selection_box = {
		type = "fixed",
		fixed = {-5 / 16, -0.5, -5 / 16, 5 / 16, -0.31, 5 / 16},
	},
	node_box = {
		type = "fixed",
		fixed = {-5 / 16, -0.5, -5 / 16, 5 / 16, -0.31, 5 / 16},
	},
	drops = "default:stick"
})

-----------
-- Tools --
-----------

minetest.register_craftitem("animalia:cat_toy", {
	description = "Cat Toy",
	inventory_image = "animalia_cat_toy.png",
	wield_image = "animalia_cat_toy.png^[transformFYR90",
	stack_max = 1
})

local nametag = {}

local function get_rename_formspec(meta)
	local tag = meta:get_string("name") or ""
	local form = {
		"size[8,4]",
		"field[0.5,1;7.5,0;name;" .. minetest.formspec_escape("Enter name:") .. ";" .. tag .. "]",
		"button_exit[2.5,3.5;3,1;set_name;" .. minetest.formspec_escape("Set Name") .. "]"
	}
	return table.concat(form, "")
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if formname == "animalia:set_name" and fields.name then
		local name = player:get_player_name()
		if not nametag[name] then
			return
		end
		local itemstack = nametag[name]
		if string.len(fields.name) > 64 then
			fields.name = string.sub(fields.name, 1, 64)
		end
		local meta = itemstack:get_meta()
		meta:set_string("name", fields.name)
		meta:set_string("description", fields.name)
		player:set_wielded_item(itemstack)
		if fields.quit or fields.key_enter then
			nametag[name] = nil
		end
	end
end)

local function nametag_rightclick(itemstack, player, pointed_thing)
	if pointed_thing
	and pointed_thing.type == "object" then
		return
	end
	local name = player:get_player_name()
	nametag[name] = itemstack
	local meta = itemstack:get_meta()
	minetest.show_formspec(name, "animalia:set_name", get_rename_formspec(meta))
end

minetest.register_craftitem("animalia:nametag", {
	description = "Nametag",
	inventory_image = "animalia_nametag.png",
	on_rightclick = nametag_rightclick,
	on_secondary_use = nametag_rightclick
})

minetest.register_craftitem("animalia:saddle", {
	description = "Saddle",
	inventory_image = "animalia_saddle.png",
})

minetest.register_tool("animalia:shears", {
	description = "Shears",
	inventory_image = "animalia_shears.png",
	groups = {flammable = 2}
})

minetest.register_craftitem("animalia:net", {
	description = "Animal Net",
	inventory_image = "animalia_net.png",
	stack_max = 1,
	on_secondary_use = function(itemstack, placer, pointed_thing)
		if pointed_thing.type == "object" then
			if pointed_thing.ref:is_player() then return end
			local ent = pointed_thing.ref:get_luaentity()
			if not ent.name:match("^animalia:") or not ent.catch_with_net then
				return
			end
			local ent_name = correct_name(ent.name)
			local ent_gender = correct_name(ent.gender)
			local meta = itemstack:get_meta()
			if not meta:get_string("mob") or meta:get_string("mob") == "" then
				if placer:get_wielded_item():get_count() > 1 then
					if placer:get_inventory():room_for_item("main", {name = "animalia:net"}) then
						itemstack:take_item(1)
						placer:get_inventory():add_item("main", "animalia:net")
						return itemstack
					else
						return
					end
				end
				meta:set_string("mob", ent.name)
				meta:set_string("staticdata", ent:get_staticdata())
				local desc = "Animal Net \n" .. color("#a9a9a9", ent_name) .. "\n" .. color("#a9a9a9", ent_gender)
				if ent.name == "animalia:cat"
				and ent.trust
				and ent.trust[placer:get_player_name()] then
					desc = desc .. "\n Trust: " .. color("#a9a9a9", ent.trust[placer:get_player_name()])
				end
				meta:set_string("description", desc)
				placer:set_wielded_item(itemstack)
				animalia.protect_from_despawn(ent)
				ent.object:remove()
				return itemstack
			else
				minetest.chat_send_player(placer:get_player_name(),
										  "This Net already contains a " ..
											  correct_name(
												  meta:get_string("mob")))
				return
			end
		end
	end,
	on_place = function(itemstack, placer, pointed_thing)
		local pos = pointed_thing.above
		if pos then
			local under = minetest.get_node(pointed_thing.under)
			local node = minetest.registered_nodes[under.name]
			if node and node.on_rightclick then
				return node.on_rightclick(pointed_thing.under, under, placer,
										  itemstack)
			end
			if pos and not minetest.is_protected(pos, placer:get_player_name()) then
				local mob = itemstack:get_meta():get_string("mob")
				local staticdata = itemstack:get_meta():get_string("staticdata")
				if mob ~= "" then
					pos.y = pos.y +
								math.abs(
									minetest.registered_entities[mob]
										.collisionbox[2])
					minetest.add_entity(pos, mob, staticdata)
					itemstack:get_meta():set_string("mob", nil)
					itemstack:get_meta():set_string("staticdata", nil)
					itemstack:get_meta():set_string("description", "Animal Net")
				end
			end
		end
		return itemstack
	end
})

-----------
-- Nodes --
-----------

minetest.register_node("animalia:guano", {
	description = "Guano",
	tiles = {"animalia_guano.png"},
	paramtype = "light",
	buildable_to = true,
	floodable = true,
	walkable = false,
	drawtype = "nodebox",
	node_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.5, 0.5, -0.25, 0.5},
		},
	},
	groups = {crumbly = 3, falling_node = 1, not_in_creative_inventory = 1},
	on_punch = function(pos, _, player)
		local item_name = player:get_wielded_item():get_name()
		if item_name:find("bucket")
		and item_name:find("empty") then
			local stack = ItemStack("animalia:bucket_guano")
			stack:get_meta():set_string("original_item", item_name)
			player:set_wielded_item(stack)
			minetest.remove_node(pos)
		end
	end
})

-----------
-- Libri --
-----------

animalia.libri_pages = {}

function animalia.show_libri_main_form(player, pages, group)
	group = group or 1
	local basic_form = table.concat({
		"formspec_version[3]",
		"size[16,10]",
		"background[-0.7,-0.5;17.5,11.5;animalia_libri_bg.png]"
	}, "")
	if group == 1 then
		if pages[1] then
			basic_form = basic_form .. "button[1.75,1.5;4,1;".. pages[1].form .."]"
		end
		if pages[2] then
			basic_form = basic_form .. "button[1.75,3.5;4,1;".. pages[2].form .."]"
		end
		if pages[3] then
			basic_form = basic_form .. "button[1.75,5.5;4,1;".. pages[3].form .."]"
		end
		if pages[4] then
			basic_form = basic_form .. "button[1.75,7.5;4,1;".. pages[4].form .."]"
		end
		if pages[5] then
			basic_form = basic_form .. "button[10.25,1.5;4,1;".. pages[5].form .."]"
		end
		if pages[6] then
			basic_form = basic_form .. "button[10.25,3.5;4,1;".. pages[6].form .."]"
		end
		if pages[7] then
			basic_form = basic_form .. "button[10.25,5.5;4,1;".. pages[7].form .."]"
		end
		if pages[8] then
			basic_form = basic_form .. "button[10.25,7.5;4,1;".. pages[8].form .."]"
		end
		if pages[9] then
			basic_form = basic_form .. "button[12.25,9;1.5,1;btn_next;Next Page]"
		end
	elseif group == 2 then
		if pages[9] then
			basic_form = basic_form .. "button[1.75,1.5;4,1;".. pages[9].form .."]"
		end
		if pages[10] then
			basic_form = basic_form .. "button[1.75,3.5;4,1;".. pages[10].form .."]"
		end
		if pages[11] then
			basic_form = basic_form .. "button[1.75,5.5;4,1;".. pages[11].form .."]"
		end
		if pages[12] then
			basic_form = basic_form .. "button[1.75,7.5;4,1;".. pages[12].form .."]"
		end
		if pages[13] then
			basic_form = basic_form .. "button[10.25,1.5;4,1;".. pages[13].form .."]"
		end
		if pages[14] then
			basic_form = basic_form .. "button[10.25,3.5;4,1;".. pages[14].form .."]"
		end
		if pages[15] then
			basic_form = basic_form .. "button[10.25,5.5;4,1;".. pages[15].form .."]"
		end
		if pages[16] then
			basic_form = basic_form .. "button[10.25,7.5;4,1;".. pages[16].form .."]"
		end
	end
	animalia.libri_pages[player:get_player_name()] = pages
	minetest.show_formspec(player:get_player_name(), "animalia:libri_main", basic_form)
end

minetest.register_craftitem("animalia:libri_animalia", {
	description = "Libri Animalia",
	inventory_image = "animalia_libri_animalia.png",
	stack_max = 1,
	on_place = function(itemstack, player, pointed_thing)
		if pointed_thing and pointed_thing.type == "object" then return end
		local meta = itemstack:get_meta()
		local pages = minetest.deserialize(meta:get_string("pages"))
		if not pages
		or #pages < 1 then return end
		animalia.show_libri_main_form(player, pages)
	end,
	on_secondary_use = function(itemstack, player, pointed_thing)
		if pointed_thing and pointed_thing.type == "object" then return end
		local meta = itemstack:get_meta()
		local pages = minetest.deserialize(meta:get_string("pages"))
		if not pages
		or #pages < 1 then return end
		animalia.show_libri_main_form(player, pages)
	end
})

--------------
-- Crafting --
--------------

minetest.register_on_mods_loaded(function()
	for name, def in pairs(minetest.registered_items) do
		if string.find(name, "ingot")
		and string.find(name, "steel") then
			if not def.groups then
				def.groups = {}
			end
			def.groups["steel_ingot"] = 1
			minetest.register_item(":" .. name, def)
		elseif string.find(name, "string") then
			if not def.groups then
				def.groups = {}
			end
			def.groups["string"] = 1
			minetest.register_item(":" .. name, def)
		end
	end
end)

minetest.register_craft({
	output = "animalia:cat_toy",
	recipe = {
		{"", "", "group:string"},
		{"", "group:stick", "group:string"},
		{"group:stick", "", "group:feather"}
	}
})

minetest.register_craft({
	output = "animalia:lasso",
	recipe = {
		{"", "group:string", "group:string"},
		{"", "group:leather", "group:string"},
		{"group:string", "", ""}
	}
})

minetest.register_craft({
	output = "animalia:net",
	recipe = {
		{"group:string", "", "group:string"},
		{"group:string", "", "group:string"},
		{"group:stick", "group:string", ""}
	}
})


minetest.register_craft({
	output = "animalia:saddle",
	recipe = {
		{"group:leather", "group:leather", "group:leather"},
		{"group:leather", "group:steel_ingot", "group:leather"},
		{"group:string", "", "group:string"}
	}
})

minetest.register_craft({
	output = "animalia:shears",
	recipe = {
		{"", "group:steel_ingot", ""},
		{"", "group:leather", "group:steel_ingot"}
	}
})

minetest.register_craft({
	output = "animalia:libri_animalia",
	recipe = {
		{"", "", ""},
		{"animalia:feather", "", ""},
		{"group:book", "group:color_green", ""}
	}
})

minetest.register_craft({
	output = "animalia:libri_animalia",
	recipe = {
		{"", "", ""},
		{"animalia:feather", "", ""},
		{"group:book", "group:unicolor_green", ""}
	}
})

minetest.register_craft({
	output = "animalia:libri_animalia 2",
	recipe = {
		{"", "", ""},
		{"animalia:libri_animalia", "group:book", ""},
		{"", "", ""}
	}
})

minetest.register_on_craft(function(itemstack, _, old_craft_grid)
	if itemstack:get_name() == "animalia:libri_animalia"
	and itemstack:get_count() > 1 then
		for _, old_libri in pairs(old_craft_grid) do
			if old_libri:get_meta():get_string("pages") then
				local pages = old_libri:get_meta():get_string("pages")
				itemstack:get_meta():set_string("pages", pages)
				return itemstack
			end
		end
	end
end)