/*a
 * This file contains the logic to attempt to intuit the labor required for
 * a given job. This is way more complicated than it should be, but I have
 * not figured out how to make it simpler.
 *
 * Usage:
 *  Instantiate an instance of the JobLaborMapper class
 *  Call the find_job_labor method of that class instance,
 *   passing the job as the only argument, to determine the labor for that job
 *  When done, destroy the instance
 *
 * The class should allow you to create multiple instances, although there is
 * little benefit to doing so. jlfuncs are not reused across instances.
 *
 */


#include "DataDefs.h"
#include "MiscUtils.h"
#include "modules/Materials.h"

#include <df/building.h>
#include <df/building_actual.h>
#include <df/building_def.h>
#include <df/building_design.h>
#include <df/building_furnacest.h>
#include <df/building_type.h>
#include <df/building_workshopst.h>

#include <df/furnace_type.h>

#include <df/general_ref.h>
#include <df/general_ref_building_holderst.h>
#include <df/general_ref_contains_itemst.h>

#include <df/item.h>
#include <df/item_type.h>

#include <df/job.h>
#include <df/job_item.h>
#include <df/job_item_ref.h>

#include <df/material_flags.h>

#include <df/reaction.h>

#include <df/unit_labor.h>

#include <df/world.h>

#include <vector>
#include <set>

using namespace std;
using std::string;
using std::endl;
using namespace DFHack;
using namespace df::enums;
using df::global::ui;
using df::global::world;

#include "labormanager.h"
#include "joblabormapper.h"

static df::unit_labor hauling_labor_map[] =
{
    df::unit_labor::HAUL_ITEM,    /* BAR */
    df::unit_labor::HAUL_STONE,    /* SMALLGEM */
    df::unit_labor::HAUL_ITEM,    /* BLOCKS */
    df::unit_labor::HAUL_STONE,    /* ROUGH */
    df::unit_labor::HAUL_STONE,    /* BOULDER */
    df::unit_labor::HAUL_WOOD,    /* WOOD */
    df::unit_labor::HAUL_FURNITURE,    /* DOOR */
    df::unit_labor::HAUL_FURNITURE,    /* FLOODGATE */
    df::unit_labor::HAUL_FURNITURE,    /* BED */
    df::unit_labor::HAUL_FURNITURE,    /* CHAIR */
    df::unit_labor::HAUL_ITEM,    /* CHAIN */
    df::unit_labor::HAUL_ITEM,    /* FLASK */
    df::unit_labor::HAUL_ITEM,    /* GOBLET */
    df::unit_labor::HAUL_ITEM,    /* INSTRUMENT */
    df::unit_labor::HAUL_ITEM,    /* TOY */
    df::unit_labor::HAUL_FURNITURE,    /* WINDOW */
    df::unit_labor::HAUL_ANIMALS,    /* CAGE */
    df::unit_labor::HAUL_ITEM,    /* BARREL */
    df::unit_labor::HAUL_ITEM,    /* BUCKET */
    df::unit_labor::HAUL_ANIMALS,    /* ANIMALTRAP */
    df::unit_labor::HAUL_FURNITURE,    /* TABLE */
    df::unit_labor::HAUL_FURNITURE,    /* COFFIN */
    df::unit_labor::HAUL_FURNITURE,    /* STATUE */
    df::unit_labor::HAUL_REFUSE,    /* CORPSE */
    df::unit_labor::HAUL_ITEM,    /* WEAPON */
    df::unit_labor::HAUL_ITEM,    /* ARMOR */
    df::unit_labor::HAUL_ITEM,    /* SHOES */
    df::unit_labor::HAUL_ITEM,    /* SHIELD */
    df::unit_labor::HAUL_ITEM,    /* HELM */
    df::unit_labor::HAUL_ITEM,    /* GLOVES */
    df::unit_labor::HAUL_FURNITURE,    /* BOX */
    df::unit_labor::HAUL_ITEM,    /* BIN */
    df::unit_labor::HAUL_FURNITURE,    /* ARMORSTAND */
    df::unit_labor::HAUL_FURNITURE,    /* WEAPONRACK */
    df::unit_labor::HAUL_FURNITURE,    /* CABINET */
    df::unit_labor::HAUL_ITEM,    /* FIGURINE */
    df::unit_labor::HAUL_ITEM,    /* AMULET */
    df::unit_labor::HAUL_ITEM,    /* SCEPTER */
    df::unit_labor::HAUL_ITEM,    /* AMMO */
    df::unit_labor::HAUL_ITEM,    /* CROWN */
    df::unit_labor::HAUL_ITEM,    /* RING */
    df::unit_labor::HAUL_ITEM,    /* EARRING */
    df::unit_labor::HAUL_ITEM,    /* BRACELET */
    df::unit_labor::HAUL_ITEM,    /* GEM */
    df::unit_labor::HAUL_FURNITURE,    /* ANVIL */
    df::unit_labor::HAUL_REFUSE,    /* CORPSEPIECE */
    df::unit_labor::HAUL_REFUSE,    /* REMAINS */
    df::unit_labor::HAUL_FOOD,    /* MEAT */
    df::unit_labor::HAUL_FOOD,    /* FISH */
    df::unit_labor::HAUL_FOOD,    /* FISH_RAW */
    df::unit_labor::HAUL_REFUSE,    /* VERMIN */
    df::unit_labor::HAUL_ITEM,    /* PET */
    df::unit_labor::HAUL_ITEM,    /* SEEDS */
    df::unit_labor::HAUL_FOOD,    /* PLANT */
    df::unit_labor::HAUL_ITEM,    /* SKIN_TANNED */
    df::unit_labor::HAUL_FOOD,    /* LEAVES */
    df::unit_labor::HAUL_ITEM,    /* THREAD */
    df::unit_labor::HAUL_ITEM,    /* CLOTH */
    df::unit_labor::HAUL_ITEM,    /* TOTEM */
    df::unit_labor::HAUL_ITEM,    /* PANTS */
    df::unit_labor::HAUL_ITEM,    /* BACKPACK */
    df::unit_labor::HAUL_ITEM,    /* QUIVER */
    df::unit_labor::HAUL_FURNITURE,    /* CATAPULTPARTS */
    df::unit_labor::HAUL_FURNITURE,    /* BALLISTAPARTS */
    df::unit_labor::HAUL_FURNITURE,    /* SIEGEAMMO */
    df::unit_labor::HAUL_FURNITURE,    /* BALLISTAARROWHEAD */
    df::unit_labor::HAUL_FURNITURE,    /* TRAPPARTS */
    df::unit_labor::HAUL_FURNITURE,    /* TRAPCOMP */
    df::unit_labor::HAUL_FOOD,    /* DRINK */
    df::unit_labor::HAUL_FOOD,    /* POWDER_MISC */
    df::unit_labor::HAUL_FOOD,    /* CHEESE */
    df::unit_labor::HAUL_FOOD,    /* FOOD */
    df::unit_labor::HAUL_FOOD,    /* LIQUID_MISC */
    df::unit_labor::HAUL_ITEM,    /* COIN */
    df::unit_labor::HAUL_FOOD,    /* GLOB */
    df::unit_labor::HAUL_STONE,    /* ROCK */
    df::unit_labor::HAUL_FURNITURE,    /* PIPE_SECTION */
    df::unit_labor::HAUL_FURNITURE,    /* HATCH_COVER */
    df::unit_labor::HAUL_FURNITURE,    /* GRATE */
    df::unit_labor::HAUL_FURNITURE,    /* QUERN */
    df::unit_labor::HAUL_FURNITURE,    /* MILLSTONE */
    df::unit_labor::HAUL_ITEM,    /* SPLINT */
    df::unit_labor::HAUL_ITEM,    /* CRUTCH */
    df::unit_labor::HAUL_FURNITURE,    /* TRACTION_BENCH */
    df::unit_labor::HAUL_ITEM,    /* ORTHOPEDIC_CAST */
    df::unit_labor::HAUL_ITEM,    /* TOOL */
    df::unit_labor::HAUL_FURNITURE,    /* SLAB */
    df::unit_labor::HAUL_FOOD,    /* EGG */
    df::unit_labor::HAUL_ITEM,    /* BOOK */
};

static df::unit_labor workshop_build_labor[] =
{
    /* Carpenters */        df::unit_labor::CARPENTER,
    /* Farmers */            df::unit_labor::PROCESS_PLANT,
    /* Masons */            df::unit_labor::MASON,
    /* Craftsdwarfs */        df::unit_labor::STONE_CRAFT,
    /* Jewelers */            df::unit_labor::CUT_GEM,
    /* MetalsmithsForge */    df::unit_labor::METAL_CRAFT,
    /* MagmaForge */        df::unit_labor::METAL_CRAFT,
    /* Bowyers */            df::unit_labor::BOWYER,
    /* Mechanics */            df::unit_labor::MECHANIC,
    /* Siege */                df::unit_labor::SIEGECRAFT,
    /* Butchers */            df::unit_labor::BUTCHER,
    /* Leatherworks */        df::unit_labor::LEATHER,
    /* Tanners */            df::unit_labor::TANNER,
    /* Clothiers */            df::unit_labor::CLOTHESMAKER,
    /* Fishery */            df::unit_labor::CLEAN_FISH,
    /* Still */                df::unit_labor::BREWER,
    /* Loom */                df::unit_labor::WEAVER,
    /* Quern */                df::unit_labor::MILLER,
    /* Kennels */            df::unit_labor::ANIMALTRAIN,
    /* Kitchen */            df::unit_labor::COOK,
    /* Ashery */            df::unit_labor::LYE_MAKING,
    /* Dyers */                df::unit_labor::DYER,
    /* Millstone */            df::unit_labor::MILLER,
    /* Custom */            df::unit_labor::NONE,
    /* Tool */                df::unit_labor::NONE
};

static df::building* get_building_from_job(df::job* j)
{
    for (auto r = j->general_refs.begin(); r != j->general_refs.end(); r++)
    {
        if ((*r)->getType() == df::general_ref_type::BUILDING_HOLDER)
        {
            int32_t id = ((df::general_ref_building_holderst*)(*r))->building_id;
            df::building* bld = binsearch_in_vector(world->buildings.all, id);
            return bld;
        }
    }
    return 0;
}

static df::unit_labor construction_build_labor(df::building_actual* b)
{
    if (b->getType() == df::building_type::RoadPaved)
        return df::unit_labor::BUILD_ROAD;
    // Find last item in building with use mode appropriate to the building's constructions state
    // For screw pumps contained_items[0] = pipe, 1 corkscrew, 2 block
    // For wells 0 mechanism, 1 rope, 2 bucket, 3 block
    // Trade depots and bridges use the last one too
    // Must check use mode b/c buildings may have items in them that are not part of the building

    df::item* i = 0;
    for (auto p = b->contained_items.begin(); p != b->contained_items.end(); p++)
        if ((b->construction_stage > 0 && (*p)->use_mode == 2) ||
            (b->construction_stage == 0 && (*p)->use_mode == 0))
            i = (*p)->item;

    MaterialInfo matinfo;
    if (i && matinfo.decode(i))
    {
        if (matinfo.material->flags.is_set(df::material_flags::IS_METAL))
            return df::unit_labor::METAL_CRAFT;
        if (matinfo.material->flags.is_set(df::material_flags::WOOD))
            return df::unit_labor::CARPENTER;
    }
    return df::unit_labor::MASON;
}


class jlfunc
{
public:
    virtual ~jlfunc() {}
    virtual df::unit_labor get_labor(df::job* j) = 0;
};

class jlfunc_const : public jlfunc
{
private:
    df::unit_labor labor;
public:
    df::unit_labor get_labor(df::job* j)
    {
        return labor;
    }
    jlfunc_const(df::unit_labor l) : labor(l) {};
};

class jlfunc_hauling : public jlfunc
{
public:
    df::unit_labor get_labor(df::job* j)
    {
        df::item* item = 0;
        if (j->job_type == df::job_type::StoreItemInStockpile && j->item_subtype != -1)
            return (df::unit_labor) j->item_subtype;

        for (auto i = j->items.begin(); i != j->items.end(); i++)
        {
            if ((*i)->role == 7)
            {
                item = (*i)->item;
                break;
            }
        }

        if (item && item->flags.bits.container)
        {
            for (auto a = item->general_refs.begin(); a != item->general_refs.end(); a++)
            {
                if ((*a)->getType() == df::general_ref_type::CONTAINS_ITEM)
                {
                    int item_id = ((df::general_ref_contains_itemst *) (*a))->item_id;
                    item = binsearch_in_vector(world->items.all, item_id);
                    break;
                }
            }
        }

        df::unit_labor l = item ? hauling_labor_map[item->getType()] : df::unit_labor::HAUL_ITEM;
        if (item && l == df::unit_labor::HAUL_REFUSE && item->flags.bits.dead_dwarf)
            l = df::unit_labor::HAUL_BODY;
        return l;
    }
    jlfunc_hauling() {};
};

class jlfunc_construct_bld : public jlfunc
{
public:
    df::unit_labor get_labor(df::job* j)
    {
        if (j->flags.bits.item_lost)
            return df::unit_labor::NONE;

        df::building* bld = get_building_from_job(j);
        switch (bld->getType())
        {
        case df::building_type::NONE:
            return df::unit_labor::NONE;
        case df::building_type::Hive:
            return df::unit_labor::BEEKEEPING;
        case df::building_type::Workshop:
        {
            df::building_workshopst* ws = (df::building_workshopst*) bld;
            if (ws->design && !ws->design->flags.bits.designed)
                return df::unit_labor::ARCHITECT;
            if (ws->type == df::workshop_type::Custom)
            {
                df::building_def* def = df::building_def::find(ws->custom_type);
                return def->build_labors[0];
            }
            else
                return workshop_build_labor[ws->type];
        }
        break;
        case df::building_type::Furnace:
        {
            df::building_furnacest* frn = (df::building_furnacest*) bld;
            if (frn->design && !frn->design->flags.bits.designed)
                return df::unit_labor::ARCHITECT;
            if (frn->type == df::furnace_type::Custom)
            {
                df::building_def* def = df::building_def::find(frn->custom_type);
                return def->build_labors[0];
            }
            else
                // cast to building_actual should be safe here because at this point the building has been designed
                return construction_build_labor((df::building_actual*)bld);
        }
        break;
        case df::building_type::Construction:
            return df::unit_labor::BUILD_CONSTRUCTION;
        case df::building_type::TradeDepot:
        case df::building_type::Bridge:
        case df::building_type::ArcheryTarget:
        case df::building_type::WaterWheel:
        case df::building_type::RoadPaved:
        case df::building_type::Well:
        case df::building_type::ScrewPump:
        case df::building_type::Wagon:
        case df::building_type::Shop:
        case df::building_type::Support:
        case df::building_type::Windmill:
        {
            df::building_actual* b = (df::building_actual*) bld;
            if (b->design && !b->design->flags.bits.designed)
                return df::unit_labor::ARCHITECT;
            return construction_build_labor(b);
        }
        break;
        case df::building_type::FarmPlot:
            return df::unit_labor::PLANT;
        case df::building_type::Chair:
        case df::building_type::Bed:
        case df::building_type::Table:
        case df::building_type::Coffin:
        case df::building_type::Door:
        case df::building_type::Floodgate:
        case df::building_type::Box:
        case df::building_type::Weaponrack:
        case df::building_type::Armorstand:
        case df::building_type::Cabinet:
        case df::building_type::Statue:
        case df::building_type::WindowGlass:
        case df::building_type::WindowGem:
        case df::building_type::Cage:
        case df::building_type::NestBox:
        case df::building_type::TractionBench:
        case df::building_type::Slab:
        case df::building_type::Chain:
        case df::building_type::GrateFloor:
        case df::building_type::Hatch:
        case df::building_type::BarsFloor:
        case df::building_type::BarsVertical:
        case df::building_type::GrateWall:
        case df::building_type::Bookcase:
        case df::building_type::Instrument:
        case df::building_type::DisplayFurniture:
            return df::unit_labor::HAUL_FURNITURE;
        case df::building_type::Trap:
        case df::building_type::GearAssembly:
        case df::building_type::AxleHorizontal:
        case df::building_type::AxleVertical:
        case df::building_type::Rollers:
            return df::unit_labor::MECHANIC;
        case df::building_type::AnimalTrap:
            return df::unit_labor::TRAPPER;
        case df::building_type::Civzone:
        case df::building_type::Nest:
        case df::building_type::OfferingPlace:
        case df::building_type::Stockpile:
        case df::building_type::Weapon:
            return df::unit_labor::NONE;
        case df::building_type::SiegeEngine:
            return df::unit_labor::SIEGECRAFT;
        case df::building_type::RoadDirt:
            return df::unit_labor::BUILD_ROAD;
        }

        debug("LABORMANAGER: Cannot deduce labor for construct building job of type %s\n",
            ENUM_KEY_STR(building_type, bld->getType()).c_str());
        debug_pause();

        return df::unit_labor::NONE;
    }
    jlfunc_construct_bld() {}
};

class jlfunc_destroy_bld : public jlfunc
{
public:
    df::unit_labor get_labor(df::job* j)
    {
        df::building* bld = get_building_from_job(j);
        df::building_type type = bld->getType();

        switch (type)
        {
        case df::building_type::NONE:
            return df::unit_labor::NONE;
        case df::building_type::Hive:
            return df::unit_labor::BEEKEEPING;
        case df::building_type::Workshop:
        {
            df::building_workshopst* ws = (df::building_workshopst*) bld;
            if (ws->type == df::workshop_type::Custom)
            {
                df::building_def* def = df::building_def::find(ws->custom_type);
                return def->build_labors[0];
            }
            else
                return workshop_build_labor[ws->type];
        }
        break;
        case df::building_type::Furnace:
        {
            df::building_furnacest* frn = (df::building_furnacest*) bld;
            if (frn->type == df::furnace_type::Custom)
            {
                df::building_def* def = df::building_def::find(frn->custom_type);
                return def->build_labors[0];
            }
            else
                // can't destroy a building if doesn't actually exist
                return construction_build_labor((df::building_actual*)bld);
        }
        break;
        case df::building_type::Construction:
            return df::unit_labor::REMOVE_CONSTRUCTION;
        case df::building_type::TradeDepot:
        case df::building_type::Wagon:
        case df::building_type::Bridge:
        case df::building_type::ScrewPump:
        case df::building_type::ArcheryTarget:
        case df::building_type::Shop:
        case df::building_type::Support:
        case df::building_type::WaterWheel:
        case df::building_type::Well:
        case df::building_type::Windmill:
        {
            auto b = (df::building_actual*) bld;
            return construction_build_labor(b);
        }
        break;
        case df::building_type::RoadPaved:
            return df::unit_labor::BUILD_ROAD;
        case df::building_type::FarmPlot:
            return df::unit_labor::PLANT;
        case df::building_type::Trap:
        case df::building_type::AxleHorizontal:
        case df::building_type::AxleVertical:
        case df::building_type::GearAssembly:
        case df::building_type::Rollers:
            return df::unit_labor::MECHANIC;
        case df::building_type::Chair:
        case df::building_type::Bed:
        case df::building_type::Table:
        case df::building_type::Coffin:
        case df::building_type::Door:
        case df::building_type::Floodgate:
        case df::building_type::Box:
        case df::building_type::Weaponrack:
        case df::building_type::Armorstand:
        case df::building_type::Cabinet:
        case df::building_type::Statue:
        case df::building_type::WindowGlass:
        case df::building_type::WindowGem:
        case df::building_type::Cage:
        case df::building_type::NestBox:
        case df::building_type::TractionBench:
        case df::building_type::Slab:
        case df::building_type::Chain:
        case df::building_type::Hatch:
        case df::building_type::BarsFloor:
        case df::building_type::BarsVertical:
        case df::building_type::GrateFloor:
        case df::building_type::GrateWall:
        case df::building_type::Bookcase:
        case df::building_type::Instrument:
        case df::building_type::DisplayFurniture:
            return df::unit_labor::HAUL_FURNITURE;
        case df::building_type::AnimalTrap:
            return df::unit_labor::TRAPPER;
        case df::building_type::Civzone:
        case df::building_type::Nest:
        case df::building_type::OfferingPlace:
        case df::building_type::RoadDirt:
        case df::building_type::Stockpile:
        case df::building_type::Weapon:
            return df::unit_labor::NONE;
        case df::building_type::SiegeEngine:
            return df::unit_labor::SIEGECRAFT;
        }

        debug("LABORMANAGER: Cannot deduce labor for destroy building job of type %s\n",
            ENUM_KEY_STR(building_type, type).c_str());
        debug_pause();

        return df::unit_labor::NONE;
    }
    jlfunc_destroy_bld() {}
};

class jlfunc_make : public jlfunc
{
private:
    df::unit_labor metaltype;
public:
    df::unit_labor get_labor(df::job* j)
    {
        df::building* bld = get_building_from_job(j);
        if (bld->getType() == df::building_type::Workshop)
        {
            df::workshop_type type = ((df::building_workshopst*)(bld))->type;
            switch (type)
            {
            case df::workshop_type::Craftsdwarfs:
            {
                df::item_type jobitem = j->job_items[0]->item_type;
                switch (jobitem)
                {
                case df::item_type::BOULDER:
                    return df::unit_labor::STONE_CRAFT;
                case df::item_type::NONE:
                    if (j->material_category.bits.bone ||
                        j->material_category.bits.horn ||
                        j->material_category.bits.tooth ||
                        j->material_category.bits.shell ||
                        j->material_category.bits.pearl)
                        return df::unit_labor::BONE_CARVE;
                    else
                    {
                        debug("LABORMANAGER: Cannot deduce labor for make crafts job (not bone)\n");
                        debug_pause();
                        return df::unit_labor::NONE;
                    }
                case df::item_type::WOOD:
                    return df::unit_labor::WOOD_CRAFT;
                case df::item_type::CLOTH:
                    return df::unit_labor::CLOTHESMAKER;
                case df::item_type::SKIN_TANNED:
                    return df::unit_labor::LEATHER;
                default:
                    debug("LABORMANAGER: Cannot deduce labor for make crafts job, item type %s\n",
                        ENUM_KEY_STR(item_type, jobitem).c_str());
                    debug_pause();
                    return df::unit_labor::NONE;
                }
            }
            case df::workshop_type::Masons:
                return df::unit_labor::MASON;
            case df::workshop_type::Carpenters:
                return df::unit_labor::CARPENTER;
            case df::workshop_type::Leatherworks:
                return df::unit_labor::LEATHER;
            case df::workshop_type::Clothiers:
                return df::unit_labor::CLOTHESMAKER;
            case df::workshop_type::Bowyers:
                return df::unit_labor::BOWYER;
            case df::workshop_type::MagmaForge:
            case df::workshop_type::MetalsmithsForge:
                return metaltype;
            default:
                debug("LABORMANAGER: Cannot deduce labor for make job, workshop type %s\n",
                    ENUM_KEY_STR(workshop_type, type).c_str());
                debug_pause();
                return df::unit_labor::NONE;
            }
        }
        else if (bld->getType() == df::building_type::Furnace)
        {
            df::furnace_type type = ((df::building_furnacest*)(bld))->type;
            switch (type)
            {
            case df::furnace_type::MagmaGlassFurnace:
            case df::furnace_type::GlassFurnace:
                return df::unit_labor::GLASSMAKER;
            default:
                debug("LABORMANAGER: Cannot deduce labor for make job, furnace type %s\n",
                    ENUM_KEY_STR(furnace_type, type).c_str());
                debug_pause();
                return df::unit_labor::NONE;
            }
        }

        debug("LABORMANAGER: Cannot deduce labor for make job, building type %s\n",
            ENUM_KEY_STR(building_type, bld->getType()).c_str());
        debug_pause();

        return df::unit_labor::NONE;
    }

    jlfunc_make(df::unit_labor mt) : metaltype(mt) {}
};

class jlfunc_custom : public jlfunc
{
public:
    df::unit_labor get_labor(df::job* j)
    {
        for (auto r : df::reaction::get_vector())
        {
            if (r->code == j->reaction_name)
            {
                df::job_skill skill = r->skill;
                df::unit_labor labor = ENUM_ATTR(job_skill, labor, skill);
                return labor;
            }
        }
        return df::unit_labor::NONE;
    }
    jlfunc_custom() {}
};

jlfunc* JobLaborMapper::jlf_const(df::unit_labor l) {
    jlfunc* jlf;
    if (jlf_cache.count(l) == 0)
    {
        jlf = new jlfunc_const(l);
        jlf_cache[l] = jlf;
    }
    else
        jlf = jlf_cache[l];

    return jlf;
}

JobLaborMapper::~JobLaborMapper()
{
    std::set<jlfunc*> log;

    for (auto i = jlf_cache.begin(); i != jlf_cache.end(); i++)
    {
        if (!log.count(i->second))
        {
            log.insert(i->second);
            delete i->second;
        }
        i->second = 0;
    }

    FOR_ENUM_ITEMS(job_type, j)
    {
        if (j < 0)
            continue;

        jlfunc* p = job_to_labor_table[j];
        if (!log.count(p))
        {
            log.insert(p);
            delete p;
        }
        job_to_labor_table[j] = 0;
    }

}

JobLaborMapper::JobLaborMapper()
{
    jlfunc* jlf_hauling = new jlfunc_hauling();
    jlfunc* jlf_make_furniture = new jlfunc_make(df::unit_labor::FORGE_FURNITURE);
    jlfunc* jlf_make_object = new jlfunc_make(df::unit_labor::METAL_CRAFT);
    jlfunc* jlf_make_armor = new jlfunc_make(df::unit_labor::FORGE_ARMOR);
    jlfunc* jlf_make_weapon = new jlfunc_make(df::unit_labor::FORGE_WEAPON);

    jlfunc* jlf_no_labor = jlf_const(df::unit_labor::NONE);

    job_to_labor_table[df::job_type::CarveFortification] = jlf_const(df::unit_labor::DETAIL);
    job_to_labor_table[df::job_type::DetailWall] = jlf_const(df::unit_labor::DETAIL);
    job_to_labor_table[df::job_type::DetailFloor] = jlf_const(df::unit_labor::DETAIL);
    job_to_labor_table[df::job_type::Dig] = jlf_const(df::unit_labor::MINE);
    job_to_labor_table[df::job_type::CarveUpwardStaircase] = jlf_const(df::unit_labor::MINE);
    job_to_labor_table[df::job_type::CarveDownwardStaircase] = jlf_const(df::unit_labor::MINE);
    job_to_labor_table[df::job_type::CarveUpDownStaircase] = jlf_const(df::unit_labor::MINE);
    job_to_labor_table[df::job_type::CarveRamp] = jlf_const(df::unit_labor::MINE);
    job_to_labor_table[df::job_type::DigChannel] = jlf_const(df::unit_labor::MINE);
    job_to_labor_table[df::job_type::FellTree] = jlf_const(df::unit_labor::CUTWOOD);
    job_to_labor_table[df::job_type::GatherPlants] = jlf_const(df::unit_labor::HERBALIST);
    job_to_labor_table[df::job_type::RemoveConstruction] = jlf_const(df::unit_labor::REMOVE_CONSTRUCTION);
    job_to_labor_table[df::job_type::CollectWebs] = jlf_const(df::unit_labor::WEAVER);
    job_to_labor_table[df::job_type::BringItemToDepot] = jlf_const(df::unit_labor::HAUL_TRADE);
    job_to_labor_table[df::job_type::BringItemToShop] = jlf_no_labor;
    job_to_labor_table[df::job_type::Eat] = jlf_no_labor;
    job_to_labor_table[df::job_type::GetProvisions] = jlf_no_labor;
    job_to_labor_table[df::job_type::Drink] = jlf_no_labor;
    job_to_labor_table[df::job_type::Drink2] = jlf_no_labor;
    job_to_labor_table[df::job_type::FillWaterskin] = jlf_no_labor;
    job_to_labor_table[df::job_type::FillWaterskin2] = jlf_no_labor;
    job_to_labor_table[df::job_type::Sleep] = jlf_no_labor;
    job_to_labor_table[df::job_type::CollectSand] = jlf_const(df::unit_labor::HAUL_ITEM);
    job_to_labor_table[df::job_type::Fish] = jlf_const(df::unit_labor::FISH);
    job_to_labor_table[df::job_type::Hunt] = jlf_const(df::unit_labor::HUNT);
    job_to_labor_table[df::job_type::HuntVermin] = jlf_no_labor;
    job_to_labor_table[df::job_type::Kidnap] = jlf_no_labor;
    job_to_labor_table[df::job_type::BeatCriminal] = jlf_no_labor;
    job_to_labor_table[df::job_type::StartingFistFight] = jlf_no_labor;
    job_to_labor_table[df::job_type::CollectTaxes] = jlf_no_labor;
    job_to_labor_table[df::job_type::GuardTaxCollector] = jlf_no_labor;
    job_to_labor_table[df::job_type::CatchLiveLandAnimal] = jlf_const(df::unit_labor::HUNT);
    job_to_labor_table[df::job_type::CatchLiveFish] = jlf_const(df::unit_labor::FISH);
    job_to_labor_table[df::job_type::ReturnKill] = jlf_no_labor;
    job_to_labor_table[df::job_type::CheckChest] = jlf_no_labor;
    job_to_labor_table[df::job_type::StoreOwnedItem] = jlf_no_labor;
    job_to_labor_table[df::job_type::PlaceItemInTomb] = jlf_const(df::unit_labor::HAUL_BODY);
    job_to_labor_table[df::job_type::StoreItemInStockpile] = jlf_hauling;
    job_to_labor_table[df::job_type::StoreItemInBag] = jlf_hauling;
    job_to_labor_table[df::job_type::StoreItemInHospital] = jlf_hauling;
    job_to_labor_table[df::job_type::StoreWeapon] = jlf_hauling;
    job_to_labor_table[df::job_type::StoreArmor] = jlf_hauling;
    job_to_labor_table[df::job_type::StoreItemInBarrel] = jlf_hauling;
    job_to_labor_table[df::job_type::StoreItemInBin] = jlf_hauling;
    job_to_labor_table[df::job_type::SeekArtifact] = jlf_no_labor;
    job_to_labor_table[df::job_type::SeekInfant] = jlf_no_labor;
    job_to_labor_table[df::job_type::AttendParty] = jlf_no_labor;
    job_to_labor_table[df::job_type::GoShopping] = jlf_no_labor;
    job_to_labor_table[df::job_type::GoShopping2] = jlf_no_labor;
    job_to_labor_table[df::job_type::Clean] = jlf_const(df::unit_labor::CLEAN);
    job_to_labor_table[df::job_type::Rest] = jlf_no_labor;
    job_to_labor_table[df::job_type::PickupEquipment] = jlf_no_labor;
    job_to_labor_table[df::job_type::DumpItem] = jlf_const(df::unit_labor::HAUL_REFUSE);
    job_to_labor_table[df::job_type::StrangeMoodCrafter] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodJeweller] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodForge] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodMagmaForge] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodBrooding] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodFell] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodCarpenter] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodMason] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodBowyer] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodTanner] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodWeaver] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodGlassmaker] = jlf_no_labor;
    job_to_labor_table[df::job_type::StrangeMoodMechanics] = jlf_no_labor;
    job_to_labor_table[df::job_type::ConstructBuilding] = new jlfunc_construct_bld();
    job_to_labor_table[df::job_type::ConstructDoor] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructFloodgate] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructBed] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructThrone] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructCoffin] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructTable] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructChest] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructBin] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructArmorStand] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructWeaponRack] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructCabinet] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructStatue] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructBlocks] = jlf_make_furniture;
    job_to_labor_table[df::job_type::MakeRawGlass] = jlf_const(df::unit_labor::GLASSMAKER);
    job_to_labor_table[df::job_type::MakeCrafts] = jlf_make_object;
    job_to_labor_table[df::job_type::MintCoins] = jlf_const(df::unit_labor::METAL_CRAFT);
    job_to_labor_table[df::job_type::CutGems] = jlf_const(df::unit_labor::CUT_GEM);
    job_to_labor_table[df::job_type::CutGlass] = jlf_const(df::unit_labor::CUT_GEM);
    job_to_labor_table[df::job_type::EncrustWithGems] = jlf_const(df::unit_labor::ENCRUST_GEM);
    job_to_labor_table[df::job_type::EncrustWithGlass] = jlf_const(df::unit_labor::ENCRUST_GEM);
    job_to_labor_table[df::job_type::DestroyBuilding] = new jlfunc_destroy_bld();
    job_to_labor_table[df::job_type::SmeltOre] = jlf_const(df::unit_labor::SMELT);
    job_to_labor_table[df::job_type::MeltMetalObject] = jlf_const(df::unit_labor::SMELT);
    job_to_labor_table[df::job_type::ExtractMetalStrands] = jlf_const(df::unit_labor::EXTRACT_STRAND);
    job_to_labor_table[df::job_type::PlantSeeds] = jlf_const(df::unit_labor::PLANT);
    job_to_labor_table[df::job_type::HarvestPlants] = jlf_const(df::unit_labor::PLANT);
    job_to_labor_table[df::job_type::TrainHuntingAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN);
    job_to_labor_table[df::job_type::TrainWarAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN);
    job_to_labor_table[df::job_type::MakeWeapon] = jlf_make_weapon;
    job_to_labor_table[df::job_type::ForgeAnvil] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructCatapultParts] = jlf_const(df::unit_labor::SIEGECRAFT);
    job_to_labor_table[df::job_type::ConstructBallistaParts] = jlf_const(df::unit_labor::SIEGECRAFT);
    job_to_labor_table[df::job_type::MakeArmor] = jlf_make_armor;
    job_to_labor_table[df::job_type::MakeHelm] = jlf_make_armor;
    job_to_labor_table[df::job_type::MakePants] = jlf_make_armor;
    job_to_labor_table[df::job_type::StudWith] = jlf_make_object;
    job_to_labor_table[df::job_type::ButcherAnimal] = jlf_const(df::unit_labor::BUTCHER);
    job_to_labor_table[df::job_type::PrepareRawFish] = jlf_const(df::unit_labor::CLEAN_FISH);
    job_to_labor_table[df::job_type::MillPlants] = jlf_const(df::unit_labor::MILLER);
    job_to_labor_table[df::job_type::BaitTrap] = jlf_const(df::unit_labor::TRAPPER);
    job_to_labor_table[df::job_type::MilkCreature] = jlf_const(df::unit_labor::MILK);
    job_to_labor_table[df::job_type::MakeCheese] = jlf_const(df::unit_labor::MAKE_CHEESE);
    job_to_labor_table[df::job_type::ProcessPlants] = jlf_const(df::unit_labor::PROCESS_PLANT);
    job_to_labor_table[df::job_type::ProcessPlantsVial] = jlf_const(df::unit_labor::PROCESS_PLANT);
    job_to_labor_table[df::job_type::ProcessPlantsBarrel] = jlf_const(df::unit_labor::PROCESS_PLANT);
    job_to_labor_table[df::job_type::PrepareMeal] = jlf_const(df::unit_labor::COOK);
    job_to_labor_table[df::job_type::WeaveCloth] = jlf_const(df::unit_labor::WEAVER);
    job_to_labor_table[df::job_type::MakeGloves] = jlf_make_armor;
    job_to_labor_table[df::job_type::MakeShoes] = jlf_make_armor;
    job_to_labor_table[df::job_type::MakeShield] = jlf_make_armor;
    job_to_labor_table[df::job_type::MakeCage] = jlf_make_furniture;
    job_to_labor_table[df::job_type::MakeChain] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeFlask] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeGoblet] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeToy] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeAnimalTrap] = jlf_const(df::unit_labor::TRAPPER);
    job_to_labor_table[df::job_type::MakeBarrel] = jlf_make_furniture;
    job_to_labor_table[df::job_type::MakeBucket] = jlf_make_furniture;
    job_to_labor_table[df::job_type::MakeWindow] = jlf_make_furniture;
    job_to_labor_table[df::job_type::MakeTotem] = jlf_const(df::unit_labor::BONE_CARVE);
    job_to_labor_table[df::job_type::MakeAmmo] = jlf_make_weapon;
    job_to_labor_table[df::job_type::DecorateWith] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeBackpack] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeQuiver] = jlf_make_armor;
    job_to_labor_table[df::job_type::MakeBallistaArrowHead] = jlf_make_weapon;
    job_to_labor_table[df::job_type::AssembleSiegeAmmo] = jlf_const(df::unit_labor::SIEGECRAFT);
    job_to_labor_table[df::job_type::LoadCatapult] = jlf_const(df::unit_labor::SIEGEOPERATE);
    job_to_labor_table[df::job_type::LoadBallista] = jlf_const(df::unit_labor::SIEGEOPERATE);
    job_to_labor_table[df::job_type::FireCatapult] = jlf_const(df::unit_labor::SIEGEOPERATE);
    job_to_labor_table[df::job_type::FireBallista] = jlf_const(df::unit_labor::SIEGEOPERATE);
    job_to_labor_table[df::job_type::ConstructMechanisms] = jlf_const(df::unit_labor::MECHANIC);
    job_to_labor_table[df::job_type::MakeTrapComponent] = jlf_make_weapon;
    job_to_labor_table[df::job_type::LoadCageTrap] = jlf_const(df::unit_labor::MECHANIC);
    job_to_labor_table[df::job_type::LoadStoneTrap] = jlf_const(df::unit_labor::MECHANIC);
    job_to_labor_table[df::job_type::LoadWeaponTrap] = jlf_const(df::unit_labor::MECHANIC);
    job_to_labor_table[df::job_type::CleanTrap] = jlf_const(df::unit_labor::MECHANIC);
    job_to_labor_table[df::job_type::CastSpell] = jlf_no_labor;
    job_to_labor_table[df::job_type::LinkBuildingToTrigger] = jlf_const(df::unit_labor::MECHANIC);
    job_to_labor_table[df::job_type::PullLever] = jlf_const(df::unit_labor::PULL_LEVER);
    job_to_labor_table[df::job_type::ExtractFromPlants] = jlf_const(df::unit_labor::HERBALIST);
    job_to_labor_table[df::job_type::ExtractFromRawFish] = jlf_const(df::unit_labor::DISSECT_FISH);
    job_to_labor_table[df::job_type::ExtractFromLandAnimal] = jlf_const(df::unit_labor::DISSECT_VERMIN);
    job_to_labor_table[df::job_type::TameVermin] = jlf_const(df::unit_labor::ANIMALTRAIN);
    job_to_labor_table[df::job_type::TameAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN);
    job_to_labor_table[df::job_type::ChainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS);
    job_to_labor_table[df::job_type::UnchainAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS);
    job_to_labor_table[df::job_type::UnchainPet] = jlf_no_labor;
    job_to_labor_table[df::job_type::ReleaseLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS);
    job_to_labor_table[df::job_type::ReleasePet] = jlf_no_labor;
    job_to_labor_table[df::job_type::ReleaseSmallCreature] = jlf_no_labor;
    job_to_labor_table[df::job_type::HandleSmallCreature] = jlf_no_labor;
    job_to_labor_table[df::job_type::HandleLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS);
    job_to_labor_table[df::job_type::CageLargeCreature] = jlf_const(df::unit_labor::HAUL_ANIMALS);
    job_to_labor_table[df::job_type::CageSmallCreature] = jlf_no_labor;
    job_to_labor_table[df::job_type::RecoverWounded] = jlf_const(df::unit_labor::RECOVER_WOUNDED);
    job_to_labor_table[df::job_type::DiagnosePatient] = jlf_const(df::unit_labor::DIAGNOSE);
    job_to_labor_table[df::job_type::ImmobilizeBreak] = jlf_const(df::unit_labor::BONE_SETTING);
    job_to_labor_table[df::job_type::DressWound] = jlf_const(df::unit_labor::DRESSING_WOUNDS);
    job_to_labor_table[df::job_type::CleanPatient] = jlf_const(df::unit_labor::DRESSING_WOUNDS);
    job_to_labor_table[df::job_type::Surgery] = jlf_const(df::unit_labor::SURGERY);
    job_to_labor_table[df::job_type::Suture] = jlf_const(df::unit_labor::SUTURING);
    job_to_labor_table[df::job_type::SetBone] = jlf_const(df::unit_labor::BONE_SETTING);
    job_to_labor_table[df::job_type::PlaceInTraction] = jlf_const(df::unit_labor::BONE_SETTING);
    job_to_labor_table[df::job_type::DrainAquarium] = jlf_const(df::unit_labor::HAUL_WATER);
    job_to_labor_table[df::job_type::FillAquarium] = jlf_const(df::unit_labor::HAUL_WATER);
    job_to_labor_table[df::job_type::FillPond] = jlf_const(df::unit_labor::HAUL_WATER);
    job_to_labor_table[df::job_type::GiveWater] = jlf_const(df::unit_labor::FEED_WATER_CIVILIANS);
    job_to_labor_table[df::job_type::GiveFood] = jlf_const(df::unit_labor::FEED_WATER_CIVILIANS);
    job_to_labor_table[df::job_type::GiveWater2] = jlf_const(df::unit_labor::FEED_WATER_CIVILIANS);
    job_to_labor_table[df::job_type::GiveFood2] = jlf_const(df::unit_labor::FEED_WATER_CIVILIANS);
    job_to_labor_table[df::job_type::RecoverPet] = jlf_no_labor;
    job_to_labor_table[df::job_type::PitLargeAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS);
    job_to_labor_table[df::job_type::PitSmallAnimal] = jlf_no_labor;
    job_to_labor_table[df::job_type::SlaughterAnimal] = jlf_const(df::unit_labor::BUTCHER);
    job_to_labor_table[df::job_type::MakeCharcoal] = jlf_const(df::unit_labor::BURN_WOOD);
    job_to_labor_table[df::job_type::MakeAsh] = jlf_const(df::unit_labor::BURN_WOOD);
    job_to_labor_table[df::job_type::MakeLye] = jlf_const(df::unit_labor::LYE_MAKING);
    job_to_labor_table[df::job_type::MakePotashFromLye] = jlf_const(df::unit_labor::POTASH_MAKING);
    job_to_labor_table[df::job_type::FertilizeField] = jlf_const(df::unit_labor::PLANT);
    job_to_labor_table[df::job_type::MakePotashFromAsh] = jlf_const(df::unit_labor::POTASH_MAKING);
    job_to_labor_table[df::job_type::DyeThread] = jlf_const(df::unit_labor::DYER);
    job_to_labor_table[df::job_type::DyeCloth] = jlf_const(df::unit_labor::DYER);
    job_to_labor_table[df::job_type::SewImage] = jlf_make_object;
    job_to_labor_table[df::job_type::MakePipeSection] = jlf_make_furniture;
    job_to_labor_table[df::job_type::OperatePump] = jlf_const(df::unit_labor::OPERATE_PUMP);
    job_to_labor_table[df::job_type::ManageWorkOrders] = jlf_no_labor;
    job_to_labor_table[df::job_type::UpdateStockpileRecords] = jlf_no_labor;
    job_to_labor_table[df::job_type::TradeAtDepot] = jlf_no_labor;
    job_to_labor_table[df::job_type::ConstructHatchCover] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructGrate] = jlf_make_furniture;
    job_to_labor_table[df::job_type::RemoveStairs] = jlf_const(df::unit_labor::MINE);
    job_to_labor_table[df::job_type::ConstructQuern] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructMillstone] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructSplint] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructCrutch] = jlf_make_furniture;
    job_to_labor_table[df::job_type::ConstructTractionBench] = jlf_const(df::unit_labor::MECHANIC);
    job_to_labor_table[df::job_type::CleanSelf] = jlf_no_labor;
    job_to_labor_table[df::job_type::BringCrutch] = jlf_const(df::unit_labor::BONE_SETTING);
    job_to_labor_table[df::job_type::ApplyCast] = jlf_const(df::unit_labor::BONE_SETTING);
    job_to_labor_table[df::job_type::CustomReaction] = new jlfunc_custom();
    job_to_labor_table[df::job_type::ConstructSlab] = jlf_make_furniture;
    job_to_labor_table[df::job_type::EngraveSlab] = jlf_const(df::unit_labor::DETAIL);
    job_to_labor_table[df::job_type::ShearCreature] = jlf_const(df::unit_labor::SHEARER);
    job_to_labor_table[df::job_type::SpinThread] = jlf_const(df::unit_labor::SPINNER);
    job_to_labor_table[df::job_type::PenLargeAnimal] = jlf_const(df::unit_labor::HAUL_ANIMALS);
    job_to_labor_table[df::job_type::PenSmallAnimal] = jlf_no_labor;
    job_to_labor_table[df::job_type::MakeTool] = jlf_make_object;
    job_to_labor_table[df::job_type::CollectClay] = jlf_const(df::unit_labor::POTTERY);
    job_to_labor_table[df::job_type::InstallColonyInHive] = jlf_const(df::unit_labor::BEEKEEPING);
    job_to_labor_table[df::job_type::CollectHiveProducts] = jlf_const(df::unit_labor::BEEKEEPING);
    job_to_labor_table[df::job_type::CauseTrouble] = jlf_no_labor;
    job_to_labor_table[df::job_type::DrinkBlood] = jlf_no_labor;
    job_to_labor_table[df::job_type::ReportCrime] = jlf_no_labor;
    job_to_labor_table[df::job_type::ExecuteCriminal] = jlf_no_labor;
    job_to_labor_table[df::job_type::TrainAnimal] = jlf_const(df::unit_labor::ANIMALTRAIN);
    job_to_labor_table[df::job_type::CarveTrack] = jlf_const(df::unit_labor::DETAIL);
    job_to_labor_table[df::job_type::PushTrackVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES);
    job_to_labor_table[df::job_type::PlaceTrackVehicle] = jlf_const(df::unit_labor::HANDLE_VEHICLES);
    job_to_labor_table[df::job_type::StoreItemInVehicle] = jlf_hauling;
    job_to_labor_table[df::job_type::GeldAnimal] = jlf_const(df::unit_labor::GELD);
    job_to_labor_table[df::job_type::MakeFigurine] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeAmulet] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeScepter] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeCrown] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeRing] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeEarring] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeBracelet] = jlf_make_object;
    job_to_labor_table[df::job_type::MakeGem] = jlf_make_object;
    job_to_labor_table[df::job_type::PutItemOnDisplay] = jlf_const(df::unit_labor::HAUL_ITEM);

    job_to_labor_table[df::job_type::StoreItemInLocation] = jlf_no_labor; // StoreItemInLocation

    job_to_labor_table[df::job_type::unk_fake_no_job] = jlf_no_labor; // added for 47.04 - see #1561
    job_to_labor_table[df::job_type::InterrogateSubject] = jlf_no_labor; // added for 47.04 - see #1561
    job_to_labor_table[df::job_type::unk_fake_no_activity] = jlf_no_labor; // added for 47.04 - see #1561
};

df::unit_labor JobLaborMapper::find_job_labor(df::job* j)
{
    if (j->job_type == df::job_type::CustomReaction)
    {
        for (auto r : df::reaction::get_vector())
        {
            if (r->code == j->reaction_name)
            {
                df::job_skill skill = r->skill;
                return ENUM_ATTR(job_skill, labor, skill);
            }
        }
        return df::unit_labor::NONE;
    }


    df::unit_labor labor;
    if (job_to_labor_table.count(j->job_type) == 0)
    {
        debug("LABORMANAGER: job has no job to labor table entry: %s (%d)\n", ENUM_KEY_STR(job_type, j->job_type).c_str(), j->job_type);
        debug_pause();
        labor = df::unit_labor::NONE;
    }
    else {

        labor = job_to_labor_table[j->job_type]->get_labor(j);
    }

    return labor;
}

/* End of labor deducer */
