-- Aberrant trees
-- By Astrobe, based on:

-- naturally growing trees
-- rnd, 2015

-- Licence is LGPL.

-- version 0.1

-- See a_tree.register() at the bottom for possible parameters and their default values.

local random=math.random

local function set_node(pos, node)
	if minetest.get_node(pos).name == "air" then
		minetest.set_node(pos, node)
	end
end

local trees= { }

local function treegen(pos)
	local meta = minetest.get_meta(pos)
	local life = meta:get_int("life")
	local branch = meta:get_int("branch")
	local tree_type=meta:get_int("treetype")
	local tree=trees[tree_type]
	minetest.set_node(pos, {name = tree.trunk});

	-- LEAVES 
	if life<=0 or (life<tree.size-tree.height and random(5)==1)  then
		-- either end of growth or above trunk randomly
		local r=random(2);
		if life <=0 then
			r = r+1; -- determine leaves region size
		end

		local i,j,k
		for i=-r,r do
			for j=-r,r do
				for k = -r,r do
					local p = {x=pos.x+i,y=pos.y+j,z=pos.z+k};
					if random(3)==1 then
						set_node(p,{name=tree.leaves});
					elseif random(tree.fruity)==1 then
						set_node(p,{name=tree.fruit})
					end
				end
			end
		end				
	end
	if life<=0 then return end -- stop growth

	local above  = {x=pos.x,y=pos.y+1,z=pos.z};
	local nodename = minetest.get_node(above).name

	-- GROWTH
	if nodename == "air" or nodename == tree.leaves or nodename== tree.fruit then -- can we grow up
		if random(tree.slanty)==1 then -- occasionaly change direction of growth a little
			above.x=above.x+random(3)-2;
			above.z=above.z+random(3)-2;
		end

		-- BRANCHING
		if (random(tree.branchy)==1 or branch == 0) and life<tree.size-tree.height then -- not yet in branch
			local dir = {x=random(5)-3,y=random(2)-1,z=random(5)-3};
			--if math.random(2)==1 then dir.y=(math.random(3)-2) end -- occassionaly branch nonhorizontaly 
			local dirlen = math.sqrt(dir.x*dir.x+dir.y*dir.y+dir.z*dir.z);
			if dirlen == 0 then dirlen = 1 end;
			dir.x=dir.x/dirlen; dir.y=dir.y/dirlen; dir.z=dir.z/dirlen; -- normalize

			local length = random(math.pow(life/tree.size,1.5)*tree.width)+1; -- length of branch
			for i=1,length-1 do
				local p = {x=above.x+dir.x*i,y=above.y+dir.y*i,z=above.z+dir.z*i};
				if minetest.get_node(p).name == tree.leaves then minetest.remove_node(p) end
				set_node(p,{name=tree.trunk});
			end
			local grow = {x=above.x+dir.x*length,y=above.y+dir.y*length,z=above.z+dir.z*length};
			set_node(grow,{name="a_trees:head"});
			minetest.get_node_timer(grow):start(tree.pace)
			meta = minetest.get_meta(grow);
			meta:set_int("life",life*math.pow(0.8,branch)-1);
			meta:set_int("branch",branch+length); -- remember that we branched
			meta:set_int("treetype", tree_type)

		end

		-- add new growing part
		set_node(above,{name="a_trees:head"} );
		minetest.get_node_timer(above):start(tree.pace)
		meta = minetest.get_meta(above);
		meta:set_int("life",life-1);
		meta:set_int("branch",branch); -- decrease life
		meta:set_int("treetype", tree_type)

		if branch==0 then -- make main trunk a bit thicker
				set_node({x=pos.x+1,y=pos.y,z=pos.z},{name=tree.trunk});
				set_node({x=pos.x-1,y=pos.y,z=pos.z},{name=tree.trunk});
				set_node({x=pos.x,y=pos.y,z=pos.z+1},{name=tree.trunk});
				set_node({x=pos.x,y=pos.y,z=pos.z-1},{name=tree.trunk});
		end
	elseif nodename=="IGNORE" then
		set_node(pos,{name="a_trees:head"} );
		minetest.get_node_timer(pos):start(tree.pace)
	end
end

minetest.register_node("a_trees:head", {
	description = "Aberrant tree growth node",
	drawtype="airlike",
	paramtype="light",
	light_source=4,
	pointable=false,
	buildable_to=false,
	diggable=false,
	on_timer=treegen
})


minetest.register_node("a_trees:sapling", {
	description = "Aberrant tree sapling. Grows into a random giant tree.\nTroublesome, disappointing or fantastic.",
	drawtype="plantlike",
	walkable=false,
	tiles = {"default_sapling.png^[multiply:magenta"},
	groups = {snappy=1},
	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		local n=random(1,#trees)
		meta:set_int("life",trees[n].size)
		meta:set_int("branch",0)
		meta:set_int("treetype", n)
		minetest.get_node_timer(pos):start(trees[n].pace)
	end,

	on_timer=treegen
})

a_trees={}

function a_trees.register(spec)
	local s={}
	s.trunk=spec.trunk or "default:tree"
	s.leaves=spec.leaves or "default:leaves"
	s.fruit=spec.fruit or s.leaves
	s.size=spec.size or 30
	s.height=spec.height or 10
	s.width=spec.width or 20
	s.branchy=spec.branchy or 3
	s.fruity=spec.fruity or 100
	s.pace=spec.pace or 10
	s.slanty=spec.slanty or 4 
	table.insert(trees, s)
end

