#define BZ_ENTRYPOINT
#include <breeze.h>

BzObjectPool *nodePool = NULL;
BzObjectPool *nodeStatePool = NULL;

BzBTNode *printBT = NULL;
BzBTState agentState;

BzBTStatus printAction(void *data, f32 dt) {
    bzLogInfo("Hello, world!");
    return BZ_BT_SUCCESS;
}

bool init(int *game) {
    rlImGuiSetup(true);
    nodePool = bzObjectPoolCreate(&(BzObjectPoolDesc) {
        .objectSize = bzBTGetNodeSize(),
    });
    nodeStatePool = bzObjectPoolCreate(&(BzObjectPoolDesc) {
       .objectSize = bzBTGetNodeStateSize()
    });

    // for 1..5:
    //   seq
    //     delay 1s
    //     print "Hello, world!"
    printBT = bzBTMakeRoot(nodePool);
    BzBTNode *node = bzBTDecorRepeat(nodePool, printBT, 5);

    BzBTNode *seq = bzBTCompSequence(nodePool, node, false);

    bzBTDecorDelay(nodePool, seq, 1.0f);
    node = bzBTAction(nodePool, seq, printAction);
    bzBTNodeSetName(node,"printAction");

    agentState = bzBTCreateState(&(BzBTStateDesc) {
            .root = printBT,
            .pool = nodeStatePool,
            .userData = NULL
    });

    return true;
}
void deinit(int *game) {

    bzObjectPoolDestroy(nodePool);
    bzObjectPoolDestroy(nodeStatePool);
}

void igVisualizeBTState(const BzBTNode *node, const BzBTNodeState *state, bool sameLine, i32 depth) {
    const BzBTNode *child = bzBTNodeChild(node);
    BzBTNodeType type = bzBTGetNodeType(node);
    char extraInfo[128];
    extraInfo[0] = '\0';

    bool hasState = bzBTNodeMatchesState(node, state);

    switch (type) {
        case BZ_BT_DECOR_REPEAT:
            if (hasState) {
                snprintf(extraInfo, sizeof(extraInfo), " (%d < %d)",
                         bzBTRepeatStateGetIter(state),
                         bzBTDecorGetRepeat(node));
            } else {
                snprintf(extraInfo, sizeof(extraInfo), " (%d)", bzBTDecorGetRepeat(node));
            }
            break;
        case BZ_BT_DECOR_DELAY:
            if (hasState) {
                snprintf(extraInfo, sizeof(extraInfo), " (%.2f < %.2fms)",
                         bzBTDelayStateGetElapsed(state),
                         bzBTDecorGetDelay(node));
            } else {
                snprintf(extraInfo, sizeof(extraInfo), " (%.2fms)", bzBTDecorGetDelay(node));
            }
            break;
        case BZ_BT_ACTION:
            snprintf(extraInfo, sizeof(extraInfo), " (%s:%p)",
                     bzBTNodeGetName(node) ? bzBTNodeGetName(node) : "?",
                     bzBTActionGetFn(node));
            break;
        default:
            break;
    }
    if (hasState)
        state = bzBTNodeStateNext(state);

    ImVec4 color = {1.0f, 1.0f, 1.0f, 1.0f};
    if (hasState)
        color = (ImVec4) {1.0f, 1.0f, 0.5f, 1.0f};

    bool hasSingleChild = false;
    if (child && bzBTNodeNext(child) == NULL) hasSingleChild = true;

    const char *suffix = hasSingleChild ? " > " : ": ";

    if (sameLine) {
        igTextColored(color, "%s%s%s", bzBTNodeTypeToStr(type),
                      extraInfo, suffix);
    } else {
        igTextColored(color, "%*s%s %s",
                      depth * 2, "",
                      bzBTNodeTypeToStr(type), extraInfo, suffix);
        depth++;
    }



    while (child) {
        if (hasSingleChild) igSameLine(0, 0);
        igVisualizeBTState(child, state, hasSingleChild, depth);
        child = bzBTNodeNext(child);
    }
}

void igRenderBT(BzBTState *state) {
    const BzBTNode *root = state->root;
    if (igBegin("BehaviourTree", NULL, 0)) {
        igVisualizeBTState(root, state->_first, false, 0);
    }
    igEnd();
}

void render(float dt, int *game) {
    ClearBackground(WHITE);

    BzBTStatus status = bzBTExecute(&agentState, dt);

    rlImGuiBegin();
    igRenderBT(&agentState);
    igShowDemoWindow(NULL);
    rlImGuiEnd();
}

bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) {
    appDesc->init = (BzAppInitFunc) init;
    appDesc->deinit = (BzAppDeinitFunc ) deinit;
    appDesc->render = (BzAppRenderFunc) render;

    return true;
}
