-- LUALOCALS < ---------------------------------------------------------
local PcgRandom, klots, math, minetest, pairs, tonumber
    = PcgRandom, klots, math, minetest, pairs, tonumber
local math_floor, math_pow
    = math.floor, math.pow
-- LUALOCALS > ---------------------------------------------------------

local modname = minetest.get_current_modname()

local dmin = {
	{2},
	{2},
	{0, 5, 2},
	{7, 5, 2},
}
local cmaj = {
	{0},
	{0},
	{-3, 2, 0},
	{5, 2, 0}
}
local keys = {
	dmin,
	dmin,
	cmaj
}

local bpm = 0.5
local function metronome() return math_floor(minetest.get_gametime() * bpm) end

local echotime = 0.5
local echogain = 0.6
local function playnote(t, data, tone, volume)
	if t < (data.noplay or 0) then return end
	local id = minetest.sound_play(modname .. "_flute",
		{
			to_player = data.pname,
			pitch = math_pow(2, tone / 12),
			gain = 0.15 * volume
		})

	local buff = data.playing
	if not buff then
		buff = {}
		data.playing = buff
	end
	buff[id] = true
	minetest.after(5, function() buff[id] = nil end)

	if volume > 0.1 then
		minetest.after(echotime, function()
				return playnote(t + echotime * bpm, data,
					tone, volume * echogain)
			end)
	end
end

local getdata = klots.register_playerstep(function(player, data)
		local t = metronome()
		if t == data.last then return end
		data.last = t

		local overall_gain = tonumber(minetest.settings:get(modname .. "_volume")) or 1
		if overall_gain <= 0 then return end

		if player:get_pos().y < 128 then return end
		if t < (data.noplay or 0) then return end

		local keypos = math_floor(t / 16)
		local measure = math_floor(t / 4)
		local note = t % 4 + 1

		local key = PcgRandom(keypos):next(1, #keys)
		local motifs = keys[key]
		local motif = PcgRandom(measure):next(1, #motifs)
		local volume = PcgRandom(measure):next(1, 1000) *.0008 + 0.2
		local tone = motifs[motif][note]
		if not tone then return end

		data.pname = data.pname or player:get_player_name()
		return playnote(t, data, tone, volume * overall_gain)
	end)

klots.register_on_player_teleport(function(player)
		local data = getdata(player)
		data.noplay = metronome() + 2
		if not data.playing then return end
		for id in pairs(data.playing) do minetest.sound_stop(id) end
	end)
