#ifndef __USEFUL_HEADER
#define __USEFUL_HEADER




#include <sstream>
#include <string>
#include <vector>

#include <allegro5/allegro.h>
#include <allegro5/allegro_color.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_native_dialog.h>

#include <allegro_flare/vec2d.h>
#include <allegro_flare/vec3d.h>
#include <allegro_flare/color.h>

#include <math.h>


/*
#ifdef __GNUC__
#define VARIABLE_IS_NOT_USED __attribute__ ((unused))
#else
#define VARIABLE_IS_NOT_USED
#endif
*/


/*
#define __INLINE inline static __attribute__ ((used))
*/



#ifdef _MSC_VER
#define A_INLINE __inline
#else
#define A_INLINE static inline
#endif




// this are keycodes for the Logitech Powerpoint Remote
// (note: the remote toggles between two different keys when the screen_play key is pressed) 
#define LOGITECH_REMOTE_BACK 80
#define LOGITECH_REMOTE_FORWARD 81
#define LOGITECH_REMOTE_SCREEN_PLAY_1 51
#define LOGITECH_REMOTE_SCREEN_PLAY_2 59
#define LOGITECH_REMOTE_SCREEN_BLACK 73
inline bool logitec_is_screen_play(int allegro_key)
{
	return ((allegro_key == LOGITECH_REMOTE_SCREEN_PLAY_2) || (allegro_key == LOGITECH_REMOTE_SCREEN_PLAY_1));
}



static const float FULL_ROTATION = 6.28318531;
static const float TAU = 6.28318531;




#define VALIDATE_AL_INIT() if (!al_is_system_installed()) { std::cout << "error [" << __FUNCTION__ << "]: allegro not initialized." << std::endl; }



A_INLINE float degrees_to_radians(float deg) { return ALLEGRO_PI * deg / 180.0f; }
A_INLINE float radians_to_degrees(float rad) { return 180.0f / ALLEGRO_PI * rad; }

A_INLINE void draw_crosshair(float x, float y, ALLEGRO_COLOR color=al_color_name("white"))
{
	float half_size = 12;
	al_draw_line(x, y-half_size, x, y+half_size, color, 1.0);
	al_draw_line(x-half_size, y, x+half_size, y, color, 1.0);
}

A_INLINE void draw_crosshair(vec2d &point, ALLEGRO_COLOR color=al_color_name("white"))
{
	float half_size = 5;
	al_draw_line(point.x, point.y-half_size, point.x, point.y+half_size, color, 1.0);
	al_draw_line(point.x-half_size, point.y, point.x+half_size, point.y, color, 1.0);
}

// this one is lower down:
//A_INLINE void draw_crosshair(vec3d point, ALLEGRO_COLOR col=color::black, float size=10)

A_INLINE float distance(float x1, float y1, float x2, float y2)
{
      return sqrt( ((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)) ) ;
}

A_INLINE float distance(const vec2d &point1, const vec2d &point2)
{
      return sqrt( ((point1.x - point2.x) * (point1.x - point2.x)) + ((point1.y - point2.y) * (point1.y - point2.y)) ) ;
}

A_INLINE float distance(const vec2d *point1, const vec2d *point2)
{
      return sqrt( ((point1->x - point2->x) * (point1->x - point2->x)) + ((point1->y - point2->y) * (point1->y - point2->y)) ) ;
}

A_INLINE float manhattan_distance(const vec2d *point1, const vec2d *point2)
{
	// may consider writing out the abs function
	return abs(point2->x - point1->x) + abs(point2->y - point1->y);
}

A_INLINE float manhattan_distance(float x1, float y1, float x2, float y2)
{
	// may consider writing out the abs function
	return abs(x2 - x1) + abs(y2 - y1);
}

A_INLINE float distance_squared(const vec2d &point1, const vec2d &point2)
{
	return ((point1.x - point2.x) * (point1.x - point2.x)) + ((point1.y - point2.y) * (point1.y - point2.y));
}

A_INLINE float random_float(float min, float max)
{
    return ((float) rand()/RAND_MAX)*(max-min) + min;
}

template<class T>
A_INLINE T random_element(std::vector<T> &elements)
{
	return elements[random_int(0, elements.size()+i)];
}

template<class T>
A_INLINE T random_element(T elements[], int size)
{
	return elements[random_int(0, size-1)];
}

A_INLINE double random_double(double min, double max)
{
    return ((double) rand()/RAND_MAX)*(max-min) + min;
}

A_INLINE int random_int(int min, int max)
{
    return rand()%(max-min+1) + min;
}

A_INLINE bool random_bool()
{
    return (rand()%2 == 1);
}

A_INLINE int random_sign()
{
    if (random_bool()) return 1;
	else return -1;
}

// this random_char() function has not been tested
/*

static A_INLINE char random_char(char min=0, char max=255)
{
	return (char)(rand()%(max-min+1) + min);
}

*/

A_INLINE unsigned char random_letter(bool lower)
{
	if (lower) return (unsigned char)(rand()%26 + 'a');
	return (unsigned char)(rand()%26 + 'A');
}


A_INLINE unsigned char random_letter_or_number()
{
	// aaaaggghhh :[ not fully tested
	int num = random_int(0, 10+26+26); // 10 digits, 26 uppercase, 26 lowercase
	if (num <= 10) return (unsigned char)(rand()%10 + '0');
	else if ((num-10) <= 26) return (unsigned char)(rand()%26 + 'A');
	else if ((num-10-26) <= 26) return (unsigned char)(rand()%26 + 'a');
	return '-';
}


A_INLINE std::string random_string(unsigned int length)
{
	std::string return_str;
	for (unsigned i=0; i<length; i++)
		return_str += random_letter_or_number();
	return return_str;
}


A_INLINE ALLEGRO_COLOR random_color()
{
	return al_map_rgb(random_int(0, 255), random_int(0, 255), random_int(0, 255));
}


// returns a point projected onto an axis
A_INLINE vec2d project(vec2d &point, vec2d &axis)
{
	float somethun = (point.x * axis.x + point.y * axis.y)
	               / (pow(axis.x, 2) + pow(axis.y, 2));
	return vec2d(somethun * axis.x, somethun * axis.y);
}

A_INLINE vec2d rotate_point(vec2d point, float angle)
{
	return vec2d(point.x*cos(angle) - point.y*sin(angle), point.x*sin(angle) + point.y*cos(angle));
}

// reflect a point along an axis
A_INLINE vec2d reflect(vec2d &point, const vec2d &axis)
{
    float d = point * axis;
	return point - 2 * d * axis;
}

A_INLINE vec3d reflect(vec3d &point, const vec3d &axis) 
{

	return -2 * (point * axis) * axis + point;

	//http://www.3dkingdoms.com/weekly/weekly.php?a=2
	// V is the "velocity" vector (or point, in this case)
	// N is the normal of the plane (axis, in this case)
	//b * ( -2*(V dot N)*N + V )
	// where b is bounce
}

// returns a point projected onto an axis
// I HAVE NO IDEA IF THIS WORKS, I'M JUST TAKING THE 2D ONE AND
// EXPANDING IT TO THE 3RD DIMENTION, LOL, seems legit... :J
/*
A_INLINE vec3d project(vec3d &point, vec3d &axis)
{
	float somethun = (point.x * axis.x + point.y * axis.y + point.z * axis.z)
	               / (pow(axis.x, 2) + pow(axis.y, 2) + pow(axis.z, 2));
	return vec3d(somethun * axis.x, somethun * axis.y, somethun * axis.z);
}
*/

A_INLINE float dot_product(vec3d A, vec3d B)
{
	return A * B;
}

A_INLINE vec3d project(vec3d point, vec3d point_on_plane, vec3d normal_of_plane)
{
	return point - dot_product(point - point_on_plane, normal_of_plane) * normal_of_plane;
}

A_INLINE vec3d cross_product(vec3d A, vec3d B)
{
	//vec3d vector;
	//vector.x = A.y*B.z - B.y*A.z;
	//vector.y = B.x*A.z - A.x*B.z;
	//vector.z = A.x*B.y - A.y*B.x; 
	//return vector;
	return vec3d(A.y*B.z - B.y*A.z, B.x*A.z - A.x*B.z, A.x*B.y - A.y*B.x);
}

template<class T>
A_INLINE T limit(const T &range1, const T &range2, const T &val)
{
	float min = (range1 < range2) ? range1 : range2;
	float max = (range1 > range2) ? range1 : range2;
	if (val < min) return min;
	if (val > max) return max;
	return val;
}

template<class T>
A_INLINE bool in_range(const T &min, const T &max, const T &val)
{
	if (val < min) return false;
	if (val > max) return false;
	return true;
}

template<class T>
A_INLINE T mid(const T &val1, const T &val2)
{
	return (max(val1, val2) - min(val1, val2)) / 2.0 + min(val1, val2);
}

A_INLINE float round(float val)
{
	return floor(val + 0.5f);
}







A_INLINE ALLEGRO_VERTEX build_vertex(float x, float y, float z, ALLEGRO_COLOR col, float u, float v)
{
	ALLEGRO_VERTEX vertex;
	vertex.x = x, vertex.y = y, vertex.z = z, vertex.color = col, vertex.u = u, vertex.v = v;
	return vertex;
}

A_INLINE void draw_crosshair(vec3d point, ALLEGRO_COLOR col=color::black, float size=10)
{
	ALLEGRO_VERTEX v[6];
	float hsize = size/2;

	for (unsigned i=0; i<6; i++)
		v[i] = build_vertex(point.x, point.y, point.z, col, 0, 0);

	v[0].x -= hsize;
	v[1].x += hsize;

	v[2].y -= hsize;
	v[3].y += hsize;

	v[4].z -= hsize;
	v[5].z += hsize;

	al_draw_prim(&v, NULL, NULL, 0, 6, ALLEGRO_PRIM_LINE_LIST);
}

A_INLINE void draw_textured_rectangle(float x, float y, float w, float h, ALLEGRO_BITMAP *texture, const ALLEGRO_COLOR &color=color::white)
{
	ALLEGRO_VERTEX v[4];
	v[0] = build_vertex(x, y, 0, color, 0, 0);
	v[1] = build_vertex(w, y, 0, color, w, 0);
	v[2] = build_vertex(w, h, 0, color, w, h);
	v[3] = build_vertex(x, h, 0, color, 0, h);

	//al_draw_filled_rounded_rectangle(0, 0, placement.w, placement.h, 3, 3, background_color);

	al_draw_prim(v, NULL, texture, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
}

A_INLINE void draw_offset_textured_rectangle(float x, float y, float w, float h, float offset_x, float offset_y, ALLEGRO_BITMAP *texture, const ALLEGRO_COLOR &color=color::white)
{
	ALLEGRO_VERTEX v[4];
	v[0] = build_vertex(x, y, 0, color, 0+offset_x, 0+offset_y);
	v[1] = build_vertex(w, y, 0, color, w+offset_x, 0+offset_y);
	v[2] = build_vertex(w, h, 0, color, w+offset_x, h+offset_y);
	v[3] = build_vertex(x, h, 0, color, 0+offset_x, h+offset_y);

	//al_draw_filled_rounded_rectangle(0, 0, placement.w, placement.h, 3, 3, background_color);

	al_draw_prim(v, NULL, texture, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
}

A_INLINE void draw_stretched_bitmap(float x, float y, float w, float h, ALLEGRO_BITMAP *bitmap, int flip_flags=NULL, ALLEGRO_COLOR color=color::white)
{
	al_draw_tinted_scaled_bitmap(bitmap, color, 0, 0, al_get_bitmap_width(bitmap), al_get_bitmap_height(bitmap),
		x, y, w, h, flip_flags);
}

#include <allegro_flare/motion_control.h>

A_INLINE void animate_color(MotionManager *motion, ALLEGRO_COLOR *dest_color, const ALLEGRO_COLOR start, const ALLEGRO_COLOR end, double start_time, double duration, interpolator::interpolator_func_t interpolator_func)
{
	motion->clear_animations_on(&dest_color->r);
	motion->clear_animations_on(&dest_color->g);
	motion->clear_animations_on(&dest_color->b);
	motion->clear_animations_on(&dest_color->a);

	motion->animate(&dest_color->r, start.r, end.r, start_time, start_time+duration, interpolator_func, NULL, NULL);
	motion->animate(&dest_color->g, start.g, end.g, start_time, start_time+duration, interpolator_func, NULL, NULL);
	motion->animate(&dest_color->b, start.b, end.b, start_time, start_time+duration, interpolator_func, NULL, NULL);
	motion->animate(&dest_color->a, start.a, end.a, start_time, start_time+duration, interpolator_func, NULL, NULL);
}









//#include <allegro5/allegro.h>

A_INLINE bool key_pressed(int al_keycode)
{
	ALLEGRO_KEYBOARD_STATE keyboard_state;
	al_get_keyboard_state(&keyboard_state);
	return al_key_down(&keyboard_state, al_keycode); 
}






template<class T>
A_INLINE std::string tostring(T val)
{
	std::ostringstream s;
	s << val;
	return s.str();
}







#include <vector>
A_INLINE std::vector<int> to_int(const std::vector<std::string> &arr)
{
	std::vector<int> result;
	for (int i=0; i<(int)arr.size(); i++)
		result.push_back(atoi(arr[i].c_str()));
	return result;
}






template<class Iter, class T>
A_INLINE Iter binary_find(Iter begin, Iter end, T val)
{
    // Finds the lower bound in at most log(last - first) + 1 comparisons
    Iter i = std::lower_bound(begin, end, val);

    if (i != end && *i == val)
        return i; // found
    else
        return end; // not found
}





// php-like functions



namespace php
{
	A_INLINE std::vector<std::string> explode(const std::string &delimiter, const std::string &str);
	A_INLINE std::string implode(const std::string &delimiter, const std::vector<std::string> &parts);
	A_INLINE std::string strtoupper(std::string input);
	A_INLINE std::string str_replace(const std::string &search, const std::string &replace, std::string &subject);
	A_INLINE size_t strpos(const std::string &haystack, const std::string &needle, int offset=0);
	A_INLINE bool file_exists(std::string filename);
	A_INLINE std::string file_get_contents(std::string filename);
	A_INLINE bool file_put_contents(std::string filename, std::string contents);
	A_INLINE std::string ltrim(std::string &s);
	A_INLINE std::string rtrim(std::string &s);
	A_INLINE std::string trim(std::string &s);
	A_INLINE char *url_encode(char *str);
	A_INLINE char *url_decode(char *str);
	//A_INLINE std::vector<std::string> glob(pattern) //< perhaps one day...
	A_INLINE std::string number_format(double number, int precision);
}


#include <locale>
A_INLINE bool is_number(const std::string& s)
{
	std::locale loc;
    std::string::const_iterator it = s.begin();
    while (it != s.end() && std::isdigit(*it, loc)) ++it;
    return !s.empty() && it == s.end();
}


#include <iomanip>
A_INLINE std::string php::number_format(double number, int precision)
{
	std::stringstream ss;
	ss << std::setprecision(precision) << number;
	return ss.str();
}


#include <vector>
A_INLINE std::vector<std::string> php::explode(const std::string &delimiter, const std::string &str)
{
	// note: this function skips multiple delimiters, e.g. it will not return return empty tokens
    std::vector<std::string> arr;

    int strleng = str.length();
    int delleng = delimiter.length();
    if (delleng==0)
        return arr;//no change

    int i=0;
    int k=0;
    while(i<strleng)
    {
        int j=0;
        while (i+j<strleng && j<delleng && str[i+j]==delimiter[j])
            j++;
        if (j==delleng)//found delimiter
        {
            arr.push_back(str.substr(k, i-k));
            i+=delleng;
            k=i;
        }
        else
        {
            i++;
        }
    }
    arr.push_back(str.substr(k, i-k));
    return arr;
}



// UNTESTED
#include <vector>
#include <sstream>
A_INLINE std::string php::implode(const std::string &delimiter, const std::vector<std::string> &parts)
{
	std::stringstream ss;
	for (unsigned i=0; i<parts.size(); i++)
	{
		ss << parts[i];
		if (i < (parts.size()-1)) ss << delimiter;
	}
	return ss.str();
}



// UNTESTED
#include <string>
A_INLINE std::string php::str_replace(const std::string &search, const std::string &replace, std::string &subject)
{
    std::string buffer;

    int sealeng = search.length();
    int strleng = subject.length();

    if (sealeng==0)
        return subject;//no change

    for(int i=0, j=0; i<strleng; j=0 )
    {
        while (i+j<strleng && j<sealeng && subject[i+j]==search[j])
            j++;
        if (j==sealeng)//found 'search'
        {
            buffer.append(replace);
            i+=sealeng;
        }
        else
        {
            buffer.append( &subject[i++], 1);
        }
    }
    subject = buffer;
    return subject;
}




#include <string>
// not tested, but from: http://www.zedwood.com/article/116/cpp-strpos-function
A_INLINE size_t php::strpos(const std::string &haystack, const std::string &needle, int offset)
{
    int sleng = haystack.length();
    int nleng = needle.length();

    if (sleng==0 || nleng==0)
        return std::string::npos;

    //for(int i=0, j=0; i<sleng; j=0, i++ )
    for(int i=offset, j=0; i<sleng; j=0, i++ )
    {
        while (i+j<sleng && j<nleng && haystack[i+j]==needle[j])
            j++;
        if (j==nleng)
            return i;
    }
    return std::string::npos;
}




#include <fstream>
#include <string>
A_INLINE bool php::file_exists(std::string filename)
{
	std::fstream file(filename.c_str());
	if (!file) return false;
	return true;
}




#include <fstream>
#include <string>
A_INLINE std::string php::file_get_contents(std::string filename)
{
	std::ifstream file(filename.c_str());
	std::string input = "";
	if (!file) return "";
	char ch;
	while (file.get(ch)) input.append(1, ch);
	if (!file.eof()) return ""; // strange error
	file.close();
	return input;
}



#include <fstream>
#include <iostream>
#include <string>
A_INLINE bool php::file_put_contents(std::string filename, std::string contents)
{
	std::ofstream file;
	file.open(filename.c_str());
	if (!file.is_open()) return false;
	file << contents.c_str();
	file.close();
	return true;
}



A_INLINE std::string php::strtoupper(std::string input)
{
	for (unsigned i=0; i<input.size(); i++)
		if ((input.at(i) <= 122) && (input.at(i) >= 97)) input[i] = input[i] - 32;
	return input;
}



#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>




A_INLINE void rtrim2(std::string& s, const std::string& delimiters=" \f\n\r\t\v\0")
{
   s.erase( s.find_last_not_of( delimiters ) + 1 );
}
 
A_INLINE void ltrim2(std::string& s, const std::string& delimiters=" \f\n\r\t\v\0")
{
   s.erase( 0, s.find_first_not_of( delimiters ) );
}
 
A_INLINE void trim2(std::string& s, const std::string& delimiters=" \f\n\r\t\v\0")
{
    s.erase( s.find_last_not_of( delimiters ) + 1 ).erase( 0, s.erase( s.find_last_not_of( delimiters ) + 1 ).find_first_not_of( delimiters ) );
}

A_INLINE std::string trim3(std::string original)
{
	trim2(original);
	return original;
}



// trim from start
A_INLINE std::string php::ltrim(std::string &s)
{
	s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
	return s;
}

// trim from end
A_INLINE std::string php::rtrim(std::string &s)
{
	s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
	return s;
}

// trim from both ends
A_INLINE std::string php::trim(std::string &s)
{
	return ltrim(rtrim(s));
}






// next 4 functions are from
// http://www.geekhideout.com/urlcode.shtml

/* Converts a hex character to its integer value */
A_INLINE char __from_hex(char ch) {
  return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}

/* Converts an integer value to its hex character*/
A_INLINE char __to_hex(char code) {
  static char hex[] = "0123456789abcdef";
  return hex[code & 15];
}

/* Returns a url-encoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *php::url_encode(char *str) {
  char *pstr = str, *buf = (char *)malloc(strlen(str) * 3 + 1), *pbuf = buf;
  while (*pstr) {
    if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') 
      *pbuf++ = *pstr;
    else if (*pstr == ' ') 
      *pbuf++ = '+';
    else 
      *pbuf++ = '%', *pbuf++ = __from_hex(*pstr >> 4), *pbuf++ = __to_hex(*pstr & 15);
    pstr++;
  }
  *pbuf = '\0';
  return buf;
}

/* Returns a url-decoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *php::url_decode(char *str) 
{
  char *pstr = str, *buf = (char *)malloc(strlen(str) + 1), *pbuf = buf;
  while (*pstr) {
    if (*pstr == '%') {
      if (pstr[1] && pstr[2]) {
        *pbuf++ = __to_hex(pstr[1]) << 4 | __from_hex(pstr[2]);
        pstr += 2;
      }
    } else if (*pstr == '+') { 
      *pbuf++ = ' ';
    } else {
      *pbuf++ = *pstr;
    }
    pstr++;
  }
  *pbuf = '\0';
  return buf;
}







// my own functions
#include <tuple>

A_INLINE std::vector<std::tuple<int, std::string>> get_delimited_text(std::string text, std::string start_delim, std::string end_delim)
{
// tuple<position_of_the_string_content_that_was_found, content>
// example usaeg
//	std::string text = "In a world [where one man must fight to survive] all stuff is ok.";
//	std::vector<std::tuple<int, std::string>> results = get_delimited_text(text, "[", "]");
//	std::cout << results.size() << " matches found" << std::endl;
//	for (unsigned i=0; i<results.size(); i++)
//		std::cout << i << ": (" << std::get<0>(results[i]) << ") \"" << std::get<1>(results[i]) << "\"" << std::endl;

	std::vector<std::tuple<int, std::string>> results;
	size_t offset = 0;

	while(offset < text.size())
	{
		size_t start_pos = php::strpos(text, start_delim, offset);

		if (start_pos == std::string::npos) return results;

		size_t end_pos = php::strpos(text, end_delim, start_pos+start_delim.size());

		if (end_pos == std::string::npos) return results;

		if (start_pos == std::string::npos || end_pos == std::string::npos) return results;
	
		results.push_back(std::make_tuple(start_pos+start_delim.size(), text.substr(start_pos+start_delim.size(), end_pos - start_pos - start_delim.size())));

		offset = end_pos + end_delim.size();
	}

	return results;
}



A_INLINE bool basically_equal(float v1, float v2, float threshold=0.00001f)
{
	if (abs(v1 - v2) > threshold) return false;
	return true;
}



A_INLINE std::vector<std::string> get_directory_listing(std::string directory)
{
	VALIDATE_AL_INIT();

	std::vector<std::string> results;
    ALLEGRO_FS_ENTRY* dir = al_create_fs_entry(directory.c_str());

    if(al_open_directory(dir))
    {
        ALLEGRO_FS_ENTRY* file;
        while(file = al_read_directory(dir))
        {
			results.push_back(al_get_fs_entry_name(file));
            al_destroy_fs_entry(file);
        }
    }
	else
	{
		std::cout << "could not open directory \"" << directory << "\"" << std::endl;
	}

    al_destroy_fs_entry(dir);

	return results;
}






// javascript-like functions


namespace javascript
{

A_INLINE void alert(std::string message)
{
	al_show_native_message_box(al_get_current_display(), "Alert", "", message.c_str(), NULL, ALLEGRO_MESSAGEBOX_WARN);
}

};






// font symbol drawing functions

#include <allegro5/allegro_font.h>

A_INLINE void draw_unicode_char(ALLEGRO_FONT *font, ALLEGRO_COLOR color, int32_t icon, int flags, float x, float y)
{
	static ALLEGRO_USTR *ustr = NULL;
	if (!ustr) ustr = al_ustr_new("");
	al_ustr_set_chr(ustr, 0, icon);
	al_draw_ustr(font, color, x, y, flags, ustr);
}







// encoding / escaping / converting strings to



#include <string>
#include <sstream>
A_INLINE std::string get_xml_encoded_ustr(ALLEGRO_USTR *ustr)
{
	std::stringstream output;
	//al_ustr_get(const ALLEGRO_USTR *ub, int pos)
	for (unsigned i=0; i<al_ustr_length(ustr); i++)
	{
		output << "&#x" << al_ustr_get(ustr, i) << ";";
	}
	return output.str();
}


#include <string>
#include <map>
#include <iostream>
A_INLINE std::string escape_xml_chars(std::string xml)
{
    std::map<char, std::string> transformations;
    transformations['&']  = std::string("&amp;");
    transformations['\''] = std::string("&apos;");
    transformations['"']  = std::string("&quot;");
    transformations['>']  = std::string("&gt;");
    transformations['<']  = std::string("&lt;");

    // Build list of characters to be searched for.
    //
    std::string reserved_chars;
    for (auto ti = transformations.begin(); ti != transformations.end(); ti++)
    {
        reserved_chars += ti->first;
    }

    size_t pos = 0;
    while (std::string::npos != (pos = xml.find_first_of(reserved_chars, pos)))
    {
        xml.replace(pos, 1, transformations[xml[pos]]);
        pos++;
    }

	return xml;
}


#include <string>
#include <sstream>
A_INLINE std::string as_hex(int32_t num)
{
	std::stringstream stream;
	stream << std::hex << num;
	return stream.str();
}




#endif
