-- API for other mods to interface with this mod


-- register an external block for use in making heatline versions and/or reinforced versions
-- if you wish to exclude one or the other, pass {heatline=true} or {reinforced=true} as 3rd argument


-- register an item that provide Repair Material to Equipment Reformer
-- value = repair material value provided by 1 item
function terumet.register_repair_material(id, value)
    -- TODO error checking
    terumet.options.repm.repair_mats[id] = value
end

-- register a tool that can be repaired in Equipment Reformer
-- needed_mat = amount of repair material value to repair fully worn tool
function terumet.register_repairable_item(id, needed_mat)
    -- TODO error checking
    terumet.options.repm.repairable[id] = needed_mat
end

-- register a new alloy smelter recipe
-- required data keys and descriptions:
local ALLOY_REQUIRED = {
    result='[itemstack string] what will be output',
    input='[table, 1-4 stacks] table of itemstacks consumed as input',
    time='[float] time in seconds to process',
    flux='[integer] amount of terumetal flux consumed from tank',
}
function terumet.register_alloy_recipe(data)
    if not data then
        error('terumet.register_alloy_recipe: no recipe data provided')
    end
    for req_key, desc in pairs(ALLOY_REQUIRED) do
        if not data[req_key] then
            error(string.format('terumet.register_alloy_recipe: recipe data is missing required key %s: %s', req_key, desc))
        end
    end
    if type(data.input) ~= 'table' or #data.input < 1 or #data.input > 4 then
        error('terumet.register_alloy_recipe: invalid input; must be a table of 1-4 itemstack strings (inclusive)')
    end
    table.insert(terumet.options.smelter.recipes, 1, data)
end

-- TODO error checking
function terumet.register_vacoven_recipe(data)
    table.insert(terumet.options.vac_oven.recipes, data)
end

-- register a new crystallized material with the provided data
-- ID of created item will be 'terumet:item_cryst_<SUFFIX>' 
-- IMPORTANT NOTE: a single source item can only be defined as a single crystal
-- for example, trying to add a new crystal for 'default:copper_lump' will override the default one
-- 
-- required data keys and descriptions:
local CRYSTAL_REQUIRED = {
    suffix='[string] ID suffix for crystallized item',
    color='[colorspec] minetest colorspec for crystallized item color',
    name='[string] name of crystallized item',
    cooking_result='[itemstack string] result of cooking crystallized item',
}


function terumet.register_crystal(data)
    local this_func = 'terumet.register_crystal'
    if not data then
        error(this_func..': no data provided')
    end
    for req_key, desc in pairs(CRYSTAL_REQUIRED) do
        if not data[req_key] then
            error(string.format('%s: data missing required key %s: %s', this_func, req_key, desc))
        end
    end
    local crys_id = terumet.id('item_cryst_'..data.suffix)
    minetest.register_craftitem( crys_id, {
        description = data.name,
        inventory_image = terumet.crystal_tex(data.color),
    })
    minetest.register_craft{ type = 'cooking',
        output = data.cooking_result,
        recipe = crys_id,
        cooktime = 5
    }
    return crys_id -- returns crystal item ID
end

-- register an item that can be put into crystal vulcanizer
-- requires source item and result, optionally include integer modifier
-- by default creates 2 result items, but count_modifier is added to that value
-- +1 more result item if vulcanizer has upgrade
function terumet.register_vulcan_result(source, result, count_modifier, specialized)
    count_modifier = count_modifier or 0
    local this_func = 'terumet.register_vulcan_result'
    if not source then error(this_func..': no source item provided') end
    if not result then error(this_func..': no result item provided') end
    local count = 2 + count_modifier
    if( count < 1 ) then count = 1 end
    terumet.options.vulcan.recipes[source] = {result, count, specialized}
end

-- register that a node can generate heat when extracted by the Environmental Entropy Extraction Heater (EEE Heater)
local ENTROPIC_REQUIRED = {
    node='[string] id of node that can be extracted',
    hu_per_s='[integer] number of heat units extracted per second',
    extract_time='[float] total amount of time to extract this node',
}
-- optional keys:
-- change_to: [string] what node this node will change into after extraction - if nil, the node will not change and thus can be extracted over and over (like air by default)
function terumet.register_entropic_node(data)
    if not data then
        error('terumet.register_entropic_node: no data provided')
    end
    for req_key, desc in pairs(ENTROPIC_REQUIRED) do
        if not data[req_key] then
            error(string.format('terumet.register_entropic_node: data is missing required key %s: %s', req_key, desc))
        end
    end
    terumet.options.heater.entropy.EFFECTS[data.node] = {hu_per_s=data.hu_per_s, extract_time=data.extract_time, change_to=data.change_to}
end


local STANDARD_INV_LISTS = {'in', 'out', 'fuel', 'upgrade'}
local EXTERNAL_MACHINES = {}
local MACHINE_REQUIRED = {
    name='[string] Name of machine',
    node_tiles='[minetest tiles definition] Tiles for machine node',
    heat_max='[integer] Base maximum heat units that can be stored by machine',
    input_slots='[integer, 0-4 expected] Size of input inventory',
    output_slots='[integer, 0-4 expected] Size of output inventory',
    has_fuel_slot='[boolean] Whether machine has direct fuel slot',
    upgrade_slots='[integer, 0-6 expected] Size of upgrade inventory',
    tick_time='[float] Time in seconds between machine ticks',
    tick_function='[fn(machine_state, dt) -> boolean] Function called when machine ticks. Return true to tick again in tick_time seconds.'
}
-- optional keys:
-- heat_provider: [boolean] true if machine generates/provides heat to adjacent machines
-- heat_transfer: [integer] Maximum amount of heat machine can send in 1 tick
-- node_name: [string] name to give machine's node. if not provided, uses same as 'name'
-- node_param2: [string] param2 to give machine's node (same as minetest's nodedef param2 setting: facedir/none/etc. )
-- custom_init: [fn(pos, meta, inv) -> nil] custom initialization for setting inventories or other metadata of a new machine
-- custom_write: [fn(machine_state)] function to call when saving machine state to metadata
-- custom_read: [fn(machine_state)] function to call when reading machine state from metadata
-- -- machine formspec options --
-- <basic>
-- machinefs_theme: [string] definition of background/listcolors for machine's formspec
-- machinefs_func: [fn(machine_state) -> string] custom function that returns formspec definition for main area above inventory in interface
-- -- <OR advanced> --
-- custom_fsdef: [fsdef table] entire customized formspec definition table for machine to use, see terumet/machine/machine.lua:build_fs for more information
--  [IMPORTANT] => using custom_fsdef completely overrides use of machinefs_* funcs and default formspec so everything must be defined

function terumet.register_heat_machine( id, data )
    if not data then
        error('terumet.register_heat_machine: no data provided')
    end
    for req_key, desc in pairs(MACHINE_REQUIRED) do
        if not data[req_key] then
            error(string.format('terumet.register_heat_machine: data is missing required key %s: %s', req_key, desc))
        end
    end

    local machine_tm_class = {
        name = data.name,
        timer = data.tick_time,
        fsdef = data.custom_fsdef or {
            control_buttons = {
                terumet.machine.buttondefs.HEAT_XFER_TOGGLE,
            },
            machine = data.machinefs_func or terumet.NO_FUNCTION,
            input = {data.input_slots > 0},
            output = {data.output_slots > 0},
            fuel_slot = {data.fuel_slot},
            theme = data.machinefs_theme,
        },
        default_heat_xfer = (data.heat_provider and terumet.machine.HEAT_XFER_MODE.PROVIDE_ONLY) or terumet.machine.HEAT_XFER_MODE.ACCEPT,
        get_drop_contents = function(machine)
            local drops = {}
            for _,std_list in ipairs(STANDARD_INV_LISTS) do
                default.get_inventory_drops(machine.pos, std_list, drops)
            end
            return drops
        end
    }

    local node_def = terumet.machine.nodedef{
        description = data.node_name or data.name,
        tiles = data.node_tiles,
        param2 = data.node_param2,
        on_construct = function(pos)
            local meta = minetest.get_meta(pos)
            local inv = meta:get_inventory()
            if data.upgrade_slots > 0 then inv:set_size('upgrade', data.upgrade_slots) end
            if data.input_slots > 0 then inv:set_size('in', data.input_slots) end
            if data.output_slots > 0 then inv:set_size('out', data.output_slots) end
            if data.has_fuel_slot then inv:set_size('fuel', 1) end
            if data.custom_init then data.custom_init(pos, meta, inv) end
            local init = {
                class = machine_tm_class,
                state = 0,
                state_time = 0,
                heat_level = 0,
                max_heat = data.heat_max,
                status_text = 'New',
                inv = inv,
                meta = meta,
                pos = pos,
            }
            terumet.machine.write_state(pos, init)
            terumet.machine.set_timer(init)
        end,
        on_timer = function(pos, dt)
            local machine = terumet.machine.tick_read_state(pos)
            local re_tick = false
            if not terumet.machine.check_overheat(machine, data.heat_max) then
                re_tick = data.tick_function(machine, dt)
                if machine.heat_xfer_mode == terumet.machine.HEAT_XFER_MODE.PROVIDE_ONLY then
                    terumet.machine.push_heat_adjacent(machine, data.heat_transfer or 50)
                end

                if data.has_fuel_slot then
                    terumet.machine.process_fuel(machine)
                    re_tick = not machine.need_heat
                end
            end
            -- write status back to meta
            terumet.machine.write_state(pos, machine)
            return re_tick
        end,
        _terumach_class = machine_tm_class
    }

    minetest.register_node( id, node_def )

    EXTERNAL_MACHINES[id] = data
end
