#ifndef BREEZE_BEHAVIOUR_TREE_H
#define BREEZE_BEHAVIOUR_TREE_H

#include "../defines.h"

typedef struct BzBTNode BzBTNode;

typedef enum BzBTStatus {
    BZ_BT_RUNNING,
    BZ_BT_SUCCESS,
    BZ_BT_FAIL,
    BZ_BT_ERROR,
} BzBTStatus;

typedef BzBTStatus(*BzBTActionFn)(void *data, f32 dt);

typedef enum BzBTNodeType {
    // Composite
    BZ_BT_COMP_SELECTOR,
    BZ_BT_COMP_SEQUENCE,
    BZ_BT_COMP_PARALLEL_SELECTOR,
    BZ_BT_COMP_PARALLEL_SEQUENCE,
    // Decorator
    BZ_BT_DECOR_DUMMY,
    BZ_BT_DECOR_SUCCESS,
    BZ_BT_DECOR_FAIL,
    BZ_BT_DECOR_INVERT,
    BZ_BT_DECOR_UNTIL_SUCCESS,
    BZ_BT_DECOR_UNTIL_FAIL,
    BZ_BT_DECOR_REPEAT,
    BZ_BT_DECOR_DELAY,
    // Action/Task
    BZ_BT_ACTION,
} BzBTNodeType;

typedef struct BzObjectPool BzObjectPool;
typedef struct BzBTNodeState BzBTNodeState;

typedef struct BzBTState {
    const BzBTNode *root;
    BzBTNodeState  *_first;
    BzBTNodeState  *_last;

    BzBTActionFn onSuccess;
    BzBTActionFn onFailure;
    BzBTActionFn onError;

    BzObjectPool *nodeStatePool;
    void *userData;
} BzBTState;

typedef struct BzBTStateDesc {
    const BzBTNode *root;

    BzObjectPool *pool;
    void *userData;
} BzBTStateDesc;

size_t  bzBTGetNodeSize();
size_t  bzBTGetNodeStateSize();

const char *bzBTNodeTypeToStr(BzBTNodeType type);

BzBTNode *bzBTMakeRoot(BzObjectPool *nodePool);
void      bzBTDestroyRoot(BzObjectPool *nodePool, BzBTNode *node);

BzBTNode *bzBTCompSelector(BzObjectPool *nodePool, BzBTNode *parent, bool parallel);
BzBTNode *bzBTCompSequence(BzObjectPool *nodePool, BzBTNode *parent, bool parallel);

BzBTNode *bzBTDecorDummy(BzObjectPool *nodePool, BzBTNode *parent);
BzBTNode *bzBTDecorSuccess(BzObjectPool *nodePool, BzBTNode *parent);
BzBTNode *bzBTDecorFail(BzObjectPool *nodePool, BzBTNode *parent);
BzBTNode *bzBTDecorInvert(BzObjectPool *nodePool, BzBTNode *parent);
BzBTNode *bzBTDecorUntilSuccess(BzObjectPool *nodePool, BzBTNode *parent);
BzBTNode *bzBTDecorUntilFail(BzObjectPool *nodePool, BzBTNode *parent);
BzBTNode *bzBTDecorRepeat(BzObjectPool *nodePool, BzBTNode *parent, i32 n);
BzBTNode *bzBTDecorDelay(BzObjectPool *nodePool, BzBTNode *parent, f32 ms);

BzBTNode *bzBTAction(BzObjectPool *nodePool, BzBTNode *parent, BzBTActionFn fn);

// Reflection data

void bzBTNodeSetName(BzBTNode *node, const char *name);
const char *bzBTNodeGetName(const BzBTNode *node);

i32 bzBTDecorGetRepeat(const BzBTNode *node);
f32 bzBTDecorGetDelay(const BzBTNode *node);

BzBTActionFn bzBTActionGetFn(const BzBTNode *node);

BzBTNodeType bzBTGetNodeType(const BzBTNode *node);
BzBTNode *bzBTNodeChild(const BzBTNode *node);
BzBTNode *bzBTNodeNext(const BzBTNode *node);

const BzBTNodeState *bzBTNodeStateNext(const BzBTNodeState *state);
bool bzBTNodeMatchesState(const BzBTNode *node, const BzBTNodeState *state);

BzBTNode *bzBTCompStateGetRunningChild(const BzBTNodeState *state);

i32 bzBTRepeatStateGetIter(const BzBTNodeState *state);
f32 bzBTDelayStateGetElapsed(const BzBTNodeState *state);

BzBTState   bzBTCreateState(const BzBTStateDesc *desc);
void        bzBTDestroyState(BzBTState *state);

BzBTStatus bzBTExecute(BzBTState *state, f32 dt);

#endif //BREEZE_BEHAVIOUR_TREE_H
