#include "cm_world_sector_private.h"

qboolean SV_inPVS(const vec3_t p1, const vec3_t p2)
{
    int32_t leafnum;
    int32_t cluster;
    int32_t area1;
    int32_t area2;
    const uint8_t *pvs;

    leafnum = CM_PointLeafnum(p1);
    cluster = CM_LeafCluster(leafnum);
    area1 = CM_LeafArea(leafnum);
    pvs = CM_ClusterPVS(cluster);

    leafnum = CM_PointLeafnum(p2);
    cluster = CM_LeafCluster(leafnum);
    area2 = CM_LeafArea(leafnum);

    /*
     * ORIGINAL_BINARY_BUG: see
     * original-bugs/coduomp-pvs-negative-cluster-underread.md. Stock does
     * not reject the second leaf's -1 cluster before indexing pvs[-1].
     */
    if (pvs != NULL &&
        ((pvs[cluster >> 3] >> (cluster & 7)) & 1) == 0) {
        return 0;
    }

    if (CM_AreasConnected(area1, area2) == 0) {
        return 0;
    }

    return 1;
}

qboolean SV_inPVSIgnorePortals(const vec3_t p1, const vec3_t p2)
{
    int32_t leafnum;
    int32_t cluster;
    const uint8_t *pvs;

    leafnum = CM_PointLeafnum(p1);
    cluster = CM_LeafCluster(leafnum);
    pvs = CM_ClusterPVS(cluster);

    leafnum = CM_PointLeafnum(p2);
    cluster = CM_LeafCluster(leafnum);

    /*
     * ORIGINAL_BINARY_BUG: see
     * original-bugs/coduomp-pvs-negative-cluster-underread.md. The same
     * signed cluster index is used here without a negative-cluster guard.
     */
    if (pvs == NULL ||
        ((pvs[cluster >> 3] >> (cluster & 7)) & 1) != 0) {
        return 1;
    }

    return 0;
}

void SV_AdjustAreaPortalState(sharedEntity_t *gentity, qboolean open)
{
    svEntity_t *svEntity = SV_SvEntityForGentity(gentity);

    if (svEntity->areaNum2 != -1) {
        CM_AdjustAreaPortalState(svEntity->areaNum, svEntity->areaNum2, open);
    }
}
