--[[ Terumet v3.0

Mod for open-source voxel game Minetest (https://www.minetest.net/)
Written for Minetest version 5.0.0
Now also supports Minetest 0.4.17

Creates a new ore in the world which can be used to make useful alloys
and heat-powered machines.

By Terumoc [https://github.com/Terumoc]
and with contributions from:
  > obl3pplifp (https://github.com/obl3pplifp) for bug reports, information, ideas, and other considerable contributions
  > RSL-Redstonier [https://github.com/RSL-Redstonier]
  > Chem871 [https://github.com/Chemguy99] for many ideas and requests

BIG Thanks to all contributors for their input!

]]--

--[[ Copyright (C) 2017-2019 Terumoc (Scott Horvath)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>. ]]

terumet = {}
terumet.version = {major=3, minor=0, patch=0}
local ver = terumet.version
terumet.version_text = ver.major .. '.' .. ver.minor .. '.' .. ver.patch
terumet.mod_name = ":terumet"

-- this isn't the suggested way to check for game version but... it works for my purposes
terumet.legacy = minetest.get_version().string:find('0.4')

if terumet.legacy then
    minetest.log('[terumet] MTv0.4.* detected - in legacy mode!')
end

terumet.RAND = PcgRandom(os.time())

local FMT = string.format
minetest.register_chatcommand( 'item_info', {
    params = '',
    description = 'Get a complete description of the ItemStack in your hand',
    privs = {debug=true},
    func = function(name)
        local player = minetest.get_player_by_name(name)
        if player then
            local witem = player:get_wielded_item()
            if witem:is_empty() then
                return true, "You're not holding anything."
            else
                local def = witem:get_definition()
                local wear = witem:get_wear()
                local wear_pct = FMT('%.1f%%', wear / 65535 * 100.0)
                if def then
                    return true, FMT('%s "%s" #%s/%s w:%s (%s)',
                        minetest.colorize('#ff0', witem:get_name()),
                        def.description,
                        witem:get_count(),
                        minetest.colorize('#0ff', def.stack_max),
                        minetest.colorize('#f0f', wear),
                        minetest.colorize('#f0f', wear_pct)
                    )
                else
                    return true, FMT('*NO DEF* %s #%s w:%s (%s)',
                        minetest.colorize('#ff0', witem:get_name()),
                        witem:get_count(),
                        minetest.colorize('#f0f', wear),
                        minetest.colorize('#f0f', wear_pct)
                    )
                end
            end
        else
            return false, "You aren't a player somehow, sorry?!"
        end
    end
})

function terumet.chance(pct)
    if pct <= 0 then return false end
    if pct >= 100 then return true end
    return terumet.RAND:next(1,100) <= pct
end

-- function for a node's on_blast callback to be removed with a pct% chance
function terumet.blast_chance(pct, id)
    return function(pos)
        if terumet.chance(pct) then
            minetest.remove_node(pos)
            return {id}
        else
            return nil
        end
    end
end

-- empty function useful for where a callback is necessary but using nil would cause undesired default behavior
terumet.NO_FUNCTION = function() end
terumet.EMPTY = {}
terumet.ZERO_XYZ = {x=0,y=0,z=0}

function terumet.recipe_3x3(i)
    return {
        {i, i, i}, {i, i, i}, {i, i, i}
    }
end

function terumet.recipe_box(outer, inner)
    return {
        {outer, outer, outer}, {outer, inner, outer}, {outer, outer, outer}
    }
end

function terumet.recipe_plus(i)
    return {
        {'', i, ''}, {i, i, i}, {'', i, ''}
    }
end

function terumet.random_velocity(max_tenths)
    return {
        x = terumet.RAND:next(-max_tenths,max_tenths) / 10,
        y = terumet.RAND:next(-max_tenths,max_tenths) / 10,
        z = terumet.RAND:next(-max_tenths,max_tenths) / 10
    }
end

function terumet.particle_stream(pointA, pointB, density, particle_data, player)
    local dist_vector = {x=(pointB.x-pointA.x), y=(pointB.y-pointA.y), z=(pointB.z-pointA.z)}
    local dist = vector.length(dist_vector)
    local pcount = dist * density
    if pcount < 1 then return end -- guard against div/0
    local step = {x=(dist_vector.x/pcount), y=(dist_vector.y/pcount), z=(dist_vector.z/pcount)}
    local ppos = vector.new(pointA)
    for _ = 1,pcount do
        ppos = util3d.pos_plus(ppos, step)
        minetest.add_particle{
            pos = vector.new(ppos),
            velocity=terumet.random_velocity(5),
            expirationtime=(particle_data.expiration or 1),
            size=(particle_data.size or 1),
            glow=(particle_data.glow or 1),
            playername=player,
            texture=particle_data.texture,
            animation=particle_data.animation
        }
    end
end

function terumet.format_time(t)
    return string.format('%.1f s', t or 0)
end

function terumet.do_lua_file(name)
    dofile(minetest.get_modpath("ip_terumet") .. '/' .. name .. '.lua')
end

-- create a copy of node groups from an unlit machine for lit version of machine
function terumet.create_lit_node_groups(unlit_groups)
    local new_groups = {not_in_creative_inventory=1}
    for k,v in pairs(unlit_groups) do new_groups[k] = v end
    return new_groups
end

function terumet.itemstack_desc(stack)
    local stack_desc = stack:get_definition().description
    -- use only what is before a newline if one is in the description
    if stack_desc:find('\n') then stack_desc = stack_desc:match('(.*)\n') end
    if stack:get_count() > 1 then
        return string.format('%s (x%d)', stack_desc, stack:get_count())
    else
        return stack_desc
    end
end

-- given a table with 'group:XXX' keys and a node/item definition with groups, return the
-- (first) value in the table where node/item has a group key of XXX, otherwise nil
function terumet.match_group_key(table, def)
    if not def then return nil end
    for group_name,_ in pairs(def.groups) do
        local grp_key = 'group:'..group_name
        if table[grp_key] then
            return table[grp_key]
        end
    end
    return nil
end

function terumet.id(id, number)
    if number then
        return string.format('%s:%s %d', terumet.mod_name, id, number)
    else
        return string.format('%s:%s', terumet.mod_name, id)
    end
end

function terumet.give_player_item(pos, player, stack)
    local inv = player:get_inventory()
    local leftover = inv:add_item("main", stack)
    if leftover and not leftover:is_empty() then
        minetest.item_drop(leftover, player, player:get_pos())
    end
end

function terumet.tex(id)
    -- accepts both base ids (assuming this mod) and full mod ids
    -- ex: terumet.tex('ingot_raw') -> 'terumet_ingot_raw.png'
    --     terumet.tex('default:cobble') -> 'default_cobble.png'
	
    if id:match(':') then
		id = id:sub(2)
        return string.format('%s.png', id:gsub(':', '_'))
    else
        return string.format('%s_%s.png', "terumet", id)
    end
end

function terumet.item_desc(name, xinfo)
    if xinfo then
        return string.format("%s\n%s", name, minetest.colorize(terumet.options.misc.TIP_COLOR, xinfo))
    else
        return name
    end
end

function terumet.crystal_tex(color)
    return string.format('%s^[multiply:%s', terumet.tex('item_cryst'), color)
end

function terumet.tex_comp(base_tex, overlay_id)
    return base_tex .. '^' .. terumet.tex(overlay_id)
end

function terumet.tex_trans(id, rot)
    return terumet.tex(id) .. '^[transform' .. rot
end

local HEAR_DIST = 12
terumet.squishy_node_sounds = {
    footstep = {name='terumet_squish_step', max_hear_distance=HEAR_DIST},
    dig = {name='terumet_squish_dig', max_hear_distance=HEAR_DIST},
    dug = {name='terumet_squish_dug', max_hear_distance=HEAR_DIST},
    place = {name='terumet_squish_place', max_hear_distance=HEAR_DIST},
}

terumet.do_lua_file('util3d')

terumet.do_lua_file('interop/terumet_api')
terumet.do_lua_file('options')
terumet.do_lua_file('material/reg_alloy')


-- reg_alloy(name, id, block hardness level, repair material value)
terumet.reg_alloy('Terucopper', 'tcop', 1, 20)
terumet.reg_alloy('Terutin', 'ttin', 1, 15)
terumet.reg_alloy('Terusteel', 'tste', 2, 40)
terumet.reg_alloy('Terugold', 'tgol', 3, 80)
terumet.reg_alloy('Coreglass', 'cgls', 4, 120)
terumet.reg_alloy('Teruchalcum', 'tcha', 2, 60)

terumet.do_lua_file('material/ceramic')
--terumet.do_lua_file('material/thermese')
terumet.do_lua_file('material/coil')
--terumet.do_lua_file('material/crushed')
--terumet.do_lua_file('material/pwood')
--terumet.do_lua_file('material/tglass')
terumet.do_lua_file('material/rebar')
terumet.do_lua_file('material/misc')
terumet.do_lua_file('material/crystallized')
--terumet.do_lua_file('material/battery')

local id = terumet.id

-- register raw terumetal ingot as weak repair-material
terumet.register_repair_material(id('ingot_raw'), 10)

--terumet.do_lua_file('tool/reg_tools')

local sword_opts = terumet.options.tools.sword_damage


--terumet.do_lua_file('tool/ore_saw')






-- register repairable default tools and materials
-- {value of 1 item, item id}:
local dmv_values = {
    steel={10, 'default:steel_ingot'},
    bronze={30, 'default:bronze_ingot'},
    mese={90, 'default:mese_crystal'},
    diamond={100, 'default:diamond'}
}

for dmat, v in pairs(dmv_values) do
    terumet.register_repairable_item("default:pick_"..dmat, v[1]*3)
    terumet.register_repairable_item("default:axe_"..dmat, v[1]*3)
    terumet.register_repairable_item("default:shovel_"..dmat, v[1])
    terumet.register_repairable_item("default:sword_"..dmat, v[1]*2)
    terumet.register_repair_material(v[2], v[1])
end

terumet.do_lua_file('material/concrete')
terumet.do_lua_file('material/coalproc')

--experimental stuff
--terumet.do_lua_file('material/meson')

local INTEROPS = {'3d_armor', 'doors', 'unified_inventory', 'tubelib', 'dungeon_loot', 'moreores', 'farming', 'extra'}
for _,mod in ipairs(INTEROPS) do
    if minetest.get_modpath(mod) then terumet.do_lua_file('interop/'..mod) end
end

local vacfood_options = terumet.options.vac_oven.VAC_FOOD
if vacfood_options and vacfood_options.ACTIVE then terumet.do_lua_file('material/vacfood') end

terumet.do_lua_file('interop/crusher_misc')