#ifndef __XENTAX_H
#define __XENTAX_H

// Windows Headers
#define NOMINMAX
#pragma warning (disable : 4307)
#pragma warning (disable : 4996)
#include<windows.h>
#include<windowsx.h>
#include<commctrl.h>
#include<shlobj.h>
#include<tchar.h>
#include<ddraw.h>
#include<atlbase.h>
#include<comdef.h>
#include<msxml6.h>
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "msxml6.lib")

// Standard Headers
#ifndef RC_INVOKED
#include<iostream>
#include<iomanip>
#include<fstream>
#include<sstream>
#include<algorithm>
#include<deque>
#include<map>
#include<set>
#include<vector>
#include<string>
#endif

// Boost Headers
#ifndef RC_INVOKED
#include<boost/shared_array.hpp>
#include<boost/shared_ptr.hpp>
#include<boost/bimap.hpp>
#endif

// ZLIB Headers
#ifndef RC_INVOKED
#include "zlib.h"
#pragma comment(lib, "zdll.lib")
#endif

// Data Types
#ifndef RC_INVOKED
typedef __int8  sint08;
typedef __int16 sint16;
typedef __int32 sint32;
typedef __int64 sint64;
typedef unsigned __int8  uint08;
typedef unsigned __int16 uint16;
typedef unsigned __int32 uint32;
typedef unsigned __int64 uint64;
typedef float real32;
typedef double real64;
#endif

/*
** MESSAGE FUNCTIONS
*/
#ifndef RC_INVOKED

inline bool error(const char* str)
{
 std::cout << str << std::endl;
 return false;
}

inline bool message(const char* str)
{
 std::cout << str << std::endl;
 return true;
}

template<class T>
inline T clamp(T value, T v0, T v1) 
{
 if(value < v0) return v0;
 else if(v1 < value) return v1;
 return value;
}

#endif

/*
** BYTE ORDER FUNCTIONS
*/
#ifndef RC_INVOKED

template<class T>
inline void reverse_byte_order(T* data)
{
 unsigned char* ptr = reinterpret_cast<unsigned char*>(data);
 std::reverse(ptr, ptr + sizeof(T)); 
}

template<class T>
inline void reverse_byte_order(T* data, size_t elem)
{
 for(size_t i = 0; i < elem; i++) {
     unsigned char* ptr = reinterpret_cast<unsigned char*>(&data[i]);
     std::reverse(ptr, ptr + sizeof(T));
    }
}

#endif

/*
** DATA CONVERSION FUNCTIONS
*/
#ifndef RC_INVOKED

real32 float_16_to_32(unsigned short value);

inline uint32 interpret_as_uint32(real32 x)
{
 return *(reinterpret_cast<uint32*>(&x));
}

inline uint64 interpret_as_uint64(real64 x)
{
 return *(reinterpret_cast<uint64*>(&x));
}

inline real32 interpret_as_real32(uint32 x)
{
 return *(reinterpret_cast<real32*>(&x));
}

inline real64 interpret_as_real64(uint64 x)
{
 return *(reinterpret_cast<real64*>(&x));
}

#endif

/*
** ENDIAN FUNCTIONS
*/
#ifndef RC_INVOKED

inline sint08 LE_read_sint08(std::istream& ifile)
{
 sint08 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline sint08 BE_read_sint08(std::istream& ifile)
{
 sint08 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline uint08 LE_read_uint08(std::istream& ifile)
{
 uint08 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline uint08 BE_read_uint08(std::istream& ifile)
{
 uint08 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline sint16 LE_read_sint16(std::istream& ifile)
{
 sint16 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline sint16 BE_read_sint16(std::istream& ifile)
{
 sint16 temp;
 ifile.read((char*)&temp, sizeof(temp));
 reverse_byte_order(&temp);
 return temp;
}

inline uint16 LE_read_uint16(std::istream& ifile)
{
 uint16 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline uint16 BE_read_uint16(std::istream& ifile)
{
 uint16 temp;
 ifile.read((char*)&temp, sizeof(temp));
 reverse_byte_order(&temp);
 return temp;
}

inline sint32 LE_read_sint32(std::istream& ifile)
{
 sint32 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline sint32 BE_read_sint32(std::istream& ifile)
{
 sint32 temp;
 ifile.read((char*)&temp, sizeof(temp));
 reverse_byte_order(&temp);
 return temp;
}

inline uint32 LE_read_uint32(std::istream& ifile)
{
 uint32 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline uint32 BE_read_uint32(std::istream& ifile)
{
 uint32 temp;
 ifile.read((char*)&temp, sizeof(temp));
 reverse_byte_order(&temp);
 return temp;
}

inline sint64 LE_read_sint64(std::istream& ifile)
{
 sint64 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline sint64 BE_read_sint64(std::istream& ifile)
{
 sint64 temp;
 ifile.read((char*)&temp, sizeof(temp));
 reverse_byte_order(&temp);
 return temp;
}

inline uint64 LE_read_uint64(std::istream& ifile)
{
 uint64 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline uint64 BE_read_uint64(std::istream& ifile)
{
 uint64 temp;
 ifile.read((char*)&temp, sizeof(temp));
 reverse_byte_order(&temp);
 return temp;
}

inline sint08 LE_read_sint08(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_sint08(ifile);
}

inline sint08 BE_read_sint08(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_sint08(ifile);
}

inline uint08 LE_read_uint08(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_uint08(ifile);
}

inline uint08 BE_read_uint08(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_uint08(ifile);
}

inline sint16 LE_read_sint16(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_sint16(ifile);
}

inline sint16 BE_read_sint16(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_sint16(ifile);
}

inline uint16 LE_read_uint16(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_uint16(ifile);
}

inline uint16 BE_read_uint16(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_uint16(ifile);
}

inline sint32 LE_read_sint32(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_sint32(ifile);
}

inline sint32 BE_read_sint32(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_sint32(ifile);
}

inline uint32 LE_read_uint32(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_uint32(ifile);
}

inline uint32 BE_read_uint32(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_uint32(ifile);
}

inline sint64 LE_read_sint64(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_sint64(ifile);
}

inline sint64 BE_read_sint64(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_sint64(ifile);
}

inline uint64 LE_read_uint64(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_uint64(ifile);
}

inline uint64 BE_read_uint64(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_uint64(ifile);
}

inline real32 LE_read_real16(std::istream& ifile)
{
 unsigned short temp;
 ifile.read((char*)&temp, sizeof(temp));
 return float_16_to_32(temp);
}

inline real32 BE_read_real16(std::istream& ifile)
{
 unsigned short temp = LE_read_uint16(ifile);
 reverse_byte_order(&temp);
 return float_16_to_32(temp);
}

inline real32 LE_read_real32(std::istream& ifile)
{
 real32 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline real32 BE_read_real32(std::istream& ifile)
{
 real32 temp = LE_read_real32(ifile);
 reverse_byte_order(&temp);
 return temp;
}

inline real64 LE_read_real64(std::istream& ifile)
{
 real64 temp;
 ifile.read((char*)&temp, sizeof(temp));
 return temp;
}

inline real64 BE_read_real64(std::istream& ifile)
{
 real64 temp = LE_read_real64(ifile);
 reverse_byte_order(&temp);
 return temp;
}

inline real32 LE_read_real16(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_real16(ifile);
}

inline real32 BE_read_real16(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_real16(ifile);
}

inline real32 LE_read_real32(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_real32(ifile);
}

inline real32 BE_read_real32(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_real32(ifile);
}

inline real64 LE_read_real64(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return LE_read_real64(ifile);
}

inline real64 BE_read_real64(std::istream& ifile, unsigned int offset)
{
 ifile.seekg(offset);
 return BE_read_real64(ifile);
}

template<class T>
inline bool LE_read_array(std::istream& ifile, T* data, size_t n)
{
 ifile.read((char*)&data[0], n*sizeof(T));
 if(ifile.fail()) return false;
 return true;
}

template<class T>
inline bool BE_read_array(std::istream& ifile, T* data, size_t n)
{
 ifile.read((char*)&data[0], n*sizeof(T));
 if(ifile.fail()) return false;
 reverse_byte_order(data, n);
 return true;
}

template<class T>
inline bool LE_read_array(std::istream& ifile, unsigned int offset, T* data, unsigned int n)
{
 ifile.seekg(offset);
 if(ifile.fail()) return false;
 return LE_read_array(ifile, data, n);
}

template<class T>
inline bool BE_read_array(std::istream& ifile, unsigned int offset, T* data, unsigned int n)
{
 ifile.seekg(offset);
 if(ifile.fail()) return false;
 return BE_read_array(ifile, data, n);
}

inline void LE_write_uint08(std::ostream& ofile, uint08 value)
{
 ofile.write((char*)&value, sizeof(value));
}

inline void BE_write_uint08(std::ostream& ofile, uint08 value)
{
 reverse_byte_order(&value);
 ofile.write((char*)&value, sizeof(value));
}

inline void LE_write_uint16(std::ostream& ofile, uint16 value)
{
 ofile.write((char*)&value, sizeof(value));
}

inline void BE_write_uint16(std::ostream& ofile, uint16 value)
{
 reverse_byte_order(&value);
 ofile.write((char*)&value, sizeof(value));
}

inline void LE_write_uint32(std::ostream& ofile, uint32 value)
{
 ofile.write((char*)&value, sizeof(value));
}

inline void BE_write_uint32(std::ostream& ofile, uint32 value)
{
 reverse_byte_order(&value);
 ofile.write((char*)&value, sizeof(value));
}

inline void LE_write_uint64(std::ostream& ofile, uint64 value)
{
 ofile.write((char*)&value, sizeof(value));
}

inline void BE_write_uint64(std::ostream& ofile, uint64 value)
{
 reverse_byte_order(&value);
 ofile.write((char*)&value, sizeof(value));
}

inline void LE_write_real32(std::ostream& ofile, real32 value)
{
 ofile.write((char*)&value, sizeof(value));
}

inline void BE_write_real32(std::ostream& ofile, real32 value)
{
 reverse_byte_order(&value);
 ofile.write((char*)&value, sizeof(value));
}

template<class T>
inline bool BE_write_array(std::ostream& ofile, T* data, size_t n)
{
 // copy and reverse data
 boost::shared_array<T> copy(new T[n]);
 for(size_t i = 0; i < n; i++) copy[i] = data[i];
 reverse_byte_order(copy.get(), n);
 // save data
 ofile.write((char*)&data[0], n*sizeof(T));
 if(ofile.fail()) return false;
 return true;
}

#endif

/*
** DATA ALIGNMENT FUNCTIONS
*/
#ifndef RC_INVOKED

template<class T>
inline T align02(T value)
{
 return ((value + 0x01) & (~(0x01)));
}

template<class T>
inline T align04(T value)
{
 return ((value + 0x03) & (~(0x03)));
}

template<class T>
inline T align08(T value)
{
 return ((value + 0x07) & (~(0x07)));
}

template<class T>
inline T align16(T value)
{
 return ((value + 0x0F) & (~(0x0F)));
}

template<class T>
inline T align32(T value)
{
 return ((value + 0x1F) & (~(0x1F)));
}

template<class T>
inline T align64(T value)
{
 return ((value + 0x3F) & (~(0x3F)));
}

#endif

/*
** STRING FUNCTIONS
*/
#ifndef RC_INVOKED

bool read_string(std::istream& ifile, char* data, size_t size);
bool read_string(std::istream& ifile, char* data, size_t size, char delimiter);
bool write_aligned_string_02(std::ofstream& ofile, const char* str);
bool write_aligned_string_04(std::ofstream& ofile, const char* str);

inline boost::shared_array<char> string_to_shared_array(const std::string& str)
{
 if(str.length() == 0) return boost::shared_array<char>();
 boost::shared_array<char> retval(new char[str.length() + 1]);
 memmove(retval.get(), str.c_str(), str.length() + 1);
 return retval;
}

#endif

/*
** MORTON COORDINATE FUNCTIONS
*/

inline uint32 dilate_2(uint32 r)
{
 r = (r | (r << 8)) & 0x00FF00FF;
 r = (r | (r << 4)) & 0x0F0F0F0F;
 r = (r | (r << 2)) & 0x33333333;
 r = (r | (r << 1)) & 0x55555555;
 return r;
}

inline uint32 array_to_morton(uint32 row, uint32 col) 
{
 return (dilate_2(col) | (dilate_2(row) << 1));
}

#endif
