#ifndef binpac_exception_h
#define binpac_exception_h

#include <stdint.h>
#include <inttypes.h>

namespace binpac {

class Exception
{
public:
	Exception(const char* m = 0)
		: msg_("binpac exception: ")
		{
		if ( m )
			append(m);
		// abort();
		}

	void append(string m) 		{ msg_ += m; }
	string msg() const		{ return msg_; }
	const char* c_msg() const 	{ return msg_.c_str(); }

protected:
	string msg_;
};

class ExceptionEnforceViolation : public Exception
{
public:
	ExceptionEnforceViolation(const char* where)
		{
		append(binpac_fmt("&enforce violation : %s", where));
		}
};

class ExceptionOutOfBound : public Exception
{
public:
	ExceptionOutOfBound(const char* where, int len_needed, int len_given)
		{
		append(binpac_fmt("out_of_bound: %s: %d > %d", 
			where, len_needed, len_given));
		}
};

class ExceptionInvalidCase : public Exception
{
public:
	ExceptionInvalidCase(const char* location, 
			int64_t index,
			const char *expected)
		: location_(location), 
		  index_(index), 
		  expected_(expected)
		{
		append(binpac_fmt("invalid case: %s: %" PRIi64 " (%s)",
			location, index, expected));
		}

protected:
	const char* location_;
	int64_t index_;
	string expected_;
};

class ExceptionInvalidCaseIndex : public Exception
{
public:
	ExceptionInvalidCaseIndex(const char* location, 
			int64_t index)
		: location_(location), 
		  index_(index)
		{
		append(binpac_fmt("invalid index for case: %s: %" PRIi64,
			location, index));
		}

protected:
	const char* location_;
	int64_t index_;
};

class ExceptionInvalidOffset : public Exception
{
public:
	ExceptionInvalidOffset(const char* location, 
			int min_offset, int offset)
		: location_(location), 
		  min_offset_(min_offset), offset_(offset)
		{
		append(binpac_fmt("invalid offset: %s: min_offset = %d, offset = %d",
			location, min_offset, offset));
		}

protected:
	const char* location_;
	int min_offset_, offset_;
};

class ExceptionStringMismatch : public Exception
{
public:
	ExceptionStringMismatch(const char* location, 
			const char *expected, const char *actual_data)
		{
		append(binpac_fmt("string mismatch at %s: \nexpected pattern: \"%s\"\nactual data: \"%s\"",
			location, expected, actual_data));
		}
};

class ExceptionInvalidStringLength : public Exception
{
public:
	ExceptionInvalidStringLength(const char* location, int len)
		{
		append(binpac_fmt("invalid length string: %s: %d",
			location, len));
		}
};

class ExceptionFlowBufferAlloc : public Exception
{
public:
	ExceptionFlowBufferAlloc(const char* reason)
		{
		append(binpac_fmt("flowbuffer allocation failed: %s", reason));
		}
};

}

#endif  // binpac_exception_h
