#ifndef JSON_MEMORY_H
#define JSON_MEMORY_H

#include <cstdlib> //for malloc, realloc, and free
#include <cstring> //for memmove
#include "JSONOptions.h"
#include "JSONDebug.h"

#if defined(JSON_DEBUG) || defined(JSON_SAFE)
	#define JSON_FREE_PASSTYPE &
#else
	#define JSON_FREE_PASSTYPE
#endif

#ifdef JSON_MEMORY_CALLBACKS
	class JSONMemory {
	public:
		static void * json_malloc(size_t siz);
		static void * json_realloc(void * ptr, size_t siz);
		static void json_free(void * ptr);
		static void registerMemoryCallbacks(json_malloc_t mal, json_realloc_t real, json_free_t fre);
	};

	template <typename T> static inline T * json_malloc(size_t count){
		return (T *)JSONMemory::json_malloc(sizeof(T) * count);
	}

	template <typename T> static inline T * json_realloc(T * ptr, size_t count){
		return (T *)JSONMemory::json_realloc(ptr, sizeof(T) * count);
	}

	template <typename T> static inline void libjson_free(T * JSON_FREE_PASSTYPE ptr){
		JSONMemory::json_free(ptr);
		#if defined(JSON_DEBUG) || defined(JSON_SAFE)  //in debug or safe mode, set the pointer to 0 so that it can't be used again
			ptr = 0;
		#endif
	}
#else
	template <typename T>
	static inline T * json_malloc(size_t count){
		#ifdef JSON_DEBUG  //in debug mode, see if the malloc was successful
			void * result = malloc(count * sizeof(T));
			JSON_ASSERT(result, JSON_TEXT("out of memory"));
			#ifdef JSON_NULL_MEMORY
				memset(result, '\0', count  * sizeof(T));
			#endif
			return (T *)result;
		#else
			return (T *)malloc(count * sizeof(T));
		#endif
	}

	template <typename T>
	static inline void libjson_free(T * JSON_FREE_PASSTYPE ptr){
		free(ptr);
		#if defined(JSON_DEBUG) || defined(JSON_SAFE)  //in debug or safe mode, set the pointer to 0 so that it can't be used again
			ptr = 0;
		#endif
	}

	template <typename T>
	static inline T * json_realloc(T * ptr, size_t count){
		#ifdef JSON_DEBUG  //in debug mode, check the results of realloc to be sure it was successful
			void * result = realloc(ptr, count * sizeof(T));
			JSON_ASSERT(result, JSON_TEXT("out of memory"));
			#ifdef JSON_NULL_MEMORY
				memset(result, '\0', count  * sizeof(T));
			#endif
			return (T *)result;
		#else
			return (T *)realloc(ptr, count * sizeof(T));
		#endif
	}
#endif

#ifdef JSON_MEMORY_MANAGE
	#include <map>
	class JSONNode;
	struct auto_expand {
		auto_expand(void) : mymap() {}
		~auto_expand(void){ purge(); }
		void purge(void);
		inline void clear(void){ purge(); mymap.clear(); }
		inline void * insert(void * ptr){ mymap[ptr] = ptr; return ptr; }
		inline void remove(void * ptr){ 
			std::map<void *, void *>::iterator i = mymap.find(ptr);
			JSON_ASSERT(i != mymap.end(), JSON_TEXT("Removing a non-managed item"));
			mymap.erase(i);
		}
		std::map<void *, void *> mymap;
	};

	struct auto_expand_node {
		auto_expand_node(void) : mymap() {}
		~auto_expand_node(void){ purge(); }
		void purge(void);
		inline void clear(void){ purge(); mymap.clear(); }
		inline JSONNode * insert(JSONNode * ptr){ mymap[ptr] = ptr; return ptr; }
		inline void remove(void * ptr){ 
			std::map<void *, JSONNode *>::iterator i = mymap.find(ptr);
			if(i != mymap.end()) mymap.erase(i);
		}
		std::map<void *, JSONNode *> mymap;
	};
#endif

//The C++ way, use an self-deleting pointer and let the optimizer decide when it gets destroyed
template <typename T>
class json_auto {
	public:
		json_auto(void) : ptr(0){}
		json_auto(size_t count) : ptr(json_malloc<T>(count)) {}
		~json_auto(void){
			libjson_free<T>(ptr);
		}
		void set(T * p){
			ptr = p; 
		}
		T * ptr;
	private:
		json_auto(const json_auto &);
		json_auto & operator = (const json_auto &);
};

//Clears a string, if required, frees the memory
static inline void clearString(json_string & str){
	#ifdef JSON_LESS_MEMORY
		json_string().swap(str);
	#else
		str.clear();
	#endif
}

#endif