/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team

This file is part of OpenMoHAA source code.

OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// movegrid.h: Move grid
//

#include "movegrid.h"
#include "debuglines.h"

cMoveGrid::cMoveGrid(int x, int y, int z)
{
    m_iXRes = x;
    m_iYRes = y;
    m_iZRes = z;

    GridPoints = (gridpoint_t *)gi.Malloc(sizeof(gridpoint_t) * x * y * z);
}

cMoveGrid::~cMoveGrid()
{
    gi.Free(GridPoints);
}

void cMoveGrid::SetMoveInfo(vmove_t *vm)
{
    v = *vm;
}

void cMoveGrid::GetMoveInfo(vmove_t *vm)
{
    *vm = v;
}

gridpoint_t *cMoveGrid::GetGridPoint(int x, int y, int z)
{
    return &GridPoints[x * m_iZRes + y * m_iXRes + z];
}

void cMoveGrid::SetOrientation(const vec3_t *v)
{
    for (int i = 0; i < 3; i++) {
        VectorCopy(v[i], orientation[i]);
    }
}

void cMoveGrid::CalculateBoxPoints(void)
{
    int    x, y, z;
    int    rx, ry, rz;
    int    xdelta, ydelta, zdelta;
    Vector vBoxSize;

    vBoxSize = Vector(v.maxs) - Vector(v.mins);

    rx = (int)(vBoxSize.x / (float)m_iXRes);
    ry = (int)(vBoxSize.y / (float)m_iYRes);
    rz = (int)(vBoxSize.z / (float)m_iZRes);

    xdelta = ydelta = zdelta = 0;

    for (x = 0; x < m_iXRes; x++) {
        for (y = 0; y < m_iYRes; y++) {
            for (z = 0; z < m_iZRes; z++) {
                gridpoint_t *gridPoint = GetGridPoint(x, y, z);

                gridPoint->origin = Vector(xdelta - rx, ydelta - ry, zdelta + rz / 2.f);
                zdelta += rz;
            }
            zdelta = 0;
            ydelta += ry;
        }
        ydelta = 0;
        xdelta += rx;
    }

    for (x = 0; x < m_iXRes; x++) {
        for (y = 0; y < m_iYRes; y++) {
            for (z = 0; z < m_iZRes; z++) {
                Vector       vTmp;
                gridpoint_t *gridPoint = GetGridPoint(x, y, z);

                MatrixTransformVector(gridPoint->origin, orientation, vTmp);

                gridPoint->origin = vTmp;
                gridPoint->vm     = v;
                gridPoint->vm.vs  = &gridPoint->vs;

                if (rx >= ry) {
                    Vector vec;

                    vec = Vector(-ry / 2.f, -ry / 2.f, -rz / 2.f);
                    vec.copyTo(gridPoint->vm.mins);

                    vec = Vector(ry / 2.f, ry / 2.f, rz / 2.f);
                    vec.copyTo(gridPoint->vm.maxs);
                } else {
                    Vector vec;

                    vec = Vector(-rx / 2.f, -rx / 2.f, -rz / 2.f);
                    vec.copyTo(gridPoint->vm.mins);

                    vec = Vector(rx / 2.f, rx / 2.f, rz / 2.f);
                    vec.copyTo(gridPoint->vm.maxs);
                }
            }
        }
    }
}

void cMoveGrid::Move(void)
{
    gridpoint_t *gridPoint;
    int          x, y, z;
    Vector       vBoxSize;
    Vector       old_origin;
    float        best;
    float        bx, by, bz;
    Vector       bestdelta;

    old_origin = v.vs->origin;
    vBoxSize   = Vector(v.maxs) - Vector(v.mins);

    CalculateBoxPoints();

    for (x = 0; x < m_iXRes; x++) {
        for (y = 0; y < m_iYRes; y++) {
            for (z = 0; z < m_iZRes; z++) {
                gridPoint = GetGridPoint(x, y, z);

                gridPoint->origindelta = gridPoint->origin;
                gridPoint->origin += old_origin;
                gridPoint->origin.copyTo(gridPoint->vm.vs->origin);

                G_DebugBBox(gridPoint->origin, gridPoint->vm.mins, gridPoint->vm.maxs, 1, 1, 0, 1);

                VmoveSingle(&gridPoint->vm);

                gridPoint->neworigin = gridPoint->vm.vs->origin;
                gridPoint->changed   = gridPoint->neworigin - gridPoint->origin;
                gridPoint->newvel    = gridPoint->vm.vs->velocity;
                // set the ground entity
                gridPoint->groundentity = NULL;
                if (gridPoint->vm.vs->groundEntityNum != ENTITYNUM_NONE) {
                    gridPoint->groundentity = &g_entities[gridPoint->vm.vs->groundEntityNum];
                }
            }
        }
    }

    best = 99999;
    bx = by = bz = 0;

    for (x = 0; x < m_iXRes; x++) {
        for (y = 0; y < m_iYRes; y++) {
            for (z = 0; z < m_iZRes; z++) {
                gridPoint = GetGridPoint(x, y, z);
                float speed;

                speed = (gridPoint->changed - gridPoint->origindelta).length();
                if (speed < best) {
                    best = gridPoint->changed.length();
                    bx   = x;
                    by   = y;
                    bz   = z;
                }
            }
        }
    }

    gridPoint = GetGridPoint(bx, by, bz);
    bestdelta = gridPoint->neworigin - gridPoint->origindelta;
    bestdelta.copyTo(v.vs->origin);
    gridPoint->newvel.copyTo(v.vs->velocity);
}

qboolean cMoveGrid::CheckStuck(void)
{
    int     x;
    int     y;
    int     z;
    trace_t trace;

    for (x = 0; x < m_iXRes; x++) {
        for (y = 0; y < m_iYRes; y++) {
            for (z = 0; z < m_iZRes; z++) {
                gi.trace(
                    &trace, v.vs->origin, v.mins, v.maxs, v.vs->origin, v.vs->entityNum, v.tracemask, false, false
                );

                if (trace.allsolid || trace.startsolid || trace.fraction == 0.0f) {
                    return true;
                }
            }
        }
    }

    return false;
}

void cMoveGrid::Archive(Archiver& arc)
{
    int i;

    arc.ArchiveInteger(&m_iXRes);
    arc.ArchiveInteger(&m_iYRes);
    arc.ArchiveInteger(&m_iZRes);

    arc.ArchiveFloat(&v.frametime);
    arc.ArchiveFloat(&v.desired_speed);
    arc.ArchiveInteger(&v.tracemask);
    arc.ArchiveInteger(&v.numtouch);

    for (i = 0; i < ARRAY_LEN(v.touchents); i++) {
        arc.ArchiveInteger(&v.touchents[i]);
    }
    arc.ArchiveVec3(v.mins);
    arc.ArchiveVec3(v.maxs);

    arc.ArchiveVec3(orientation[0]);
    arc.ArchiveVec3(orientation[1]);
    arc.ArchiveVec3(orientation[2]);

    if (arc.Loading()) {
        if (GridPoints) {
            if (m_iXRes != 3 && m_iYRes != 3 && m_iZRes != 1) {
                gi.Free(GridPoints);
                GridPoints = (gridpoint_t *)gi.Malloc(sizeof(gridpoint_t) * m_iXRes * m_iYRes * m_iZRes);
            }
        }
    }
}
