#include "cm_world_sector_private.h"

void CM_RebucketWorldSectorLinks(worldSector_t *sector,
                                 const float mins[2],
                                 const float maxs[2])
{
    int32_t axis;
    float dist;
    svEntity_t *previousEntityLink;
    svEntity_t *entityLink;
    svEntity_t *nextEntityLink;
    worldSector_t *childSector;
    worldSectorAreaLink_t *previousAreaLink;
    worldSectorAreaLink_t *areaLink;
    worldSectorAreaLink_t *nextAreaLink;

    axis = sector->axis;
    dist = sector->dist;

    previousEntityLink = NULL;
    entityLink = CM_LoadEntityLink(sector->entityLinkHead);
    while (entityLink != NULL) {
        if (dist < entityLink->linkMins[axis]) {
            childSector = CM_LoadWorldSector(sector->children[0]);
            if (childSector == &cm_nullWorldSector) {
                childSector = CM_AllocWorldSector(mins, maxs);
                if (childSector == NULL) {
                    return;
                }
                sector->children[0] = CM_StoreWorldSector(childSector);
                childSector->parent = CM_StoreWorldSector(sector);
            }

            nextEntityLink =
                CM_LoadEntityLink(entityLink->nextInWorldSector);
            entityLink->worldSector = CM_StoreWorldSector(childSector);
            entityLink->nextInWorldSector = childSector->entityLinkHead;
            childSector->entityLinkHead = CM_StoreEntityLink(entityLink);
            childSector->entityContentsMask =
                SV_GEntityForSvEntity(entityLink)->contents |
                childSector->entityContentsMask;

            if (previousEntityLink == NULL) {
                sector->entityLinkHead = CM_StoreEntityLink(nextEntityLink);
            } else {
                previousEntityLink->nextInWorldSector =
                    CM_StoreEntityLink(nextEntityLink);
            }
            entityLink = nextEntityLink;
        } else if (entityLink->linkMaxs[axis] < dist) {
            childSector = CM_LoadWorldSector(sector->children[1]);
            if (childSector == &cm_nullWorldSector) {
                childSector = CM_AllocWorldSector(mins, maxs);
                if (childSector == NULL) {
                    return;
                }
                sector->children[1] = CM_StoreWorldSector(childSector);
                childSector->parent = CM_StoreWorldSector(sector);
            }

            nextEntityLink =
                CM_LoadEntityLink(entityLink->nextInWorldSector);
            entityLink->worldSector = CM_StoreWorldSector(childSector);
            entityLink->nextInWorldSector = childSector->entityLinkHead;
            childSector->entityLinkHead = CM_StoreEntityLink(entityLink);
            childSector->entityContentsMask =
                SV_GEntityForSvEntity(entityLink)->contents |
                childSector->entityContentsMask;

            if (previousEntityLink == NULL) {
                sector->entityLinkHead = CM_StoreEntityLink(nextEntityLink);
            } else {
                previousEntityLink->nextInWorldSector =
                    CM_StoreEntityLink(nextEntityLink);
            }
            entityLink = nextEntityLink;
        } else {
            previousEntityLink = entityLink;
            entityLink = CM_LoadEntityLink(entityLink->nextInWorldSector);
        }
    }

    previousAreaLink = NULL;
    areaLink = CM_LoadAreaLink(sector->staticModelLinkHead);
    while (areaLink != NULL) {
        if (dist < areaLink->linkMins[axis]) {
            childSector = CM_LoadWorldSector(sector->children[0]);
            if (childSector == &cm_nullWorldSector) {
                childSector = CM_AllocWorldSector(mins, maxs);
                if (childSector == NULL) {
                    return;
                }
                sector->children[0] = CM_StoreWorldSector(childSector);
                childSector->parent = CM_StoreWorldSector(sector);
            }

            nextAreaLink = CM_LoadAreaLink(areaLink->nextInWorldSector);
            areaLink->nextInWorldSector = childSector->staticModelLinkHead;
            childSector->staticModelLinkHead = CM_StoreAreaLink(areaLink);
            childSector->staticModelContentsMask =
                XModelGetContents(areaLink->model) |
                childSector->staticModelContentsMask;
            if (areaLink->sightTraceEligible != qfalse) {
                childSector->sightTraceStaticModelContentsMask =
                    XModelGetContents(areaLink->model) |
                    childSector->sightTraceStaticModelContentsMask;
            }

            if (previousAreaLink == NULL) {
                sector->staticModelLinkHead =
                    CM_StoreAreaLink(nextAreaLink);
            } else {
                previousAreaLink->nextInWorldSector =
                    CM_StoreAreaLink(nextAreaLink);
            }
            areaLink = nextAreaLink;
        } else if (areaLink->linkMaxs[axis] < dist) {
            childSector = CM_LoadWorldSector(sector->children[1]);
            if (childSector == &cm_nullWorldSector) {
                childSector = CM_AllocWorldSector(mins, maxs);
                if (childSector == NULL) {
                    return;
                }
                sector->children[1] = CM_StoreWorldSector(childSector);
                childSector->parent = CM_StoreWorldSector(sector);
            }

            nextAreaLink = CM_LoadAreaLink(areaLink->nextInWorldSector);
            areaLink->nextInWorldSector = childSector->staticModelLinkHead;
            childSector->staticModelLinkHead = CM_StoreAreaLink(areaLink);
            childSector->staticModelContentsMask =
                XModelGetContents(areaLink->model) |
                childSector->staticModelContentsMask;
            if (areaLink->sightTraceEligible != qfalse) {
                childSector->sightTraceStaticModelContentsMask =
                    XModelGetContents(areaLink->model) |
                    childSector->sightTraceStaticModelContentsMask;
            }

            if (previousAreaLink == NULL) {
                sector->staticModelLinkHead =
                    CM_StoreAreaLink(nextAreaLink);
            } else {
                previousAreaLink->nextInWorldSector =
                    CM_StoreAreaLink(nextAreaLink);
            }
            areaLink = nextAreaLink;
        } else {
            previousAreaLink = areaLink;
            areaLink = CM_LoadAreaLink(areaLink->nextInWorldSector);
        }
    }
}
