/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
Copyright (C) 2020-2021 Vittorio Romeo

This program 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.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#pragma once

#include <GL/glew.h>

#include "host.hpp"
#include "modelgen.hpp"
#include "spritegn.hpp"
#include "efrag.hpp"
#include "quakeglm_qvec3.hpp"
#include "vr_macros.hpp"
#include "zone.hpp"
#include "quakedef_macros.hpp"
#include "bspfile.hpp"
#include "modeleffects.hpp"

/*

d*_t structures are on-disk representations
m*_t structures are in-memory

*/

/*
==============================================================================

BRUSH MODELS

==============================================================================
*/


//
// in memory representation
//
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct
{
    qvec3 position;
} mvertex_t;

#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2


// plane_t structure
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct mplane_s
{
    qvec3 normal;
    float dist;
    byte type;     // for texture axis selection and fast side tests
    byte signbits; // signx + signy<<1 + signz<<1
    byte pad[2];
} mplane_t;

// ericw -- each texture has two chains, so we can clear the model chains
//          without affecting the world
typedef enum
{
    chain_world = 0,
    chain_model = 1
} texchain_t;


struct gltexture_t;

struct texture_t
{
    char name[16];
    unsigned width, height;
    gltexture_t* gltexture;              // johnfitz -- pointer to gltexture
    gltexture_t* fullbright;             // johnfitz -- fullbright mask texture
    gltexture_t* warpimage;              // johnfitz -- for water animation
    bool update_warp;                    // johnfitz -- update warp this frame
    struct msurface_s* texturechains[2]; // for texture chains
    int anim_total;                      // total tenths in sequence ( 0 = no)
    int anim_min, anim_max;              // time for this frame min <=time< max
    texture_t* anim_next;                // in the animation sequence
    texture_t* alternate_anims;          // bmodels in frmae 1 use these
};


#define SURF_PLANEBACK 2
#define SURF_DRAWSKY 4
#define SURF_DRAWSPRITE 8
#define SURF_DRAWTURB 0x10
#define SURF_DRAWTILED 0x20
#define SURF_DRAWBACKGROUND 0x40
#define SURF_UNDERWATER 0x80
#define SURF_NOTEXTURE 0x100 // johnfitz
#define SURF_DRAWFENCE 0x200
#define SURF_DRAWLAVA 0x400
#define SURF_DRAWSLIME 0x800
#define SURF_DRAWTELE 0x1000
#define SURF_DRAWWATER 0x2000

// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct
{
    unsigned int v[2];
    unsigned int cachededgeoffset;
} medge_t;

typedef struct
{
    float vecs[2][4];
    float mipadjust;
    texture_t* texture;
    int flags;
} mtexinfo_t;

#define VERTEXSIZE 7

typedef struct glpoly_s
{
    struct glpoly_s* next;
    struct glpoly_s* chain;
    int numverts;
    float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2)
} glpoly_t;

typedef struct msurface_s
{
    int visframe; // should be drawn when node is crossed
    bool culled;  // johnfitz -- for frustum culling
    qvec3 mins;   // johnfitz -- for frustum culling
    qvec3 maxs;   // johnfitz -- for frustum culling

    mplane_t* plane;
    int flags;

    int firstedge; // look up in model->surfedges[], negative numbers
    int numedges;  // are backwards edges

    short texturemins[2];
    short extents[2];

    int light_s, light_t; // gl lightmap coordinates
    unsigned char lmshift;

    glpoly_t* polys; // multiple if warped
    struct msurface_s* texturechain;

    mtexinfo_t* texinfo;

    int vbo_firstvert; // index of this surface's first vert in the VBO

    // lighting info
    int dlightframe;
    unsigned int dlightbits[(MAX_DLIGHTS + 31) >> 5];
    // int is 32 bits, need an array for MAX_DLIGHTS > 32

    int lightmaptexturenum;
    unsigned short styles[MAXLIGHTMAPS];
    int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap
    bool cached_dlight;             // true if dynamic light in cache
    byte* samples;                  // [numstyles*surfsize]
} msurface_t;

struct mnode_t
{
    // common with leaf
    int contents; // 0, to differentiate from leafs
    int visframe; // node needs to be traversed if current

    float minmaxs[6]; // for bounding box culling

    struct mnode_t* parent;

    // node specific
    mplane_t* plane;
    struct mnode_t* children[2];

    unsigned int firstsurface;
    unsigned int numsurfaces;
};



typedef struct mleaf_s
{
    // common with node
    int contents; // wil be a negative contents number
    int visframe; // node needs to be traversed if current

    float minmaxs[6]; // for bounding box culling

    mnode_t* parent;

    // leaf specific
    byte* compressed_vis;
    efrag_t* efrags;

    msurface_t** firstmarksurface;
    int nummarksurfaces;
    int key; // BSP sequence number for leaf's contents
    byte ambient_sound_level[NUM_AMBIENTS];
} mleaf_t;

// johnfitz -- for clipnodes>32k
typedef struct mclipnode_s
{
    int planenum;
    int children[2]; // negative numbers are contents
} mclipnode_t;
// johnfitz

// !!! if this is changed, it must be changed in asm_i386.h too !!!
struct hull_t
{
    mclipnode_t* clipnodes; // johnfitz -- was dclipnode_t
    mplane_t* planes;
    int firstclipnode;
    int lastclipnode;
    qvec3 clip_mins;
    qvec3 clip_maxs;
};

/*
==============================================================================

SPRITE MODELS

==============================================================================
*/


// FIXME: shorten these?
typedef struct mspriteframe_s
{
    int width, height;
    qfloat up, down, left, right;
    qfloat smax, tmax; // johnfitz -- image might be padded
    gltexture_t* gltexture;
} mspriteframe_t;

typedef struct
{
    int numframes;
    float* intervals;
    mspriteframe_t* frames[1];
} mspritegroup_t;

typedef struct
{
    spriteframetype_t type;
    mspriteframe_t* frameptr;
} mspriteframedesc_t;

typedef struct
{
    int type;
    int maxwidth;
    int maxheight;
    int numframes;
    float beamlength; // remove?
    void* cachespot;  // remove?
    mspriteframedesc_t frames[1];
} msprite_t;


/*
==============================================================================

ALIAS MODELS

Alias models are position independent, so the cache manager can move them.
==============================================================================
*/

//-- from RMQEngine
// split out to keep vertex sizes down
typedef struct aliasmesh_s
{
    float st[2];
    unsigned short vertindex;
} aliasmesh_t;

typedef struct meshxyz_mdl_s
{
    byte xyz[4];
    signed char normal[4];
} meshxyz_mdl_t;

typedef struct meshxyz_mdl16_s
{
    unsigned short xyz[4];
    signed char normal[4];
} meshxyz_mdl16_t;

typedef struct meshxyz_md3_s
{
    signed short xyz[4];
    signed char normal[4];
} meshxyz_md3_t;

typedef struct meshxyz_s
{
    byte xyz[4];
    signed char normal[4];
} meshxyz_t;

typedef struct meshst_s
{
    float st[2];
} meshst_t;
//--

typedef struct
{
    int firstpose;
    int numposes;
    float interval;
    trivertx_t bboxmin;
    trivertx_t bboxmax;
    //	int					frame;	//spike - this was redundant
    char name[16];
} maliasframedesc_t;

typedef struct
{
    trivertx_t bboxmin;
    trivertx_t bboxmax;
    int frame;
} maliasgroupframedesc_t;

typedef struct
{
    int numframes;
    int intervals;
    maliasgroupframedesc_t frames[1];
} maliasgroup_t;

// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct mtriangle_s
{
    int facesfront;
    int vertindex[3];
} mtriangle_t;


#define MAX_SKINS 32
struct aliashdr_t
{
    int ident;
    int version;
    qvec3 scale;
    qvec3 scale_origin;
    float boundingradius;
    qvec3 eyeposition;
    int numskins;
    int skinwidth;
    int skinheight;
    int numverts;
    int numtris;
    int numframes;
    synctype_t synctype;
    int flags;
    float size;

    // For vr - we modify these, so keep the originals
    qvec3 original_scale;
    qvec3 original_scale_origin;

    // ericw -- used to populate vbo
    int numverts_vbo;  // number of verts with unique x,y,z,s,t
    intptr_t meshdesc; // offset into extradata: numverts_vbo aliasmesh_t
    int numindexes;
    intptr_t indexes; // offset into extradata: numindexes unsigned shorts
    intptr_t
        vertexes; // offset into extradata: numposes*vertsperframe trivertx_t

    intptr_t vbovertofs;
    intptr_t vbostofs;
    intptr_t eboofs;
    // ericw --

    intptr_t nextsurface; // spike
    int nummorphposes;    // spike -- renamed from numposes
    int poseverts;
    int posedata;          // numposes*poseverts trivert_t
    int commands;          // gl command list with embedded s/t
    int numboneposes;      // spike -- for iqm
    int numbones;          // spike -- for iqm
    intptr_t boneinfo;     // spike -- for iqm, boneinfo_t[numbones]
    intptr_t boneposedata; // spike -- for iqm,
                           // bonepose_t[numboneposes*numbones]
    enum PoseVertType
    {
        PV_QUAKE1 = 1,                     // trivertx_t
        PV_QUAKE3 = 2,                     // md3XyzNormal_t
        PV_QUAKEFORGE,                     // trivertx16_t
        PV_IQM,                            // iqmvert_t
    } poseverttype;                        // spike
    gltexture_t* gltextures[MAX_SKINS][4]; // johnfitz
    gltexture_t* fbtextures[MAX_SKINS][4]; // johnfitz
    intptr_t texels[MAX_SKINS];            // only for player skins
    maliasframedesc_t frames[1];           // variable sized
};

typedef struct
{
    short xyz[3];
    byte latlong[2];
} md3XyzNormal_t;

typedef struct
{
    float xyz[3];
    float norm[3];
    float st[2];   // these are separate for consistency
    float rgba[4]; // because we can.
    float weight[4];
    byte idx[4];
} iqmvert_t;
struct bonepose_t
{
    float mat[12];
}; // pose data for a single bone.
typedef struct
{
    int parent; //-1 for a root bone
    char name[32];
    bonepose_t inverse;
} boneinfo_t;

#define VANILLA_MAXALIASVERTS 1024
#define MAXALIASVERTS 65536 // spike -- was 2000 //johnfitz -- was 1024
#define MAXALIASFRAMES 1024 // spike -- was 256
#define MAXALIASTRIS 4096   // ericw -- was 2048
extern aliashdr_t* pheader;
extern stvert_t stverts[MAXALIASVERTS];
extern mtriangle_t* triangles;
extern trivertx_t* poseverts_mdl[MAXALIASFRAMES];

//===================================================================

//
// Whole model
//

typedef enum
{
    mod_brush,
    mod_sprite,
    mod_alias,
    mod_ext_invalid
} modtype_t;

// Spike -- these are misnamed/ambiguous.
#define EF_ROCKET 1         // leave a trail
#define EF_GRENADE 2        // leave a trail
#define EF_GIB 4            // leave a trail
#define EF_ROTATE 8         // rotate (bonus items)
#define EF_TRACER 16        // green split trail
#define EF_ZOMGIB 32        // small blood trail
#define EF_TRACER2 64       // orange split trail + rotate
#define EF_TRACER3 128      // purple trail
#define MF_HOLEY (1u << 14) // MarkV/QSS -- make index 255 transparent on mdl's

// johnfitz -- extra flags for rendering
#define MOD_NOLERP 256   // don't lerp when animating
#define MOD_NOSHADOW 512 // don't cast a shadow
#define MOD_FBRIGHTHACK \
    1024 // when fullbrights are disabled, use a hack to render this model
         // brighter
// johnfitz
// spike -- added this for particle stuff
#define MOD_EMITREPLACE \
    2048 // particle effect completely replaces the model (for flames or
         // whatever).
#define MOD_EMITFORWARDS \
    4096 // particle effect is emitted forwards, rather than downwards. why
         // down? good question.
// spike

struct qmodel_t
{
    char name[MAX_QPATH];
    unsigned int path_id; // path id of the game directory
                          // that this model came from
    bool needload;        // bmodels and sprites don't cache normally

    modtype_t type;
    int numframes;
    synctype_t synctype;

    int flags;

#ifdef PSET_SCRIPT
    int emiteffect;  // spike -- this effect is emitted per-frame by entities
                     // with this model
    int traileffect; // spike -- this effect is used when entities move
    struct skytris_s*
        skytris; // spike -- surface-based particle emission for this model
    struct skytriblock_s*
        skytrimem;  // spike -- surface-based particle emission for this model
                    // (for better cache performance+less allocs)
    double skytime; // doesn't really cope with multiples. oh well...
#endif

    //
    // volume occupied by the model graphics
    //
    qvec3 mins, maxs;
    qvec3 ymins, ymaxs; // johnfitz -- bounds for entities with nonzero yaw
    qvec3 rmins,
        rmaxs; // johnfitz -- bounds for entities with nonzero pitch or roll
    // johnfitz -- removed float radius;

    //
    // solid volume for clipping
    //
    bool clipbox;
    qvec3 clipmins, clipmaxs;

    //
    // brush model
    //
    int firstmodelsurface, nummodelsurfaces;

    int numsubmodels;
    mmodel_t* submodels;

    int numplanes;
    mplane_t* planes;

    int numleafs; // number of visible leafs, not counting 0
    mleaf_t* leafs;

    int numvertexes;
    mvertex_t* vertexes;

    int numedges;
    medge_t* edges;

    int numnodes;
    mnode_t* nodes;

    int numtexinfo;
    mtexinfo_t* texinfo;

    int numsurfaces;
    msurface_t* surfaces;

    int numsurfedges;
    int* surfedges;

    int numclipnodes;
    mclipnode_t* clipnodes; // johnfitz -- was dclipnode_t

    int nummarksurfaces;
    msurface_t** marksurfaces;

    hull_t hulls[MAX_MAP_HULLS];

    int numtextures;
    texture_t** textures;

    byte* visdata;
    byte* lightdata;
    char* entities;

    bool viswarn; // for Mod_DecompressVis()

    int bspversion;
    int contentstransparent; // spike -- added this so we can disable glitchy
                             // wateralpha where its not supported.

    //
    // alias model
    //

    GLuint meshvbo;
    byte* meshvboptr; // for non-vbo fallback.

    GLuint meshindexesvbo;
    byte* meshindexesvboptr; // for non-ebo fallback.

    int vboindexofs; // offset in vbo of the hdr->numindexes unsigned shorts
    int vboxyzofs; // offset in vbo of hdr->numposes*hdr->numverts_vbo meshxyz_t
    int vbostofs;  // offset in vbo of hdr->numverts_vbo meshst_t

    //
    // additional model data
    //
    cache_user_t cache; // only access through Mod_Extradata
};

//============================================================================

void Mod_Init();
void Mod_ClearAll();
void Mod_ResetAll(); // for gamedir changes (Host_Game_f)
qmodel_t* Mod_ForName(const char* name, bool crash);
qmodel_t* Mod_ForName_WithFallback(const char* name, const char* fallback);
void* Mod_Extradata(qmodel_t* mod); // handles caching
void Mod_TouchModel(const char* name);

mleaf_t* Mod_PointInLeaf(const qvec3& p, qmodel_t* model);
byte* Mod_LeafPVS(mleaf_t* leaf, qmodel_t* model);
byte* Mod_NoVisPVS(qmodel_t* model);

void Mod_SetExtraFlags(qmodel_t* mod);

void Mod_ForAllKnownNames(void (*f)(const char*)) noexcept;
