#ifndef MACADDRESS_H
#define MACADDRESS_H

#include <cstdint>
#include <string>

/**
 * describe a MAC-Address as 64-bit integer
 * or 8-bit access to all fields
 */
union MACAddressValue {

	struct {
		uint8_t h5;
		uint8_t h4;
		uint8_t h3;
		uint8_t h2;
		uint8_t h1;
		uint8_t h0;
	};

	uint64_t mac;

	/** initialize everything with zeros */
	MACAddressValue() : mac(0) {;}

};

class MACAddress {

private:

	/** the address as integer value */
	MACAddressValue value;

public:

	/** empty ctor */
	MACAddress() {
		;
	}

	/** copy ctor */
	MACAddress(const MACAddress& o) : value(o.value) {
		;
	}

	/** ctor form string (e.g. "xx:xx:xx:xx:xx:xx") */
	MACAddress(const std::string& str) {

		// sanity check
		if (str.size() != 17) {throw "invalid hex string length. must be 17";}

		value.mac = 0;	// all zeros
		value.h5 = hexWordToInt(str[ 0], str[ 1]);
		value.h4 = hexWordToInt(str[ 3], str[ 4]);
		value.h3 = hexWordToInt(str[ 6], str[ 7]);
		value.h2 = hexWordToInt(str[ 9], str[10]);
		value.h1 = hexWordToInt(str[12], str[13]);
		value.h0 = hexWordToInt(str[15], str[16]);

	}

	/** convert to hex-string ("xx:xx:xx:xx:xx:xx") */
	std::string asString() {

		std::string str = ":::::::::::::::::";

		intToHexStr(value.h5, &str[ 0]);
		intToHexStr(value.h4, &str[ 3]);
		intToHexStr(value.h3, &str[ 6]);
		intToHexStr(value.h2, &str[ 9]);
		intToHexStr(value.h1, &str[12]);
		intToHexStr(value.h0, &str[15]);

		return str;

	}

	/** get the mac address as a long-int value */
	uint64_t asLong() const {
		return value.mac;
	}

	/** equal? */
	bool operator == (const MACAddress& o) const {
		return o.asLong() == asLong();
	}

private:

	/** convert the given hex char [0-F] to an integer [0-15] */
	static uint8_t hexCharToInt(char hex) {

		// to upper case
		if (hex >= 'a') {hex -= 'a' - 'A';}

		// convert
		return (hex - '0' < 10) ? (hex - '0') : (hex - 'A' + 10);

	}

	/** convert the given hex-word to an integer */
	static uint8_t hexWordToInt(char hi, char lo) {
		return hexCharToInt(hi) << 4 | hexCharToInt(lo);
	}

	/** conver the given integer [0-15] to a hex char [0-F] */
	static char intToHexChar(const uint8_t val) {
		return (val < 10) ? ('0' + val) : ('A' - 10 + val);
	}

	/** insert two hex chars into the provided string buffer */
	static void intToHexStr(const uint8_t val, char* dst) {
		dst[0] = intToHexChar((val >> 4) & 0xF);
		dst[1] = intToHexChar((val >> 0) & 0xF);
	}

};


/** hash-method for MAC-Addresses */
namespace std {
	template <> struct hash<MACAddress> {
		std::size_t operator() (const MACAddress& mac) const {
			return std::hash<uint64_t>()(mac.asLong());
		}
	};
}

#endif // MACADDRESS_H
