#include "components.h"

#include "ai_actions.h"

ECS_TAG_DECLARE(GameEntity);

ECS_COMPONENT_DECLARE(Resource);

ECS_COMPONENT_DECLARE(Owner);

ECS_COMPONENT_DECLARE(SpatialGridID);
ECS_COMPONENT_DECLARE(Position);
ECS_COMPONENT_DECLARE(Size);
ECS_COMPONENT_DECLARE(Velocity);
ECS_COMPONENT_DECLARE(Rotation);
ECS_COMPONENT_DECLARE(Orientation);
ECS_COMPONENT_DECLARE(Steering);
ECS_COMPONENT_DECLARE(TargetPosition);
ECS_COMPONENT_DECLARE(Path);

ECS_COMPONENT_DECLARE(TextureRegion);

ECS_COMPONENT_DECLARE(Animation);
ECS_COMPONENT_DECLARE(Easing);

ECS_COMPONENT_DECLARE(Arms);
ECS_COMPONENT_DECLARE(Arm);
ECS_COMPONENT_DECLARE(BzBTState);
ECS_COMPONENT_DECLARE(AIBlackboard);

ECS_TAG_DECLARE(Selectable);
ECS_TAG_DECLARE(Selected);

ECS_COMPONENT_DECLARE(Worker);
ECS_COMPONENT_DECLARE(Building);
ECS_COMPONENT_DECLARE(Unit);
ECS_TAG_DECLARE(Storage);
ECS_TAG_DECLARE(Harvestable);
ECS_TAG_DECLARE(Buildable);
ECS_TAG_DECLARE(Workable);
ECS_TAG_DECLARE(Attackable);

void initComponentIDs(ecs_world_t *ecs) {
    ECS_TAG_DEFINE(ecs, GameEntity);

    ECS_COMPONENT_DEFINE(ecs, Resource);

    ECS_COMPONENT_DEFINE(ecs, Owner);

    ECS_COMPONENT_DEFINE(ecs, SpatialGridID);
    ECS_COMPONENT_DEFINE(ecs, Position);
    ECS_COMPONENT_DEFINE(ecs, Size);
    ECS_COMPONENT_DEFINE(ecs, Velocity);
    ECS_COMPONENT_DEFINE(ecs, Rotation);
    ECS_COMPONENT_DEFINE(ecs, Orientation);
    ECS_COMPONENT_DEFINE(ecs, Steering);
    ECS_COMPONENT_DEFINE(ecs, TargetPosition);
    ECS_COMPONENT_DEFINE(ecs, Path);

    ECS_COMPONENT_DEFINE(ecs, TextureRegion);

    ECS_COMPONENT_DEFINE(ecs, Animation);
    ECS_COMPONENT_DEFINE(ecs, Easing);

    ECS_COMPONENT_DEFINE(ecs, Arms);
    ECS_COMPONENT_DEFINE(ecs, Arm);
    ECS_COMPONENT_DEFINE(ecs, BzBTState);
    ECS_COMPONENT_DEFINE(ecs, AIBlackboard);

    ECS_TAG_DEFINE(ecs, Selectable);
    ECS_TAG_DEFINE(ecs, Selected);

    ECS_COMPONENT_DEFINE(ecs, Worker);
    ECS_COMPONENT_DEFINE(ecs, Building);
    ECS_COMPONENT_DEFINE(ecs, Unit);
    ECS_TAG_DEFINE(ecs, Storage);
    ECS_TAG_DEFINE(ecs, Harvestable);
    ECS_TAG_DEFINE(ecs, Buildable);
    ECS_TAG_DEFINE(ecs, Workable);
    ECS_TAG_DEFINE(ecs, Attackable);

}

#include <rlImGui.h>
void igTagCheckbox(const char *label, ecs_world_t *ecs,
                   ecs_entity_t entity, ecs_entity_t tag) {
    bool hasTag = ecs_has_id(ecs, entity, tag);
    igCheckbox(label, &hasTag);
    if (hasTag)
        ecs_add_id(ecs, entity, tag);
    else
        ecs_remove_id(ecs, entity, tag);
}
void igResource(ecs_world_t *ecs,
                ecs_entity_t entity, ecs_entity_t comp) {
    Resource *res = ecs_get_mut_id(ecs, entity, comp);
    const char *resStrings[RES_COUNT];
    for (i32 i = 0; i < RES_COUNT; i++) {
        resStrings[i] = getResourceTypePrettyName(i);
    }
    int curType = res->type;
    igCombo_Str_arr("Type", &curType, resStrings, RES_COUNT, -1);
    res->type = curType;
    igInputInt("Amount", &res->amount, 1, 10, 0);
}
void igOwner(ecs_world_t *ecs,
             ecs_entity_t entity, ecs_entity_t comp) {
    Owner *owner = ecs_get_mut_id(ecs, entity, comp);
    igInputInt("PlayerID", &owner->playerID, 0, 0, 0);
}

void igSpatialGridID(ecs_world_t *ecs,
                     ecs_entity_t entity, ecs_entity_t comp) {
    SpatialGridID *id = ecs_get_mut_id(ecs, entity, comp);
    igText("SpatialID", *id);
}
void igVec2(Vector2 *vec) {
    igInputFloat("X", &vec->x, 1.0f, 10.0f, "%.2f", 0);
    igInputFloat("Y", &vec->y, 1.0f, 10.0f, "%.2f", 0);
}
void igVec2Comp(ecs_world_t *ecs,
                ecs_entity_t entity, ecs_entity_t comp) {
    Vector2 *vec = ecs_get_mut_id(ecs, entity, comp);
    igVec2(vec);
}
void igFloat(ecs_world_t *ecs,
             ecs_entity_t entity, ecs_entity_t comp) {
    f32 *f = ecs_get_mut_id(ecs, entity, comp);
    igInputFloat("", f, 0.1f, 1.0f, "%.2f", 0);
}

void igPath(ecs_world_t *ecs,
            ecs_entity_t entity, ecs_entity_t comp) {
    Path path = *(Path *) ecs_get_mut_id(ecs, entity, comp);
    i32 idx = 0;
    while (path.paths) {
        for (int32_t i = path.curWaypoint; i < path.paths->numWaypoints; i++) {
            igPushID_Int(idx);
            igText("Waypoint %d:", idx);
            igInputFloat("X", &path.paths->waypoints[i].x, 1, 16, "%.2f", 0);
            igInputFloat("Y", &path.paths->waypoints[i].y, 1, 16, "%.2f", 0);
            igPopID();
            idx++;
        }
        path.paths = path.paths->next;
        path.curWaypoint = 0;
    }
}

void igRect(Rectangle *rect) {
    igInputFloat("X", &rect->x, 1, 16, "%.2f", 0);
    igInputFloat("Y", &rect->y, 1, 16, "%.2f", 0);
    igInputFloat("Width", &rect->width, 1, 16, "%.2f", 0);
    igInputFloat("Height", &rect->height, 1, 16, "%.2f", 0);
}
void igTextureRegion(ecs_world_t *ecs,
                     ecs_entity_t entity, ecs_entity_t comp) {
    TextureRegion *tex = ecs_get_mut_id(ecs, entity, comp);
    igText("Texture: %d", tex->texture.id);
    igRect(&tex->rec);
    bool flipX = tex->flipX;
    bool flipY = tex->flipY;
    igCheckbox("flipX", &flipX);
    igCheckbox("flipY", &flipY);
    tex->flipX = flipX;
    tex->flipY = flipY;
}

static void igEntityType(EntityType *type) {
    const char *prettyStrings[ENTITY_COUNT];
    for (int i = 0; i < ENTITY_COUNT; i++) {
        prettyStrings[i] = getEntityStr(i);
    }
    igCombo_Str_arr("EntityType", type, prettyStrings, ENTITY_COUNT, -1);
}
static void igAnimType(AnimType *type) {
    const char *prettyStrings[ANIM_COUNT];
    for (int i = 0; i < ANIM_COUNT; i++) {
        prettyStrings[i] = getEntityAnimationStr(i);
    }
    igCombo_Str_arr("AnimType", type, prettyStrings, ANIM_COUNT, -1);
}
void igAnimation(ecs_world_t *ecs,
                 ecs_entity_t entity, ecs_entity_t comp) {
    Animation *anim = ecs_get_mut_id(ecs, entity, comp);

    EntityType curEntityType = anim->entityType;
    igEntityType(&curEntityType);
    anim->entityType = curEntityType;

    AnimType curAnimType = anim->animType;
    igAnimType(&curAnimType);
    anim->animType = curAnimType;

    igText("tileset: %p", anim->tileset);
    igText("curFrame: %d", anim->curFrame);
    igText("elapsed: %.2f < %.2F", anim->elapsed, anim->frame.duration);
}
void igEasing(ecs_world_t *ecs,
              ecs_entity_t entity, ecs_entity_t comp) {
    Easing *easing = ecs_get_mut_id(ecs, entity, comp);
    igText("Component: %d, offset: %d", easing->compID, easing->offset);
    igText("x = %.2f + %.2f * (%.2f + (%.2f * %.2f) + %.2f) + %.2f",
           easing->start, easing->target, easing->easeStart, easing->easeTarget,
           easing->x, easing->easeOffset, easing->offset);
}

void igArms(ecs_world_t *ecs,
            ecs_entity_t entity, ecs_entity_t comp) {

}
void igArm(ecs_world_t *ecs,
           ecs_entity_t entity, ecs_entity_t comp) {

}

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

    bool hasState = bzBTNodeMatchesState(node, state);
    isActive |= hasState;

    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;
    }


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

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

    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++;
    }

    bool isComposite = type == BZ_BT_COMP_SELECTOR ||
            type == BZ_BT_COMP_PARALLEL_SELECTOR ||
            type == BZ_BT_COMP_SEQUENCE ||
            type == BZ_BT_COMP_PARALLEL_SEQUENCE;

    while (child) {
        if (hasSingleChild) igSameLine(0, 0);
        bool childActive = isActive && hasSingleChild;
        if (hasState && isComposite && !childActive)
            childActive = bzBTCompStateGetRunningChild(state) == child;
        const BzBTNodeState *childState = state;
        if (hasState)
            childState = bzBTNodeStateNext(state);
        igVisualizeBTState(child, childState, childActive, hasSingleChild, depth);
        child = bzBTNodeNext(child);
    }
}
void igBzBTState(ecs_world_t *ecs,
                 ecs_entity_t entity, ecs_entity_t comp) {

}
void igAIBlackboard(ecs_world_t *ecs,
                    ecs_entity_t entity, ecs_entity_t comp) {

}

void igWorker(ecs_world_t *ecs,
              ecs_entity_t entity, ecs_entity_t comp) {

}
void igUnit(ecs_world_t *ecs,
            ecs_entity_t entity, ecs_entity_t comp) {

}
