#ifndef CUSTOM_H_
#define CUSTOM_H_


extern GLFWmonitor *monitor;
extern const GLFWvidmode *vmode;
extern int mX,mY,mW,mH;
extern int fullScreen,readyFullScreen;

// settings
extern unsigned int samples;
extern unsigned int FOV;
//unsigned int FOV = 90;

extern unsigned int SCR_WIDTH; // auto fit to screen/window
extern unsigned int SCR_HEIGHT;
extern float ratio;
extern float fovrad;
extern float radian;
extern unsigned int lastSW;
extern unsigned int lastSH;
extern unsigned int rendMode;

extern int spamonce;

extern double limitFPS;
//extern double deltaTime, currentTime;
extern double fixedDeltaTime;
//extern double lastFrameTime;
extern double lastFixedFrameTime;
extern double timer;
extern unsigned int updates, frames;
extern double renderDeltaTime, renderCurrentTime, renderLastTime;
extern double updateDeltaTime, updateCurrentTime, updateLastTime;
extern double updateDeltaLapsed;

extern float saveTime;
extern float saveCount;

typedef struct Mat3 { // not used, only Vec3 or linmath vec3 and mat4x4
    float x1,y1,z1;
    float x2,y2,z2;
    float x3,y3,z3;
} Mat3;

// Define a vector struct for 3D vectors
typedef struct Vec3 { // linmath definition vec3 small v=array
    float x, y, z;
} Vec3;

typedef struct Transform {
    struct Vec3 *position;
    struct Vec3 *rotation;
    struct Vec3 *scale;
} Transform;

typedef struct Direction {
    struct Vec3 *forward; //z
    struct Vec3 *up;      //y
    struct Vec3 *right;   //x
} Direction;

extern vec3 up; // linmath type for math4x4_look_at eye,centr,up must be array, not truct, struct = capital Vec3
typedef struct Camera {
    //unsigned int id;
    struct Transform *eye; // of body
    Vec3 *center; // looking at, ie player pos
    struct Direction *dir;
    float theta, phi;
    float xOffset; // separating rotation of player and camera
} Camera;
extern struct Camera *camera;
//#define CAMERA_STRUCT_INITIALIZE { .x = 0, .y = 0, z = 0 }

typedef struct Player {
    struct Camera *camera;
    struct Transform *transform; // of view
    struct Direction *dir; // 
    //struct Vec3 *copydir; // copy of exec rotation to render
    //float theta, phi;
    float phi; // theta = xOffset on camera
} Player;
extern struct Player *player;

extern unsigned int quadVAO, quadVBO;
extern unsigned int msaaFB, msaaSample, msaaDepth;//, msaaStencil;
extern unsigned int cfm_state;
int CreateFrameMsaa(); // final pixel post. multisample, drop depth

extern unsigned int mainFB, mainFBsample, mainFBdepth;
extern unsigned int cfa_state;
int CreateFrameMain(); // use for water and world post before msaa post

float lerp(float a, float b, float f);

Vec3 getDirection(Direction *dir, float theta, float phi); // dir = return

int saveGame();
int loadGame(FILE *fp);

int InitializePlayer();

extern int terrainTexLen;
extern unsigned int terrainTextures[20];

extern int terrainMaskLen;
extern unsigned int terrainMasks[20];

int unloadTerrainTextures();

unsigned int loadTexture(char *file);
int InitializeTextures();

unsigned long to_nearest_pow2(unsigned long v);

float Q_rsqrt(float number);

void printBits(size_t const size, void const * const ptr);

char *strdup(const char *s);

char** split(const char *str, const char *delimiter, size_t *len);
void free4split(char** sa);

//typedef struct EulerAngles { double roll, pitch, yaw; } EulerAngles;
typedef struct Quaternion { double w, x, y, z; } Quaternion;

//extern int qmode;

//Quaternion ToQuaternion(double roll, double pitch, double yaw);
Quaternion ToQuaternion(Vec3 euler);
Vec3 ToEulerAngles(Quaternion q);
//EulerAngles ToEulerAngles(Quaternion q);

void mul_mat3_vec3(Vec3 *r, Mat3 *b, Vec3 a);

float magnitude(Vec3 *result);
Quaternion directionToQuaternion(Vec3 D);

void createScalingMatrix(mat4x4 m, float sx, float sy, float sz);
void applyScalingToModel(mat4x4 model, float sx, float sy, float sz);

Vec3 quaternionToDirection(Quaternion q);
Quaternion eulerToQuaternion(Vec3 *erot);
Vec3 eulerToDirection(Vec3 *erot);

//void normalize_vec3(vec3 result, vec3 in);
double* normalizeVector3(double* v);
Vec3 normalize(Vec3 v);
double dot_product(Vec3 *a, Vec3 *b);
float dot(Vec3 a, Vec3 b);
void cross_product(Vec3 *result, Vec3 *a, Vec3 *b);
Vec3 cross(Vec3 a, Vec3 b);

double calculate_distance(Vec3 *p1, Vec3 *p2);
double vector3_distance(Vec3 *v1, Vec3 *v2);

double vector_length(Vec3 *v);
double quaternion_length(Quaternion *q);

void rotate_towards(Vec3 *rotation, Vec3 *v, Vec3 *target);
double angle_between(Vec3 *a, Vec3 *b);

void quaternion_rotation(Quaternion *q, Vec3 *v, float angle);
void AngleAxis(Quaternion *q, double angle, Vec3 *axis);

void QuaternionToMat4(mat4x4 M, Quaternion *q);

extern Vec3 unit_vector_from_spherical(double theta, double phi);
extern Vec3 convert_spherical_to_cartesian(double radius, double theta, double phi);

Vec3 Vec3_sub(Vec3 a, Vec3 b);

#endif
