/*
===========================================================================
Copyright (C) 2024 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
===========================================================================
*/

// tr_util.c -- renderer utility

#include "tr_local.h"
#include "../qcommon/str.h"

static byte  cntColor[4];
static float cntSt[2];

static int Numbers[12][8] = {
    {1, 3,  4,  5,  6, 7, 0, 0},
    {4, 5,  0,  0,  0, 0, 0, 0},
    {1, 4,  2,  7,  3, 0, 0, 0},
    {1, 4,  2,  5,  3, 0, 0, 0},
    {6, 4,  2,  5,  0, 0, 0, 0},
    {1, 6,  2,  5,  3, 0, 0, 0},
    {1, 6,  2,  5,  7, 3, 0, 0},
    {1, 8,  0,  0,  0, 0, 0, 0},
    {1, 2,  3,  4,  5, 6, 7, 0},
    {1, 6,  4,  2,  5, 3, 0, 0},
    {9, 10, 11, 12, 0, 0, 0, 0},
    {2, 0,  0,  0,  0, 0, 0, 0}
};

static float Lines[13][4] = {
    {0.0,  0.0, 0.0,  0.0},
    {-4.0, 8.0, 4.0,  8.0},
    {-4.0, 4.0, 4.0,  4.0},
    {-4.0, 0.0, 4.0,  0.0},
    {4.0,  8.0, 4.0,  4.0},
    {4.0,  4.0, 4.0,  0.0},
    {-4.0, 8.0, -4.0, 4.0},
    {-4.0, 4.0, -4.0, 0.0},
    {4.0,  8.0, -4.0, 0.0},
    {-1.0, 2.0, 1.0,  2.0},
    {1.0,  2.0, 1.0,  0.0},
    {-1.0, 0.0, 1.0,  0.0},
    {-1.0, 0.0, -1.0, 2.0}
};

/*
===============
RB_StreamBegin
===============
*/
void RB_StreamBegin(shader_t *shader)
{
    RB_BeginSurface(shader);
}

/*
===============
RB_StreamEnd
===============
*/
void RB_StreamEnd(void)
{
    int i;

    if (tess.numVertexes <= 2) {
        RB_EndSurface();
        return;
    }

    tess.indexes[0] = 0;
    tess.indexes[1] = 1;
    tess.indexes[2] = 2;

    for (i = 0; i < tess.numVertexes - 2; i++) {
        tess.indexes[i * 3 + 0] = (i & 1) + i;
        tess.indexes[i * 3 + 1] = i - ((i & 1) - 1);
        tess.indexes[i * 3 + 2] = i + 2;
        tess.numIndexes += 3;
    }

    RB_EndSurface();
}

/*
===============
RB_StreamBeginDrawSurf
===============
*/
void RB_StreamBeginDrawSurf(void)
{
    backEnd.dsStreamVert = tess.numVertexes;
}

/*
===============
RB_StreamEndDrawSurf
===============
*/
void RB_StreamEndDrawSurf(void)
{
    int numverts;
    int i;

    if (tess.numVertexes - backEnd.dsStreamVert <= 2) {
        tess.numVertexes = backEnd.dsStreamVert;
        return;
    }

    numverts = tess.numVertexes - backEnd.dsStreamVert - 2;
    for (i = 0; i < numverts; i++) {
        tess.indexes[i + tess.numIndexes]     = (i & 1) + i + backEnd.dsStreamVert;
        tess.indexes[i + tess.numIndexes + 1] = i + backEnd.dsStreamVert - ((i & 1) - 1);
        tess.indexes[i + tess.numIndexes + 2] = i + backEnd.dsStreamVert + 2;
        tess.numIndexes += 3;
    }
}

/*
===============
addTriangle
===============
*/
static void addTriangle(void)
{
    tess.texCoords[tess.numVertexes][0][0] = cntSt[0];
    tess.texCoords[tess.numVertexes][0][1] = cntSt[1];
    tess.vertexColors[tess.numVertexes][0] = cntColor[0];
    tess.vertexColors[tess.numVertexes][1] = cntColor[1];
    tess.vertexColors[tess.numVertexes][2] = cntColor[2];
    tess.vertexColors[tess.numVertexes][3] = cntColor[3];
    tess.vertexColorValid                  = qtrue;
    tess.numVertexes++;
}

/*
===============
RB_Vertex3fv
===============
*/
void RB_Vertex3fv(vec3_t v)
{
    VectorCopy(v, tess.xyz[tess.numVertexes]);
    addTriangle();
}

/*
===============
RB_Vertex3f
===============
*/
void RB_Vertex3f(vec_t x, vec_t y, vec_t z)
{
    tess.xyz[tess.numVertexes][0] = x;
    tess.xyz[tess.numVertexes][1] = y;
    tess.xyz[tess.numVertexes][2] = z;
    addTriangle();
}

/*
===============
RB_Vertex2f
===============
*/
void RB_Vertex2f(vec_t x, vec_t y)
{
    RB_Vertex3f(x, y, 0);
}

/*
===============
RB_Color4f
===============
*/
void RB_Color4f(vec_t r, vec_t g, vec_t b, vec_t a)
{
    cntColor[0] = r * tr.identityLightByte;
    cntColor[1] = g * tr.identityLightByte;
    cntColor[2] = b * tr.identityLightByte;
    cntColor[3] = a * 255.0;
}

/*
===============
RB_Color3f
===============
*/
void RB_Color3f(vec_t r, vec_t g, vec_t b)
{
    RB_Color4f(r, g, b, 1.0);
}

/*
===============
RB_Color3fv
===============
*/
void RB_Color3fv(vec3_t col)
{
    RB_Color3f(col[0], col[1], col[2]);
}

/*
===============
RB_Color4bv
===============
*/
void RB_Color4bv(unsigned char *colors)
{
    cntColor[0] = colors[0];
    cntColor[1] = colors[1];
    cntColor[2] = colors[2];
    cntColor[3] = colors[3];
}

/*
===============
RB_Texcoord2f
===============
*/
void RB_Texcoord2f(float s, float t)
{
    cntSt[0] = s;
    cntSt[1] = t;
}

/*
===============
RB_Texcoord2fv
===============
*/
void RB_Texcoord2fv(vec2_t st)
{
    cntSt[0] = st[0];
    cntSt[1] = st[1];
}

/*
===============
R_DrawDebugNumber
===============
*/
void R_DrawDebugNumber(const vec3_t org, float number, float scale, float r, float g, float b, int precision)
{
    vec3_t up, right;
    vec3_t pos1, pos2;
    vec3_t start;
    str    text;
    char   format[20];
    int    i;
    int    j;
    int    l;
    int    num;

    VectorCopy(tr.viewParms.ori.axis[2], up);
    VectorCopy(tr.viewParms.ori.axis[1], right);
    VectorNegate(right, right);

    VectorNormalize(up);
    VectorNormalize(right);

    VectorScale(up, scale, up);
    VectorScale(right, scale, right);

    if (precision > 0) {
        Com_sprintf(format, sizeof(format), "%%.%df", precision);
        text = va(format, number);
    } else {
        text = va("%d", (int)number);
    }

    // NOTE: this cast here is needed!
    // Otherwise the compiler will do very nasty implicit casting of negative
    // numbers to unsigned numbers, resulting in incorrect values.
    // -------------------v
    VectorMA(org, 5 - 5 * (int)text.length(), right, start);

    // Draw each character/digit of the text
    for (i = 0; i < text.length(); i++) {
        if (text[i] == '.') {
            num = 10;
        } else if (text[i] == '-') {
            num = 11;
        } else {
            num = text[i] - '0';
        }

        // Get the first line index for the number to be drawn
        l = Numbers[num][0];

        // Draw each line of the character/digit
        for (j = 0; j < 8 && l; ++j, l = Numbers[num][j]) {
            pos1[0] = start[0] + Lines[l][0] * right[0] + Lines[l][1] * up[0];
            pos1[1] = start[1] + Lines[l][0] * right[1] + Lines[l][1] * up[1];
            pos1[2] = start[2] + Lines[l][0] * right[2] + Lines[l][1] * up[2];

            pos2[0] = start[0] + Lines[l][2] * right[0] + Lines[l][3] * up[0];
            pos2[1] = start[1] + Lines[l][2] * right[1] + Lines[l][3] * up[1];
            pos2[2] = start[2] + Lines[l][2] * right[2] + Lines[l][3] * up[2];

            R_DebugLine(pos1, pos2, r, g, b, 1.0);
        }

        VectorMA(start, 10.0, right, start);
    }
}

/*
===============
R_DebugRotatedBBox
===============
*/
void R_DebugRotatedBBox(
    const vec3_t org, const vec3_t ang, const vec3_t mins, const vec3_t maxs, float r, float g, float b, float alpha
)
{
    int    i;
    vec3_t tmp;
    vec3_t points[8];
    vec3_t axis[3];

    AnglesToAxis(ang, axis);

    for (i = 0; i < 8; i++) {
        if (i & 1) {
            tmp[0] = mins[0];
        } else {
            tmp[0] = maxs[0];
        }

        if (i & 2) {
            tmp[1] = mins[1];
        } else {
            tmp[1] = maxs[1];
        }

        if (i & 4) {
            tmp[2] = mins[2];
        } else {
            tmp[2] = maxs[2];
        }

        points[i][0] = tmp[0] * axis[0][0];
        points[i][1] = tmp[0] * axis[0][1];
        points[i][2] = tmp[0] * axis[0][2];

        points[i][0] += tmp[1] * axis[1][0];
        points[i][1] += tmp[1] * axis[1][1];
        points[i][2] += tmp[1] * axis[1][2];

        points[i][0] += tmp[2] * axis[2][0];
        points[i][1] += tmp[2] * axis[2][1];
        points[i][2] += tmp[2] * axis[2][2];

        points[i][0] += org[0];
        points[i][1] += org[1];
        points[i][2] += org[2];
    }

    R_DebugLine(points[0], points[1], r, g, b, alpha);
    R_DebugLine(points[1], points[3], r, g, b, alpha);
    R_DebugLine(points[3], points[2], r, g, b, alpha);
    R_DebugLine(points[2], points[0], r, g, b, alpha);
    R_DebugLine(points[4], points[5], r, g, b, alpha);
    R_DebugLine(points[5], points[7], r, g, b, alpha);
    R_DebugLine(points[7], points[6], r, g, b, alpha);
    R_DebugLine(points[6], points[4], r, g, b, alpha);

    for (i = 0; i < 4; i++) {
        R_DebugLine(points[i], points[i + 4], r, g, b, alpha);
    }
}

/*
===============
RE_GetShaderWidth
===============
*/
int RE_GetShaderWidth(qhandle_t hShader)
{
    shader_t *shader;

    if (hShader) {
        shader = R_GetShaderByHandle(hShader);
    } else {
        shader = tr.defaultShader;
    }

    return shader->unfoggedStages[0]->bundle[0].image[0]->uploadWidth;
}

/*
===============
RE_GetShaderHeight
===============
*/
int RE_GetShaderHeight(qhandle_t hShader)
{
    shader_t *shader;

    if (hShader) {
        shader = R_GetShaderByHandle(hShader);
    } else {
        shader = tr.defaultShader;
    }

    return shader->unfoggedStages[0]->bundle[0].image[0]->uploadHeight;
}

/*
===============
RE_GetShaderName
===============
*/
const char *RE_GetShaderName(qhandle_t hShader)
{
    shader_t *shader;

    if (hShader) {
        shader = R_GetShaderByHandle(hShader);
    } else {
        shader = tr.defaultShader;
    }

    return shader->name;
}

/*
===============
RE_GetModelName
===============
*/
const char *RE_GetModelName(qhandle_t hModel)
{
    model_t *model;

    if (!hModel) {
        return "";
    }

    model = R_GetModelByHandle(hModel);

    return model->name;
}
