/* GemRB - Infinity Engine Emulator
 * Copyright (C) 2003 The GemRB Project
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *
 */

/**
 * @file globals.h
 * Some global definitions and includes
 * @author The GemRB Project
 */


#ifndef GLOBALS_H
#define GLOBALS_H

#include "ie_types.h"
#include <type_traits>

#include <cmath>

#define VERSION_GEMRB "0.9.2"

#define GEMRB_STRING "GemRB v" VERSION_GEMRB
#define PACKAGE "GemRB"

#include "RGBAColor.h"
#include "errors.h"

#include "Region.h"
#include "Streams/DataStream.h"

#include <algorithm>
#include <bitset>
#include <climits>
#include <chrono>
#include <memory>

namespace GemRB {

//Global Variables

/////feature flags
enum class GFFlags : uint32_t {
	// this enum must begin at 0 for iteration
	HAS_KAPUTZ = 0,          		//pst
	ALL_STRINGS_TAGGED,   			//bg1, pst, iwd1
	HAS_SONGLIST,        			//bg2
	TEAM_MOVEMENT,       			//pst
	UPPER_BUTTON_TEXT,   			//bg2
	LOWER_LABEL_TEXT,    			//bg2
	HAS_PARTY_INI,       			//iwd2
	SOUNDFOLDERS,        			//iwd2
	IGNORE_BUTTON_FRAMES,			// all?
	ONE_BYTE_ANIMID,     			// pst
	HAS_DPLAYER,         			// not pst
	HAS_EXPTABLE,        			// iwd, iwd2
	HAS_BEASTS_INI,      			//pst; also for quests.ini
	HAS_EE_EFFECTS,       			//ees
	HAS_PICK_SOUND,      			//pst
	IWD_MAP_DIMENSIONS,  			//iwd, iwd2
	AUTOMAP_INI,         			//pst
	SMALL_FOG,           			//bg1, pst
	REVERSE_DOOR,        			//pst
	PROTAGONIST_TALKS,   			//pst
	HAS_SPELLLIST,       			//iwd2
	IWD2_SCRIPTNAME,     			//iwd2, iwd, how
	DIALOGUE_SCROLLS,    			//pst
	KNOW_WORLD,          			//iwd2
	REVERSE_TOHIT,       			//all except iwd2
	SAVE_FOR_HALF,       			//pst
	CHARNAMEISGABBER,   				//iwd2
	MAGICBIT,            			//iwd, iwd2
	CHECK_ABILITIES,     			//bg2 (others?)
	CHALLENGERATING,     			//iwd2
	SPELLBOOKICONHACK,   			//bg2
	ENHANCED_EFFECTS,    			//iwd2 (maybe iwd/how too)
	DEATH_ON_ZERO_STAT,  			//not in iwd2
	SPAWN_INI,           			//pst, iwd, iwd2
	IWD2_DEATHVARFORMAT,  			//iwd branch (maybe pst)
	RESDATA_INI,         			//pst
	OVERRIDE_CURSORPOS,  			//pst, iwd2
	BREAKABLE_WEAPONS,     			//only bg1
	RULES_3ED,              			//iwd2
	LEVELSLOT_PER_CLASS,    			//iwd2
	SELECTIVE_MAGIC_RES,    			//bg2, iwd2, (how)
	HAS_HIDE_IN_SHADOWS,    			// bg2, iwd2
	AREA_VISITED_VAR,    			//iwd, iwd2
	PROPER_BACKSTAB,     			//bg2, iwd2, how?
	ONSCREEN_TEXT,       			//pst
	SPECIFIC_DMG_BONUS,				//how, iwd2
	STRREF_SAVEGAME,       			//iwd2
	SIMPLE_DISRUPTION,      			// ToBEx: simplified disruption
	BIOGRAPHY_RES,               	//iwd branch
	NO_BIOGRAPHY,                	//pst
	STEAL_IS_ATTACK,             	//bg2 for sure
	CUTSCENE_AREASCRIPTS,			//bg1, maybe more
	FLEXIBLE_WMAP,               	//iwd
	AUTOSEARCH_HIDDEN,           	//all except iwd2
	PST_STATE_FLAGS,             	//pst complicates this
	NO_DROP_CAN_MOVE,            	//bg1
	JOURNAL_HAS_SECTIONS,        	//bg2
	CASTING_SOUNDS,              	//all except pst and bg1
	CASTING_SOUNDS2,             	//bg2
	FORCE_AREA_SCRIPT,           	//how and iwd2 (maybe iwd1)
	AREA_OVERRIDE,               	//pst maze and other hardcode
	NO_NEW_VARIABLES,            	//pst
	SOUNDS_INI,                  	//iwd/how/iwd2
	USEPOINT_400,                	//all except pst and iwd2
	USEPOINT_200,                	//iwd2
	HAS_FLOAT_MENU,              	//pst
	RARE_ACTION_VB,              	//pst
	NO_UNDROPPABLE,              	//iwd,how
	START_ACTIVE,                	//bg1
	INFOPOINT_DIALOGS,           	//pst, but only bg1 has garbage there
	IMPLICIT_AREAANIM_BACKGROUND,	//idw,how,iwd2
	HEAL_ON_100PLUS,             	//bg1, bg2, pst
	IN_PARTY_ALLOWS_DEAD,			//all except bg2
	ZERO_TIMER_IS_VALID,         	// how, not bg2, other unknown
	SHOP_RECHARGE,               	// all?
	MELEEHEADER_USESPROJECTILE,  	// minimally bg2
	FORCE_DIALOGPAUSE,           	// all except if using v1.04 DLG files (bg2, special)
	RANDOM_BANTER_DIALOGS,       	// bg1
	FIXED_MORALE_OPCODE,         	// bg2
	HAPPINESS,                   	// all except pst and iwd2
	EFFICIENT_OR,                	// does the OR trigger shortcircuit on success or not? Only in iwd2
	LAYERED_WATER_TILES,				// TileOverlay for water has an extra half transparent layer (all but BG1)
	CLEARING_ACTIONOVERRIDE,         // bg2, not iwd2
	DAMAGE_INNOCENT_REP,             // not bg1
	HAS_WEAPON_SETS,             	// iwd2

	count // must be last
};

//the number of item usage fields (used in CREItem and STOItem)
#define CHARGE_COUNTERS  3

/////globally used functions

class Scriptable;
class Actor;

GEM_EXPORT unsigned int Distance(const Point &pos, const Scriptable *b);
GEM_EXPORT unsigned int SquaredMapDistance(const Point &pos, const Scriptable *b);
GEM_EXPORT unsigned int PersonalDistance(const Point &p, const Scriptable *b);
GEM_EXPORT unsigned int SquaredPersonalDistance(const Point &pos, const Scriptable *b);
GEM_EXPORT unsigned int Distance(const Scriptable *a, const Scriptable *b);
GEM_EXPORT unsigned int SquaredDistance(const Scriptable *a, const Scriptable *b);
GEM_EXPORT unsigned int PersonalDistance(const Scriptable *a, const Scriptable *b);
GEM_EXPORT unsigned int SquaredPersonalDistance(const Scriptable *a, const Scriptable *b);
GEM_EXPORT unsigned int SquaredMapDistance(const Scriptable *a, const Scriptable *b);
GEM_EXPORT unsigned int PersonalLineDistance(const Point &v, const Point &w, const Scriptable *s, double *proj);
GEM_EXPORT double Feet2Pixels(int feet, double angle);
GEM_EXPORT bool WithinAudibleRange(const Actor *actor, const Point &dest);
GEM_EXPORT bool WithinRange(const Scriptable *actor, const Point &dest, int distance);
GEM_EXPORT bool WithinPersonalRange(const Scriptable *actor, const Point &dest, int distance);
GEM_EXPORT bool WithinPersonalRange(const Scriptable* scr1, const Scriptable* scr2, int distance);
GEM_EXPORT int EARelation(const Scriptable *a, const Actor *b);
GEM_EXPORT bool Schedule(ieDword schedule, ieDword time);

#define SCHEDULE_MASK(time) (1 << core->Time.GetHour(time - core->Time.hour_size/2))

using tick_t = unsigned long; // milliseconds
inline tick_t GetMilliseconds()
{
	using namespace std::chrono;
	return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}

template<typename T>
T strtosigned(const char* str, char** endptr = nullptr, int base = 0)
{
	static_assert(std::is_integral<T>::value, "Type must be integral.");
	static_assert(sizeof(T) <= sizeof(long), "Type is too big for conversion.");
	static_assert(std::is_signed<T>::value, "Type must be signed");
	
	long ret = strtol(str, endptr, base);
	if (ret > std::numeric_limits<T>::max()) {
		return std::numeric_limits<T>::max();
	}
	
	if (ret < std::numeric_limits<T>::min()) {
		return std::numeric_limits<T>::min();
	}
	
	return static_cast<T>(ret);
}

template<typename T>
inline bool valid_signednumber(const char* string, T& val)
{
	char* endpr = nullptr;
	val = strtosigned<T>(string, &endpr, 0);
	return endpr != string;
}

template<typename T>
T strtounsigned(const char* str, char** endptr = nullptr, int base = 0)
{
	static_assert(std::is_integral<T>::value, "Type must be integral.");
	static_assert(sizeof(T) <= sizeof(long), "Type is too big for conversion.");
	static_assert(std::is_unsigned<T>::value, "Type must be unsigned");
	
	unsigned long ret = strtoul(str, endptr, base);
	if (ret > std::numeric_limits<T>::max()) {
		return std::numeric_limits<T>::max();
	}
	
	return static_cast<T>(ret);
}

template<typename T>
inline bool valid_unsignednumber(const char* string, T& val)
{
	char* endpr = nullptr;
	val = strtounsigned<T>(string, &endpr, 0);
	return endpr != string;
}

// bitflag operations
// !!! Keep these synchronized with GUIDefines.py !!!
enum class BitOp : int {
	SET = 0, //gemrb extension
	AND = 1,
	OR = 2,
	XOR = 3,
	NAND = 4 //gemrb extension
};

template <typename T>
inline bool SetBits(T& flag, const T& value, BitOp mode)
{
	switch(mode) {
		case BitOp::OR: flag |= value; break;
		case BitOp::NAND: flag &= ~value; break;
		case BitOp::SET: flag = value; break;
		case BitOp::AND: flag &= value; break;
		case BitOp::XOR: flag ^= value; break;
		default:
			return false;
	}
	return true;
}

template <typename T>
inline size_t CountBits(const T& i)
{
	// using a bitset is guaranteed to be portable
	// it is also usually much faster than a loop over the bits since it is often implemented with a CPU popcount instruction
	static std::bitset<sizeof(T) * CHAR_BIT> bits;
	bits = i;
	return bits.count();
}

template <typename T>
inline T Clamp(const T& n, const T& lower, const T& upper)
{
	return std::max(lower, std::min(n, upper));
}

// this is like static_cast, but clamps to the range supported by DST instead of turning large positive numbers negative and large negatives 0
template <typename DST, typename SRC>
inline DST Clamp(const SRC& n)
{
	static_assert(sizeof(DST) <= sizeof(SRC), "Clamping a SRC smaller than DST has no effect.");
	return static_cast<DST>(Clamp<SRC>(n, std::numeric_limits<DST>::min(), std::numeric_limits<DST>::max()));
}

template <>
inline Point Clamp(const Point& p, const Point& lower, const Point& upper)
{
	Point ret;
	ret.x = std::max(lower.x, std::min(p.x, upper.x));
	ret.y = std::max(lower.y, std::min(p.y, upper.y));
	return ret;
}

// WARNING: these are meant to be fast, if you are concerned with overflow/underflow don't use them
template <typename T>
inline T CeilDivUnsigned(T dividend, T divisor)
{
	//static_assert(std::is_unsigned<T>::value, "This quick round only works for positive values.");
	return (dividend + divisor - 1) / divisor;
}

template <typename T>
inline T CeilDivSlow(T dividend, T divisor)
{
	if (divisor < 0) {
		return (dividend - divisor - 1) / divisor;
	} else {
		return (dividend + divisor - 1) / divisor;
	}
}

template <typename T>
inline T CeilDiv(T dividend, T divisor)
{
	static_assert(std::is_integral<T>::value, "Only integral types allowed");
	// the compiler should be able to boil all this away (at least in release mode)
	// and just inline the fastest version supported by T
	if (std::is_unsigned<T>::value) {
		return CeilDivUnsigned(dividend, divisor);
	} else {
		return CeilDivSlow(dividend, divisor);
	}
}

// TODO: remove this when we switch to c++14
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
	return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

//the maximum supported game CD count
#define MAX_CD               6

}

#endif //! GLOBALS_H


