-- =============================================================================
-- Action Database Lua API Examples
-- =============================================================================
-- This file demonstrates how to create, query, and manage GOAP actions and
-- goals from Lua using the ecs.action_db API.
--
-- The ActionDatabase is a global singleton. Actions and goals defined here
-- are immediately available to all characters in the scene.
-- =============================================================================

-- =============================================================================
-- Defining Bit Names
-- =============================================================================
-- Before using bits in preconditions/effects, you should define meaningful
-- names for the 64 available bit slots. This makes your actions readable.
--
-- Bit names are global across the entire game session. They map
-- human-readable names (like "has_axe", "is_hungry") to bit indices
-- (0-63) used in GoapBlackboard preconditions and effects.
--
-- You can define bits explicitly at startup:
-- =============================================================================

-- Explicitly assign bit names to specific indices:
ecs.action_db.set_bit_name(0, "has_axe")
ecs.action_db.set_bit_name(1, "has_wood")
ecs.action_db.set_bit_name(2, "is_hungry")
ecs.action_db.set_bit_name(3, "near_tree")
ecs.action_db.set_bit_name(4, "near_well")
ecs.action_db.set_bit_name(5, "has_bucket")
ecs.action_db.set_bit_name(6, "has_food")
ecs.action_db.set_bit_name(7, "near_fire")
ecs.action_db.set_bit_name(8, "has_cooked_food")
ecs.action_db.set_bit_name(9, "is_awake")
ecs.action_db.set_bit_name(10, "at_market")
ecs.action_db.set_bit_name(11, "at_home")
ecs.action_db.set_bit_name(12, "near_chair")
ecs.action_db.set_bit_name(13, "is_sitting")
ecs.action_db.set_bit_name(14, "near_forest")
ecs.action_db.set_bit_name(15, "is_strong")
ecs.action_db.set_bit_name(16, "has_strength")

-- Or use auto_assign_bit() to let the system pick the index:
local idx = ecs.action_db.auto_assign_bit("has_water")
print("'has_water' assigned to bit " .. idx)

-- Look up a bit by name:
local bit_idx = ecs.action_db.find_bit_by_name("has_axe")
print("'has_axe' is at bit " .. bit_idx)

-- Get the name for a bit index:
local name = ecs.action_db.get_bit_name(0)
print("Bit 0 is named '" .. name .. "'")

-- List all currently assigned bit names:
local bits = ecs.action_db.list_bit_names()
print("Assigned bit names:")
for _, b in ipairs(bits) do
    print("  Bit " .. b.index .. ": " .. b.name)
end

-- NOTE: If you use a bit name in an action's preconditions/effects
-- that hasn't been explicitly assigned, it will be auto-assigned
-- to the first free slot automatically. So you don't HAVE to
-- pre-define them, but it's good practice for clarity.

-- =============================================================================
-- Creating Actions
-- =============================================================================

-- Simple action with just a name and cost:
ecs.action_db.add_action("idle", 1)

-- Action with preconditions (what must be true before the action can run):
ecs.action_db.add_action("chop_wood", 2,
    {
        bits = { has_axe = true },
        values = { stamina = 10 }
    },
    {   -- effects (what becomes true after the action runs)
        bits = { has_wood = true },
        values = { stamina = -5 }
    }
)

-- Action with only preconditions, no effects:
ecs.action_db.add_action("fetch_water", 3,
    {
        bits = { near_well = true, has_bucket = true },
        values = { thirst = 50 }
    }
)

-- Action with only effects, no preconditions:
ecs.action_db.add_action("rest", 1,
    {},
    {
        values = { stamina = 100, energy = 100 }
    }
)

-- Action with float and string values in blackboard:
ecs.action_db.add_action("cook_food", 4,
    {
        bits = { has_food = true, near_fire = true },
        values = { cooking_skill = 3 },
        floatValues = { hunger = 50.0 }
    },
    {
        bits = { has_cooked_food = true },
        values = { hunger = -30 },
        stringValues = { last_action = "cooking" }
    }
)

-- =============================================================================
-- Creating Actions WITH Behavior Trees
-- =============================================================================

-- Action with a simple leaf behavior tree (plays an animation):
ecs.action_db.add_action("wave", 1,
    {},  -- no preconditions
    {},  -- no effects
    {    -- behavior tree (arg 5)
        type = "sequence",
        children = {
            { type = "setAnimationState", name = "SM/Wave" },
            { type = "delay", params = "2.0" },
            { type = "setAnimationState", name = "SM/Idle" }
        }
    }
)

-- Action with a selector behavior tree (try to chop, fall back to idle):
ecs.action_db.add_action("chop_tree", 3,
    {
        bits = { near_tree = true },
        values = { stamina = 15 }
    },
    {
        bits = { has_wood = true },
        values = { stamina = -8, wood_count = 1 }
    },
    {
        type = "selector",
        children = {
            {
                type = "sequence",
                children = {
                    { type = "checkBit", name = "has_axe", params = "1" },
                    { type = "setAnimationState", name = "SM/Chop" },
                    { type = "delay", params = "3.0" },
                    { type = "setAnimationState", name = "SM/Idle" },
                    { type = "setBit", name = "has_wood", params = "1" }
                }
            },
            {
                type = "sequence",
                children = {
                    { type = "debugPrint", name = "No axe! Picking up stick..." },
                    { type = "setAnimationState", name = "SM/Pickup" },
                    { type = "delay", params = "1.0" },
                    { type = "setBit", name = "has_wood", params = "1" }
                }
            }
        }
    }
)

-- Action with blackboard checks and value manipulation:
ecs.action_db.add_action("travel_to_market", 5,
    {
        bits = { is_awake = true },
        values = { energy = 20 }
    },
    {
        bits = { at_market = true },
        values = { energy = -15 }
    },
    {
        type = "sequence",
        children = {
            { type = "checkValue", name = "energy", params = ">= 20" },
            { type = "setAnimationState", name = "SM/Walk" },
            { type = "delay", params = "5.0" },
            { type = "setAnimationState", name = "SM/Idle" },
            { type = "setBit", name = "at_market", params = "1" },
            { type = "setBit", name = "at_home", params = "0" }
        }
    }
)

-- Action with inventory operations:
ecs.action_db.add_action("gather_wood", 2,
    {
        bits = { near_forest = true }
    },
    {
        values = { wood_count = 3 }
    },
    {
        type = "sequence",
        children = {
            { type = "setAnimationState", name = "SM/Gather" },
            { type = "delay", params = "2.0" },
            { type = "addItemToInventory", params = "wood,Firewood,material,3,1.0,0" },
            { type = "setAnimationState", name = "SM/Idle" }
        }
    }
)

-- Action that teleports character to a smart object child:
ecs.action_db.add_action("sit_on_chair", 1,
    {
        bits = { near_chair = true }
    },
    {
        bits = { is_sitting = true }
    },
    {
        type = "sequence",
        children = {
            { type = "teleportToChild", name = "SitTarget" },
            { type = "disablePhysics" },
            { type = "setAnimationState", name = "SM/Sit" },
            { type = "delay", params = "5.0" },
            { type = "setAnimationState", name = "SM/Stand" },
            { type = "enablePhysics" }
        }
    }
)

-- =============================================================================
-- Creating Goals
-- =============================================================================

-- Simple goal with just a name and priority:
ecs.action_db.add_goal("survive", 100)

-- Goal with a target blackboard state:
ecs.action_db.add_goal("gather_resources", 50,
    {
        bits = { has_wood = true, has_water = true },
        values = { wood_count = 5, water_count = 3 }
    }
)

-- Goal with a condition expression (evaluated against character's blackboard):
ecs.action_db.add_goal("stay_healthy", 80,
    {
        values = { health = 100, stamina = 80 }
    },
    "health < 50 || stamina < 30"  -- only valid when character needs healing
)

-- Goal with full specification:
ecs.action_db.add_goal("become_strong", 30,
    {
        bits = { is_strong = true },
        values = { strength = 100 }
    },
    "strength < 100"  -- only valid if not already strong
)

-- =============================================================================
-- Querying Actions and Goals
-- =============================================================================

-- Find an action by name:
local action = ecs.action_db.find_action("chop_wood")
if action then
    print("Found action: " .. action.name .. " (cost: " .. action.cost .. ")")
    -- action.preconditions and action.effects are tables with:
    --   .bits     - table of boolean flags
    --   .values   - table of integer values
    --   .floatValues - table of float values
    --   .stringValues - table of string values
    -- action.behaviorTree is a table with:
    --   .type     - node type string
    --   .name     - optional name
    --   .params   - optional params
    --   .children - optional array of child nodes
end

-- Find a goal by name:
local goal = ecs.action_db.find_goal("gather_resources")
if goal then
    print("Found goal: " .. goal.name .. " (priority: " .. goal.priority .. ")")
    -- goal.target is a blackboard table
    -- goal.condition is the condition string
end

-- =============================================================================
-- Listing All Actions and Goals
-- =============================================================================

-- List all action names:
local actions = ecs.action_db.list_actions()
print("Available actions:")
for i, name in ipairs(actions) do
    print("  " .. i .. ". " .. name)
end

-- List all goal names:
local goals = ecs.action_db.list_goals()
print("Available goals:")
for i, name in ipairs(goals) do
    print("  " .. i .. ". " .. name)
end

-- =============================================================================
-- Removing Actions and Goals
-- =============================================================================

-- Remove an action by name:
local removed = ecs.action_db.remove_action("idle")
if removed then
    print("Removed action: idle")
end

-- Remove a goal by name:
ecs.action_db.remove_goal("become_strong")

-- =============================================================================
-- Replacing Actions (same name = replace)
-- =============================================================================

-- If you add an action with the same name as an existing one, it replaces it:
ecs.action_db.add_action("chop_wood", 5,
    {
        bits = { has_axe = true, has_strength = true },
        values = { stamina = 20 }
    },
    {
        bits = { has_wood = true },
        values = { stamina = -10, wood_count = 2 }
    }
)
-- The old "chop_wood" action is replaced with this new definition.

-- =============================================================================
-- Clearing Everything
-- =============================================================================

-- Remove all actions and goals:
-- ecs.action_db.clear()

-- =============================================================================
-- Blackboard Table Format Reference
-- =============================================================================
--
-- The blackboard table passed to add_action/add_goal has this structure:
--
-- {
--     bits = {
--         has_axe = true,     -- boolean flags (use named bits)
--         has_wood = false,
--         is_hungry = true
--     },
--     values = {              -- integer values
--         health = 100,
--         stamina = 50,
--         wood_count = 0
--     },
--     floatValues = {         -- float values
--         hunger = 75.5,
--         speed = 1.2
--     },
--     stringValues = {        -- string values
--         last_action = "idle",
--         current_state = "exploring"
--     }
-- }
--
-- All sub-tables are optional. An empty table or nil means no constraints.
-- =============================================================================

-- =============================================================================
-- Behavior Tree Table Format Reference
-- =============================================================================
--
-- The behaviorTree table (arg 5 of add_action) has this structure:
--
-- {
--     type = "sequence",           -- node type (required)
--     name = "optional_name",      -- depends on type (task name, anim state, etc.)
--     params = "optional_params",  -- extra parameters (delay seconds, bit index, etc.)
--     children = {                 -- array of child nodes (for sequence/selector/invert)
--         { type = "task", name = "myAction" },
--         { type = "setAnimationState", name = "SM/Walk" },
--         { type = "delay", params = "2.0" },
--         { type = "checkBit", name = "has_axe", params = "1" }
--     }
-- }
--
-- Common node types:
--   "sequence"         - Execute children in order until one fails
--   "selector"         - Execute children in order until one succeeds
--   "invert"           - Invert the result of a single child
--   "task"             - Leaf: references a named task
--   "check"            - Leaf: references a named condition
--   "debugPrint"       - Leaf: prints 'name' to console
--   "setAnimationState"- Leaf: sets animation state (name="SM/State")
--   "isAnimationEnded" - Leaf: true if animation ended
--   "setBit"           - Leaf: sets blackboard bit (name=bit, params=0/1)
--   "checkBit"         - Leaf: true if blackboard bit is set
--   "setValue"         - Leaf: sets blackboard value (name=key, params=val)
--   "checkValue"       - Leaf: blackboard comparison (name=key, params="op val")
--   "delay"            - Leaf: waits N seconds (params=seconds as float)
--   "teleportToChild"  - Leaf: teleports to named child of Smart Object
--   "disablePhysics"   - Leaf: removes character from physics
--   "enablePhysics"    - Leaf: re-adds character to physics
--   "hasItem"          - Leaf check: true if inventory has itemId
--   "pickupItem"       - Leaf: picks up nearest item
--   "dropItem"         - Leaf: drops item from inventory
--   "useItem"          - Leaf: uses item from inventory
--   "addItemToInventory"- Leaf: adds item directly to inventory
-- =============================================================================
