/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell
 * or otherwise commercially exploit the source or things you created based on the
 * source.
 *
*/



#ifndef _PSTYPES_H
#define _PSTYPES_H



#include "windows_stub/config.h"
#include "globalincs/scp_defines.h"
#include "globalincs/toolchain.h"
#include "globalincs/vmallocator.h"
#include "utils/strings.h"

#include <cstdio>    // For NULL, etc
#include <cstdlib>
#include <memory.h>
#include <cstring>
#include <algorithm>
#include <cstdint>
#include <functional>

// value to represent an uninitialized state in any int or uint
#define UNINITIALIZED 0x7f8e6d9c

#define MAX_PLAYERS	12

#ifdef LOCAL
#undef LOCAL
#endif

#define LOCAL static			// make module local varilable static.

#ifdef _WIN32
#define DIR_SEPARATOR_CHAR '\\'
#define DIR_SEPARATOR_STR  "\\"
#else
#define DIR_SEPARATOR_CHAR '/'
#define DIR_SEPARATOR_STR  "/"
#endif

constexpr char COMMENT_CHAR =    static_cast<char>(';');
constexpr char EOLN =            static_cast<char>(0x0a);
constexpr char CARRIAGE_RETURN = static_cast<char>(0x0d);

#ifndef NDEBUG
constexpr bool FSO_DEBUG = true;
#else
constexpr bool FSO_DEBUG = false;
#endif

typedef std::int32_t _fs_time_t;  // time_t here is 64-bit and we need 32-bit
typedef std::int32_t fix;

// PTR compatible sizes
typedef ptrdiff_t ptr_s;
typedef size_t ptr_u;

typedef std::int64_t longlong;
typedef std::uint64_t ulonglong;
typedef std::uint8_t ubyte;
typedef std::uint16_t ushort;
typedef std::uint32_t uint;
typedef unsigned long ulong;

//Stucture to store clipping codes in a word
typedef struct ccodes {
	ubyte cc_or, cc_and;		//or is low byte, and is high byte
} ccodes;

struct vertex;

typedef struct vec4 {
	union {
		struct {
			float x,y,z,w;
		} xyzw;
		float a1d[4];
	};
} vec4;

struct bvec4 {
	bool x;
	bool y;
	bool z;
	bool w;
};

// sometimes, you just need some integers
typedef struct ivec3 {
	int x, y, z;
} ivec3;

struct ivec2 {
	int x, y;
};

inline bool operator<(const ivec3& l, const ivec3& r){
	return l.x < r.x || (l.x == r.x && (l.y < r.y || (l.y == r.y && l.z < r.z)));
}

inline bool operator<(const ivec2& l, const ivec2& r){
	return l.x < r.x || (l.x == r.x && l.y < r.y);
}

namespace scripting {
	class ade_table_entry;
}
namespace luacpp {
	class LuaValue;
}
struct lua_State;

/** Represents a point in 3d space.

Note: this is a struct, not a class, so no member functions. */
typedef struct vec3d {
	union {
		struct {
			float x,y,z;
		} xyz;
		float a1d[3];
	};
} vec3d;

typedef struct vec2d {
	float x, y;
} vec2d;

// Euler angles for a rotation: h=heading, b=bank and p=pitch; angles are around the y, z and x axes
// respectively and are performed in that order.  (Strictly speaking these are Tait-Bryan angles,
// not classic Euler angles.)
typedef struct angles {
	float	p, b, h;
} angles_t;

// For the avoidance of doubt, this is a row-major order matrix.
typedef struct matrix {
	union {
		struct {
			vec3d	rvec, uvec, fvec;
		} vec;
		float a2d[3][3];
		float a1d[9];
	};
} matrix;

// You might think this is also row-major. You fool! It is actually column-major.
typedef struct matrix4 {
	union {
		struct {
			vec4 rvec, uvec, fvec, pos;
		} vec;
		float a2d[4][4];
		float a1d[16];
	};
} matrix4;

typedef struct uv_pair {
	float u,v;
} uv_pair;

/** Compares two uv_pairs */
inline bool operator==(const uv_pair &left, const uv_pair &right)
{
	return (left.u == right.u) && (left.v == right.v);
}

/** Represents a point in 3d screen space. 'w' is 1/z.

Like vec3d but for screens.

Note: this is a struct, not a class, so no member functions. */
typedef struct screen3d
{
	union {
		struct {
			float x,y,w;
		} xyw;
		float a1d[3];
	};
} screen3d;

/** Compares two screen3ds */
inline bool operator==(const screen3d &self, const screen3d &other)
{
	return (self.xyw.x == other.xyw.x
		&& self.xyw.y == other.xyw.y
		&& self.xyw.w == other.xyw.w
	);
}

/** Used to store rotated points for mines. Has flag to indicate if projected.

Note: this is a struct, not a class, so no memeber functions. */
typedef struct vertex {
	vec3d		world;				// world space position
	screen3d	screen;				// screen space position (sw == 1/z)
	uv_pair		texture_position;	// texture position
	ubyte		r, g, b, a;			// color.  Use b for darkening;
	ubyte		codes;				// what sides of view pyramid this point is on/off.  0 = Inside view pyramid.
	ubyte		flags;				// Projection flags.  Indicates whether it is projected or not or if projection overflowed.
	ubyte		pad[2];				// pad structure to be 4 byte aligned.
} vertex;

typedef struct effect_vertex {
	vec3d position;
	uv_pair tex_coord;
	float radius;
	ubyte r, g, b, a;
} effect_vertex;

struct particle_pnt {
	vec3d position;
	float size;
	vec3d up;
};

// for compiler compatibility, even though C++17 supports omitting the template type...

//def_list
template<typename T>
struct flag_def_list_templated {
	const char *name;
	T def;
	ubyte var;
};
using flag_def_list = flag_def_list_templated<int>;

//A list of parse names for a flag enum
template<typename T>
struct flag_def_list_new {
    const char* name;			// The parseable representation of this flag
    T def;				// The flag definition for this flag
    bool in_use;		// Whether or not this flag is currently in use or obsolete
    bool is_special;	// Whether this flag requires special processing. See parse_string_flag_list<T, T> for details
};

//A list of parse names for a flag enum. Instead of specifying whether an argument needs special handling,
//a functor can passed that is called to handle an argument proceeding the flag. If used with parse_string_flag_list_special,
//these will automatically be called when a special argument is encountered
template<class T, typename... additional_args>
struct special_flag_def_list_new : public flag_def_list_new<T> {
	std::function<void(const SCP_string&, additional_args...)> parse_special;

	special_flag_def_list_new(const char* name, T flag, bool in_use) : flag_def_list_new<T>{ name, flag, in_use, false } {}
	special_flag_def_list_new(const char* name, T flag, bool in_use, const decltype(parse_special)& parseSpecial) : flag_def_list_new<T>{ name, flag, in_use, true }, parse_special(parseSpecial) { }
};

// weapon count list (mainly for pilot files)
typedef struct wep_t {
	int index;
	int count;
} wep_t;

typedef struct coord2d {
	int x,y;
} coord2d;

#include "osapi/dialogs.h"

extern int Global_warning_count;
extern int Global_error_count;

enum class ErrorType : ubyte { NONE = 0, WARNING, FATAL_ERROR };

#include "osapi/outwnd.h"

// To debug printf do this:
// mprintf(( "Error opening %s\n", filename ));
#ifndef NDEBUG
constexpr bool LoggingEnabled = true;
#else
#ifdef SCP_RELEASE_LOGGING
constexpr bool LoggingEnabled = true;
#else
constexpr bool LoggingEnabled = false;
#endif
#endif

#define mprintf(args) do { if (LoggingEnabled) { outwnd_printf2 args; } } while (false)
#define nprintf(args) do { if (LoggingEnabled) { outwnd_printf args; } } while (false)

#define LOCATION __FILE__,__LINE__

// To flag an error, you can do this:
// Error( __FILE__, __LINE__, "Error opening %s", filename );
// or,
// Error( LOCATION, "Error opening %s", filename );

/*******************NEVER COMMENT Assert ************************************************/
// Please never comment the functionality of Assert in debug
// The code, as with all development like this is littered with Asserts which are designed to throw
// up an error message if variables are out of range.
// Disabling this functionality is dangerous, crazy values can run rampant unchecked, and the longer it's disabled
// the more likely you are to have problems getting it working again.
#if defined(NDEBUG)
#	define Assert(expr) do { ASSUME(expr); (void)sizeof(expr); } while (false)
#else
#	define Assert(expr) do {\
		if (!(expr)) {\
			os::dialogs::AssertMessage(#expr,__FILE__,__LINE__);\
		}\
		ASSUME( expr );\
	} while (false)
#endif

template <typename T>
bool CallAssert(bool val, const char *msg, const char *filename, int linenum, T assertMsgFunc)
{
	if (!val)
		assertMsgFunc(msg, filename, linenum, nullptr);
	ASSUME(val);
	return true;
}
#if defined(NDEBUG)
#	define AssertExpr(expr) (true)
#else
#	define AssertExpr(expr) CallAssert(expr, #expr, __FILE__, __LINE__, os::dialogs::AssertMessage)
#endif
/*******************NEVER COMMENT Assert ************************************************/

// Goober5000 - define Verify for use in both release and debug mode
#define Verify(x) do { if (!(x)){ Error(LOCATION, "Verify failure: %s\n", #x); } ASSUME(x); } while(false)

// Verification (like Assertion)
#ifndef _MSC_VER   // non MS compilers
#	define Verification(x, y, ...) do { if (!(x)) { Error(LOCATION, "Verify failure: %s with help text " #y "\n", #x, ##__VA_ARGS__); } ASSUME(x); } while(false)
#else
#	define Verification(x, y, ...) do { if (!(x)) { Error(LOCATION, "Verify failure: %s with help text " #y "\n", #x, __VA_ARGS__); } ASSUME(x); } while(false)
#endif

#if defined(NDEBUG)
	// No debug version of Int3
	#define Int3() do { } while (false)
#else
	void debug_int3(const char *file, int line);

	// Debug version of Int3
	#define Int3() debug_int3(__FILE__, __LINE__)
#endif	// NDEBUG


// the older MIN and MAX macros were vulnerable to performance issues since they duplicated their tokens

#ifndef MIN
#define MIN(a,b) std::min(a,b)
#endif
#ifndef MAX
#define MAX(a,b) std::max(a,b)
#endif


#define PI				3.141592654f
// twice values
const float PI2			= (PI*2.0f);
// half values
const float PI_2		= (PI/2.0f);
const float PI_4		= (PI/4.0f);


extern int Fred_running;  // Is Fred running, or FreeSpace?
extern int Qtfred_running;  // Distinguishes QtFRED from legacy Fred2; Fred_running is set in both, but Qtfred_running only in QtFRED.
extern bool running_unittests;

const size_t INVALID_SIZE = static_cast<size_t>(-1);

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


#include "math/fix.h"
#include "math/floating.h"

// Some constants for stuff
#define MAX_FILENAME_LEN	32		// Length for filenames, ie "title.pcx"
#define MAX_PATH_LEN		256		// Length for pathnames, ie "c:\bitmaps\title.pcx"

// contants and defined for byteswapping routines (useful for mac)

#ifdef SCP_SOLARIS // Solaris
#define INTEL_INT(x)	x
#define INTEL_LONG(x)   x
#define INTEL_SHORT(x)	x
#define INTEL_FLOAT(x)	(*x)
#elif BYTE_ORDER == BIG_ENDIAN
// turn off inline asm
#undef USE_INLINE_ASM

#define INTEL_INT(x)	SDL_Swap32(x)
#define INTEL_LONG(x)   SDL_Swap64(x)
#define INTEL_SHORT(x)	SDL_Swap16(x)
#define INTEL_FLOAT(x)	SDL_SwapFloat((*x))

#else // Little Endian -
#define INTEL_INT(x)	x
#define INTEL_LONG(x)   x
#define INTEL_SHORT(x)	x
#define INTEL_FLOAT(x)	(*x)
#endif // BYTE_ORDER

// since a lot of header files will try to #define TRUE and FALSE,
// making them constexpr here doesn't gain us much
#define TRUE	1
#define FALSE	0

// the trailing underscores are to avoid conflicts with previously #define'd tokens
enum class TriStateBool : int { FALSE_ = 0, TRUE_ = 1, UNKNOWN_ = -1 };

// Callback Loading function.
// If you pass a function to this, that function will get called
// around 10x per second, so you can update the screen.
// Pass NULL to turn it off.
// Call this with the name of a function.  That function will
// then get called around 10x per second.  The callback function
// gets passed a 'count' which is how many times game_busy has
// been called since the callback was set.   It gets called
// one last time with count=-1 when you turn off the callback
// by calling game_busy_callback(NULL).   Game_busy_callback
// returns the current count, so you can tell how many times
// game_busy got called.
// If delta_step is above 0, then it will also make sure it
// calls the callback each time count steps 'delta_step' even
// if 1/10th of a second hasn't elapsed.
extern int game_busy_callback( void (*callback)(int count), int delta_step = -1 );

// Call whenever loading to display cursor
extern void game_busy(const char *filename = NULL);

#define NOX(s) s

const char *XSTR(const char *str, int index, bool force_lookup = false);

// The older CLAMP macro was vulnerable to performance issues since it duplicated its tokens.  (The older CAP was a function.)  Technically
// the new macro duplicates the LHS, but that should be ok.

// Ensure that x is at least min and at most max.
#define CLAMP(x, min, max) { x = std::clamp(x, min, max); }

// Ensure that x is at least min and at most max.
#define CAP(v, mn, mx) CLAMP(v, mn, mx)


//=========================================================
// Memory management functions
//=========================================================

#include "globalincs/fsmemory.h"

class camid
{
private:
	int sig;
	size_t idx;
public:
	camid();
	camid(size_t n_idx, int n_sig);

	class camera *getCamera();
	size_t getIndex();
	int getSignature();
	bool isValid() const;
};

#include "globalincs/vmallocator.h"
#include "globalincs/safe_strings.h"

// check to see that a passed sting is valid, ie:
//  - has >0 length
//  - is not "none"
//  - is not "<none>"
inline bool VALID_FNAME(const char* x) {
	return (x != nullptr) && (x[0] != '\0') && stricmp(x, "none") != 0 && stricmp(x, "<none>") != 0;
}
/**
 * @brief Checks if the specified string may be a valid file name
 *
 * @warning This only does a quick check against an empty string and a few known invalid names. It does not check if the
 * file actually exists.
 *
 * @param x The file name to check
 * @return @c true if the name is valid, @c false otherwise
 */
inline bool VALID_FNAME(const SCP_string& x) {
	if (x.empty()) {
		return false;
	}
	if (!stricmp(x.c_str(), "none")) {
		return false;
	}
	if (!stricmp(x.c_str(), "<none>")) {
		return false;
	}
	return true;
}

// Function to generate a stacktrace
SCP_string dump_stacktrace();

// DEBUG compile time catch for dangerous uses of memset/memcpy/memmove
// This is disabled for VS2013 and lower since that doesn't support the necessary features
#if !defined(NDEBUG) && !defined(USING_THIRD_PARTY_LIBS) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
	// feature support seems to be: gcc   clang   msvc
	// auto                         4.4   2.9     2010
	// std::is_trivial              4.5   ?       2012 (2010 only duplicates std::is_pod)
	// static_assert                4.3   2.9     2010
	#include <type_traits>
	#include <cstring>

	// MEMSET!
	const auto ptr_memset = std::memset;
	#define memset memset_if_trivial_else_error

// Forward declarations from libraries
struct ImDrawListSplitter;

// Put into std to be compatible with code that uses std::mem*
namespace std
{
template <typename T>
using trivial_check = std::is_trivially_copyable<T>;

template <typename T>
void* memset_if_trivial_else_error(
	typename std::enable_if<!std::is_same<T, ImDrawListSplitter>::value, T>::type* memset_data,
	int ch,
	size_t count)
{
	static_assert(trivial_check<T>::value, "memset on non-trivial object");
	return ptr_memset(memset_data, ch, count);
}

// assume memset on a void* is "safe"
// only used in cutscene/mveplayer.cpp:mve_video_createbuf()
inline void* memset_if_trivial_else_error(void* memset_data, int ch, size_t count)
{
	return ptr_memset(memset_data, ch, count);
}

// Dear ImGui triggers these as well, so we need to let them pass
inline void* memset_if_trivial_else_error(ImDrawListSplitter* memset_data, int ch, size_t count)
{
	return ptr_memset(memset_data, ch, count);
}

	// MEMCPY!
	const auto ptr_memcpy = std::memcpy;
	#define memcpy memcpy_if_trivial_else_error

	template<typename T, typename U>
	void *memcpy_if_trivial_else_error(T *memcpy_dest, U *src, size_t count)
	{
		static_assert(trivial_check<T>::value, "memcpy on non-trivial object T");
		static_assert(trivial_check<U>::value, "memcpy on non-trivial object U");
		return ptr_memcpy(memcpy_dest, src, count);
	}

	// assume memcpy with void* is "safe"
	// used in:
	//   globalincs/systemvars.cpp:insertion_sort()
	//   network/chat_api.cpp:AddChatCommandToQueue()
	//   network/multi_obj.cpp:multi_oo_sort_func()
	//   parse/lua.cpp:ade_get_args() && ade_set_args()
	//
	// probably should setup a static_assert on insertion_sort as well
	template<typename U>
	void *memcpy_if_trivial_else_error(void *memcpy_dest, U *memcpy_src, size_t count)
	{
		static_assert(trivial_check<U>::value, "memcpy on non-trivial object U");
		return ptr_memcpy(memcpy_dest, memcpy_src, count);
	}

	template<typename T>
	void *memcpy_if_trivial_else_error(T *memcpy_dest, void *memcpy_src, size_t count)
	{
		static_assert(trivial_check<T>::value, "memcpy on non-trivial object T");
		return ptr_memcpy(memcpy_dest, memcpy_src, count);
	}
	template<typename T>
	void *memcpy_if_trivial_else_error(T *memcpy_dest, const void *memcpy_src, size_t count)
	{
		static_assert(trivial_check<T>::value, "memcpy on non-trivial object T");
		return ptr_memcpy(memcpy_dest, memcpy_src, count);
	}

	inline void *memcpy_if_trivial_else_error(void *memcpy_dest, void *memcpy_src, size_t count)
	{
		return ptr_memcpy(memcpy_dest, memcpy_src, count);
	}

	// MEMMOVE!
	const auto ptr_memmove = std::memmove;
	#define memmove memmove_if_trivial_else_error

	template<typename T, typename U>
	inline void *memmove_if_trivial_else_error(T *memmove_dest, U *memmove_src, size_t count)
	{
		static_assert(trivial_check<T>::value, "memmove on non-trivial object T");
		static_assert(trivial_check<U>::value, "memmove on non-trivial object U");
		return ptr_memmove(memmove_dest, memmove_src, count);
	}

	// Not really needed but else clang thinks ptr_memmove isn't used
	inline void *memmove_if_trivial_else_error(void *memmove_dest, void *memmove_src, size_t count)
	{
		return ptr_memmove(memmove_dest, memmove_src, count);
	}
}
// Put into global namespace
using std::memcpy_if_trivial_else_error;
using std::memmove_if_trivial_else_error;
using std::memset_if_trivial_else_error;

#endif // NDEBUG

#endif		// _PSTYPES_H
