/*
 * FredyGames: GLUT calls of Jason Rohrer's minorGems games that the Emscripten GLUT does not have.
 * Force-included (-include) in every file; implemented as no-ops in glut-compat.c. Public domain,
 * like the games.
 */
#ifndef FREDYGAMES_GLUT_COMPAT_H
#define FREDYGAMES_GLUT_COMPAT_H

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <GL/glut.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef GLUT_STROKE_ROMAN
#define GLUT_STROKE_ROMAN ((void *)0)
#endif
#ifndef GLUT_GAME_MODE_POSSIBLE
#define GLUT_GAME_MODE_POSSIBLE 1
#endif

void glutIgnoreKeyRepeat(int ignore);
void glutEntryFunc(void (*func)(int state));
void glutStrokeCharacter(void *font, int character);
int glutStrokeWidth(void *font, int character);
void glutGameModeString(const char *string);
int glutGameModeGet(unsigned int mode);
int glutEnterGameMode(void);
void glutLeaveGameMode(void);

/* the double-precision form, which Emscripten's legacy GL emulation lacks (glut-compat.c) */
void glGetDoublev(GLenum pname, GLdouble *params);

/* GLU, as the OpenGL specification defines them (multiplying the current matrix); the Emscripten
 * versions replace it. glut-compat.c */
void fg_gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
void fg_gluLookAt(GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY,
                  GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ);
#define gluPerspective fg_gluPerspective
#define gluLookAt fg_gluLookAt

/* The games sleep to hold their frame rate (and for a whole second while paused); in the browser that
 * only freezes the page, and requestAnimationFrame already paces the frames. */
static inline int fg_nanosleep(const struct timespec *req, struct timespec *rem) { (void)req; (void)rem; return 0; }
static inline int fg_usleep(useconds_t usec) { (void)usec; return 0; }
#define nanosleep fg_nanosleep
#define usleep fg_usleep

/* Immediate mode: Emscripten's glBegin/glEnd emulation needs the same attributes at every vertex
 * (a glColor4f for two of four vertices of a quad scrambles the whole primitive), while OpenGL keeps
 * the "current" color and texture coordinate. These wrappers record the vertices of a primitive and
 * hand them to the emulation at glEnd with the color (and the texture coordinate, when the primitive
 * uses one) repeated at each vertex. glut-compat.c */
void fg_Begin(GLenum mode);
void fg_End(void);
void fg_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
void fg_TexCoord2f(GLfloat s, GLfloat t);
void fg_Vertex3f(GLfloat x, GLfloat y, GLfloat z);
static inline void fg_Color3f(GLfloat r, GLfloat g, GLfloat b) { fg_Color4f(r, g, b, 1.0f); }
static inline void fg_Color3fv(const GLfloat *v) { fg_Color4f(v[0], v[1], v[2], 1.0f); }
static inline void fg_Color4fv(const GLfloat *v) { fg_Color4f(v[0], v[1], v[2], v[3]); }
static inline void fg_Vertex2f(GLfloat x, GLfloat y) { fg_Vertex3f(x, y, 0.0f); }
static inline void fg_Vertex2fv(const GLfloat *v) { fg_Vertex3f(v[0], v[1], 0.0f); }
static inline void fg_Vertex3fv(const GLfloat *v) { fg_Vertex3f(v[0], v[1], v[2]); }
static inline void fg_Vertex2d(GLdouble x, GLdouble y) { fg_Vertex3f((GLfloat)x, (GLfloat)y, 0.0f); }
static inline void fg_Vertex3d(GLdouble x, GLdouble y, GLdouble z) { fg_Vertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); }
static inline void fg_TexCoord2d(GLdouble s, GLdouble t) { fg_TexCoord2f((GLfloat)s, (GLfloat)t); }
#define glBegin fg_Begin
#define glEnd fg_End
#define glColor4f fg_Color4f
#define glColor3f fg_Color3f
#define glColor3fv fg_Color3fv
#define glColor4fv fg_Color4fv
#define glTexCoord2f fg_TexCoord2f
#define glTexCoord2d fg_TexCoord2d
#define glVertex2f fg_Vertex2f
#define glVertex2fv fg_Vertex2fv
#define glVertex3f fg_Vertex3f
#define glVertex3fv fg_Vertex3fv
#define glVertex2d fg_Vertex2d
#define glVertex3d fg_Vertex3d

/* WebGL has no GL_CLAMP, and Emscripten's glTexEnvf does not take the texture mode */
static inline void fg_TexParameteri(GLenum target, GLenum pname, GLint param) {
    glTexParameteri(target, pname, param == GL_CLAMP ? GL_CLAMP_TO_EDGE : param);
}
static inline void fg_TexEnvf(GLenum target, GLenum pname, GLfloat param) {
    glTexEnvi(target, pname, (GLint)param);
}
#define glTexParameteri fg_TexParameteri
#define glTexEnvf fg_TexEnvf

#ifdef __cplusplus
}
#endif

#endif
