#define VR_ONE_FRIENDLY_UNIT    0
#define VR_UPDATE_FRIENDLY_UNIT 1
#define VR_FRIENDLY_UNIT_DIED   3
#define VR_ONE_ENEMY_UNIT               4
#define VR_ALL_UNITS                    5

class MPCtrl1
{
        I64   mode, lo, hi;
        Unit *tmpu;
};

class MPCtrl2
{
        I64 lo, hi, row, col;
};


U0 VRSetUp(I64 player)
{
        I64 i;
        Unit *ut0, *ut1;

        ut0 = &units[player][0];
        ut1 = &units[player ^ 1][0];
        for (i = 0; i < UNITS_NUM; i++, ut0++, ut1++)
        {
                LBtr(&ut1->vis[player], 0);
                LBEqual(&ut0->vis[player], 0, ut0->life > 0);
        }
}

U0 VRMerge(I64 player)
{
        I64   i, j;
        Unit *ut1;
        U8       *dst, *src, *mask = CAlloc((UNITS_NUM + 7) >> 3);

        for (j = 0; j < UNITS_NUM; j++) {//p0
                src = &vis_unit_bitmap[player][(((UNITS_NUM + 7) & ~7)*j) >> 3];
                dst = mask;
                for (i = 0; i < (UNITS_NUM + 7) >> 3; i++) //player1
                        *dst++ |= *src++;
        }
        ut1 = &units[player ^ 1][0];
        for (j = 0; j < UNITS_NUM; j++, ut1++)
                LBEqual(&ut1->vis[player], 0, Bt(mask, j) && ut1->life > 0);
        Free(mask);
}

Bool MPVisRecalc(MPCtrl1 *job)
{
        Bool  res = FALSE, seen;
        I64   i, j, row, col;
        F64   x1, y1, x2, y2, dd, range;
        Unit *ut0, *ut1;

        ut0 = &units[cur_player][job->lo];
        ut1 = &units[enemy_player][job->lo];
        if (job->tmpu) {
                row = job->tmpu->row;
                col = job->tmpu->col;
                range = job->tmpu->range * 2 * HEX_RADIUS;
                range *= range;
        }
        switch (job->mode)
        {
                case VR_UPDATE_FRIENDLY_UNIT:
                case VR_ONE_FRIENDLY_UNIT:
                        if (job->mode == VR_UPDATE_FRIENDLY_UNIT)
                                range = F64_MAX;
                        RowCol2XY(&x1, &y1, row, col);
                        for (i = job->lo; i < job->hi; i++, ut1++)
                        {
                                seen = FALSE;
                                if (ut1->life > 0 && LOS(row, col, ut1->row, ut1->col))
                                {
                                        RowCol2XY(&x2, &y2, ut1->row, ut1->col);
                                        dd = Sqr(x2 - x1) + Sqr(y2 - y1);
                                        if (dd < range)
                                        {
                                                seen = TRUE;
                                                LBts(&ut1->vis[cur_player], 0);
                                        }
                                }
                                if (job->mode == VR_UPDATE_FRIENDLY_UNIT)
                                        LBEqual(&vis_unit_bitmap[cur_player], i + job->tmpu->num * ((UNITS_NUM + 7) & ~7), seen);
                        }
                        break;

                case VR_ONE_ENEMY_UNIT:
                        RowCol2XY(&x1, &y1, row, col);
                        for (i = job->lo; i < job->hi; i++, ut1++)
                                if (ut1->life > 0 && LOS(row, col, ut1->row, ut1->col))
                                {
                                        LBts(&vis_unit_bitmap[enemy_player], job->tmpu->num + i * ((UNITS_NUM + 7) & ~7));
                                        res = TRUE;
                                }
                                else
                                        LBtr(&vis_unit_bitmap[enemy_player], job->tmpu->num + i * ((UNITS_NUM + 7) & ~7));
                        break;

                case VR_ALL_UNITS:
                        ut0 = &units[cur_player][0];
                        for (i = 0; i < UNITS_NUM; i++, ut0++)
                                if (ut0->life > 0)
                                {
                                        RowCol2XY(&x1, &y1, ut0->row, ut0->col);
                                        ut1 = &units[enemy_player][job->lo];
                                        for (j = job->lo; j < job->hi; j++, ut1++)
                                        {
                                                if (ut1->life > 0 && LOS(ut0->row, ut0->col, ut1->row, ut1->col))
                                                {
                                                        LBts(&ut1->vis[cur_player], 0);
                                                        LBts(&vis_unit_bitmap[cur_player], j + i * ((UNITS_NUM + 7) & ~7));
                                                }
                                                else
                                                        LBtr(&vis_unit_bitmap[cur_player], j + i * ((UNITS_NUM + 7) & ~7));
                                        }
                                }
                                else
                                        for (j = job->lo; j < job->hi ;j++)
                                                LBtr(&vis_unit_bitmap[cur_player], j + i * ((UNITS_NUM + 7) & ~7));
                        ut0 = &units[enemy_player][0];
                        for (i = 0; i < UNITS_NUM; i++, ut0++)
                                if (ut0->life > 0)
                                {
                                        RowCol2XY(&x1, &y1, ut0->row, ut0->col);
                                        ut1 = &units[cur_player][job->lo];
                                        for (j = job->lo; j < job->hi; j++, ut1++)
                                        {
                                                if (ut1->life > 0 && LOS(ut0->row, ut0->col, ut1->row, ut1->col))
                                                {
                                                        LBts(&ut1->vis[enemy_player], 0);
                                                        LBts(&vis_unit_bitmap[enemy_player], j + i * ((UNITS_NUM + 7) & ~7));
                                                }
                                                else
                                                        LBtr(&vis_unit_bitmap[enemy_player], j + i * ((UNITS_NUM + 7) & ~7));
                                        }
                                }
                                else
                                        for (j = job->lo; j < job->hi; j++)
                                                LBtr(&vis_unit_bitmap[enemy_player], j + i * ((UNITS_NUM + 7) & ~7));
                        break;
        }

        return res;
}

Bool VisRecalc(I64 mode, Unit *tmpu=NULL)
{
        I64  i, hi, k, count;
        Bool res;
/*
The compiler doesn't go out of it's way
to know if something is const.;-)  This
just compiles with the val at compile
time, an advantage of just-in-time over
AOT binaries.  ZealOS has a limited
stack size, so don't get in the habit.
MAlloc() would probably be the better choice.
*/
        MPCtrl1  job[mp_count];
        CJob    *cmd[mp_count];

        if (mode == VR_FRIENDLY_UNIT_DIED)
        {
                MemSet((&vis_unit_bitmap[enemy_player])(U8 *) + (tmpu->num * ((UNITS_NUM + 7) & ~7)) >> 3, 0, (UNITS_NUM + 7) >> 3);
                VRMerge(enemy_player);
                return 0; //Return any value--don't care
        }

        count = mp_count; //Cores
        hi = UNITS_NUM;
        if (mode == VR_ONE_ENEMY_UNIT)
        {
                for (hi--; hi >= 0; hi--)
                        if (units[enemy_player][hi].life > 0)
                                break;
                hi++;
        }
        k = hi;
        if (hi / mp_count < 2)
                count = 1;
        for (i = 0; i < count; i++)
        {
                job[i].mode = mode;
                job[i].tmpu = tmpu;
                job[i].hi = k;
                k -= hi / count;
                if (k < 0)
                        k = 0;
                if (i == count - 1)
                        k = 0;
                job[i].lo = k;
        }

        res = FALSE;
        for (i = 0; i < count; i++)
                cmd[i] = JobQueue(&MPVisRecalc, &job[i], i, 0);
        for (i = 0; i < count; i++)
                if (JobResGet(cmd[i]))
                        res = TRUE;
        if (mode == VR_UPDATE_FRIENDLY_UNIT)
                VRMerge(cur_player);

        return res;
}

U0 MPVisRecalcMap(MPCtrl2 *job)
{
        I64 i, j;

        for (j = job->lo; j < job->hi; j++)
                for (i = 0; i < map_cols; i++)
                        if (LOS(job->row, job->col, j, i))
                                vis_map[j][i] = TRUE;
                        else
                                vis_map[j][i] = FALSE;
}

U0 VisRecalcMap(I64 row, I64 col)
{
        I64              i, hi, k, count;
        MPCtrl2  job[mp_count];
        CJob    *cmd[mp_count];

        count = mp_count; //Cores
        hi = map_rows;
        k = hi;
        if (hi / mp_count < 2)
                count = 1;
        for (i = 0; i < count; i++)
        {
                job[i].row = row;
                job[i].col = col;
                job[i].hi  = k;
                k -= hi/count;
                if (k < 0)
                        k = 0;
                if (i == count - 1)
                        k = 0;
                job[i].lo=k;
        }
        for (i = 0; i < count; i++)
                cmd[i] = JobQueue(&MPVisRecalcMap, &job[i], i, 0);
        for (i = 0; i < count; i++)
                JobResGet(cmd[i]);
}