/*
 *
 *
 *    inaddr.h
 *
 *    C++ class to deal with IPv4 addresses.
 *
 *    =========================================================================
 *    
 *    Copyright (C) 1999-2013 by Matt Roberts, KK5JY.
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    =========================================================================
 *
 *    TODO:
 *       + Move the long inline functions into a .cc file and make a
 *         library from them.
 *       + Generalize the code to work with IPv4 and IPv6.
 *
 *
 */


#ifndef __TINADDR_H
    #define __TINADDR_H

    #define DOTTED_QUAD_LENGTH_MAX (16)

    #include <string>
    #include <iostream>
    #include <sstream>
    #include <cstdio>
    #include <cstring>

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>

    #include <my++/hostent.h>


	namespace my {

		/*
		 *
		 *    struct inet_address
		 *
		 *    Encapsulates an IP address and port. The port may or may not be
		 *    used, and should be set to zero if not.
		 *
		 */

		class inet_address {
			private:
				in_addr address;

			public:
				// Call this function to bind to any/all interfaces.
				bool set_any_addr (void) { address.s_addr = htonl (INADDR_ANY); return true; };
				#ifdef INADDR_NONE
				bool set_no_addr (void) { address.s_addr = htonl (INADDR_NONE); return true; };
				#endif /* INADDR_NONE */

				// call this to zero out the address field
				void clear (void) { address.s_addr = htonl (0x00000000); };

				// call this to get the C-style address
				void copy_to (in_addr *addr) const { memcpy(addr, &address, sizeof(in_addr)); };
				void copy_to (in_addr &addr) const { memcpy(&addr, &address, sizeof(in_addr)); };

				bool readHostnameFromString (const std::string &s);
				bool readAddressFromString (const std::string &s);
				bool readNetmaskFromString (const std::string &s);

				const inet_address &operator = (const inet_address &a);
				const inet_address &operator = (const in_addr &a);
				const inet_address &operator = (const uint32_t &w);
				const inet_address &operator = (const std::string &s);

				bool operator == (const inet_address &a) const;
				bool operator == (const in_addr &a) const;
				bool operator == (const uint32_t &w) const;
				bool operator == (const std::string &s) const;

				bool operator != (const inet_address &a) const;
				bool operator != (const in_addr &a) const;
				bool operator != (const uint32_t &w) const;
				bool operator != (const std::string &s) const;

				friend bool operator == (const in_addr &w, const inet_address &addr);
				friend bool operator == (const uint32_t &w, const inet_address &addr);
				friend bool operator == (const std::string &s, const inet_address &addr);

				friend bool operator != (const in_addr &w, const inet_address &addr);
				friend bool operator != (const uint32_t &w, const inet_address &addr);
				friend bool operator != (const std::string &s, const inet_address &addr);

				const inet_address operator | (const inet_address &a) const;
				const inet_address operator | (const in_addr &a) const;
				const inet_address operator | (const uint32_t &w) const;
				const inet_address operator | (const std::string &s) const;

				friend const inet_address operator | (const in_addr &addr1, const inet_address &addr2);
				friend const inet_address operator | (const uint32_t &w, const inet_address &addr);
				friend const inet_address operator | (const std::string &s, const inet_address &addr);

				const inet_address &operator |= (const inet_address &a);
				const inet_address &operator |= (const in_addr &a);
				const inet_address &operator |= (const uint32_t &w);
				const inet_address &operator |= (const std::string &s);

				const inet_address operator & (const inet_address &a) const;
				const inet_address operator & (const in_addr &a) const;
				const inet_address operator & (const uint32_t &w) const;
				const inet_address operator & (const std::string &s) const;

				friend const inet_address operator & (const in_addr &addr1, const inet_address &addr2);
				friend const inet_address operator & (const uint32_t &w, const inet_address &addr);
				friend const inet_address operator & (const std::string &s, const inet_address &addr);

				const inet_address &operator &= (const inet_address &a);
				const inet_address &operator &= (const in_addr &a);
				const inet_address &operator &= (const uint32_t &w);
				const inet_address &operator &= (const std::string &s);

				const inet_address operator >>  (const unsigned int i) const;
				const inet_address operator >>= (const unsigned int i);

				const inet_address operator <<  (const unsigned int i) const;
				const inet_address operator <<= (const unsigned int i);

				friend std::ostream &my::operator << (std::ostream &o, const inet_address &addr);
				friend std::istream &my::operator >> (std::istream &o, inet_address &addr);

				inet_address (void)                  { clear ();                          };
				inet_address (const inet_address &w) { address.s_addr = w.address.s_addr; };
				inet_address (const in_addr &w)      { address.s_addr = w.s_addr;         };
				inet_address (const uint32_t &w)     { address.s_addr = htonl(w);         };
				inet_address (const std::string &s)  { readHostnameFromString (s);        };

				~inet_address (void) { };

				// Conversion functions
				uint32_t c_hlong (void)  const { return ntohl (address.s_addr); };
				uint32_t c_nlong (void)  const { return       (address.s_addr); };
				uint32_t c_uint32 (void) const { return ntohl (address.s_addr); };

				std::string &to_string (std::string &s) const;

		};


		/*
		 *
		 *   inet_address::operator = (...)
		 *
		 */

		inline const inet_address &inet_address::operator = (const inet_address &a) {
			address.s_addr = a.address.s_addr;
			return *this;
		}

		inline const inet_address &inet_address::operator = (const in_addr &a) {
			address.s_addr = a.s_addr;
			return *this;
		}

		inline const inet_address &inet_address::operator = (const uint32_t &w) {
			address.s_addr = htonl(w);
			return *this;
		}

		inline const inet_address &inet_address::operator = (const std::string &s) {
			readAddressFromString (s);
			return *this;
		}


		/*
		 *
		 *   inet_address::operator == (...)
		 *
		 */

		inline bool inet_address::operator == (const inet_address &a) const {
			return (address.s_addr == a.address.s_addr);
		}

		inline bool inet_address::operator == (const in_addr &a) const {
			return (address.s_addr == a.s_addr);
		}

		inline bool inet_address::operator == (const uint32_t &w) const {
			return (address.s_addr == htonl(w));
		}

		inline bool inet_address::operator == (const std::string &s) const {
			inet_address a(s);
			return (address == a);
		}


		/*
		 *
		 *   inet_address::operator != (...)
		 *
		 */

		inline bool inet_address::operator != (const inet_address &a) const {
			return (address.s_addr != a.address.s_addr);
		}

		inline bool inet_address::operator != (const in_addr &a) const {
			return (address.s_addr != a.s_addr);
		}

		inline bool inet_address::operator != (const uint32_t &w) const {
			return (address.s_addr != htonl(w));
		}

		inline bool inet_address::operator != (const std::string &s) const {
			inet_address a(s);
			return (address != a);
		}


		/*
		 *
		 *   operator ==/!= (..., const inet_address &)
		 *
		 */

		inline bool operator == (const in_addr &addr1, const inet_address &addr2) {
			return (addr1.s_addr == addr2.address.s_addr);
		}

		inline bool operator == (const uint32_t &w, const inet_address &addr) {
			return (addr.address.s_addr == htonl(w));
		}

		inline bool operator == (const std::string &s, const inet_address &addr1) {
			inet_address addr2(s);
			return (addr1 == addr2);
		}

		inline bool operator != (const in_addr &addr1, const inet_address &addr2) {
			return (addr1.s_addr == addr2.address.s_addr);
		}

		inline bool operator != (const uint32_t &w, const inet_address &addr) {
			return (addr.address.s_addr == htonl(w));
		}

		inline bool operator != (const std::string &s, const inet_address &addr1) {
			inet_address addr2(s);
			return (addr1 == addr2);
		}


		/*
		 *
		 *   inet_address::operator |  [bitwise OR]
		 *
		 */

		inline const inet_address inet_address::operator | (const inet_address &a) const {
			inet_address r;
			r.address.s_addr = (address.s_addr | a.address.s_addr);
			return r;
		}

		inline const inet_address inet_address::operator | (const in_addr &a) const {
			inet_address r;
			r.address.s_addr = (address.s_addr | a.s_addr);
			return r;
		}

		inline const inet_address inet_address::operator | (const uint32_t &w) const {
			inet_address r;
			r.address.s_addr = (address.s_addr | htonl(w));
			return r;
		}

		inline const inet_address inet_address::operator | (const std::string &s) const {
			inet_address a(s);
			a.address.s_addr = address.s_addr | a.address.s_addr;
			return a;
		}


		/*
		 *
		 *   inet_address::operator & [bitwise AND]
		 *
		 */

		inline const inet_address inet_address::operator & (const inet_address &a) const {
			inet_address r;
			r.address.s_addr = (address.s_addr & a.address.s_addr);
			return r;
		}

		inline const inet_address inet_address::operator & (const in_addr &a) const {
			inet_address r;
			r.address.s_addr = (address.s_addr & a.s_addr);
			return r;
		}

		inline const inet_address inet_address::operator & (const uint32_t &w) const {
			inet_address r;
			r.address.s_addr = (address.s_addr & htonl(w));
			return r;
		}

		inline const inet_address inet_address::operator & (const std::string &s) const {
			inet_address a(s);
			a.address.s_addr = (address.s_addr & a.address.s_addr);
			return a;
		}


		/*
		 *
		 *   operator &/| (..., const inet_address &)
		 *
		 */

		inline const inet_address operator | (const in_addr &addr1, const inet_address &addr2) {
			return inet_address (addr1.s_addr | addr2.address.s_addr);
		}

		inline const inet_address operator | (const uint32_t &w, const inet_address &addr) {
			return inet_address (w & addr.address.s_addr);
		}

		inline const inet_address operator | (const std::string &s, const inet_address &addr2) {
			inet_address addr1 = s;
			return (addr1 & addr2);
		}

		inline const inet_address operator & (const in_addr &addr1, const inet_address &addr2) {
			return inet_address (addr1.s_addr & addr2.address.s_addr);
		}

		inline const inet_address operator & (const uint32_t &w, const inet_address &addr) {
			return inet_address (w & addr.address.s_addr);
		}

		inline const inet_address operator & (const std::string &s, const inet_address &addr2) {
			inet_address addr1 = s;
			return (addr1 & addr2);
		}


		/*
		 *
		 *    inet_address operator >>  [bitshift right]
		 *    inet_address operator >>= [bitshift right reassign]
		 *
		 */

		inline const inet_address inet_address::operator >> (const unsigned int i) const {
			inet_address r;
			r.address.s_addr >>= i;
			return r;
		}

		inline const inet_address inet_address::operator >>= (const unsigned int i) {
			address.s_addr >>= i;
			return *this;
		}


		/*
		 *
		 *    inet_address operator <<  [bitshift left]
		 *    inet_address operator <<= [bitshift left reassign]
		 *
		 */

		inline const inet_address inet_address::operator << (const unsigned int i) const {
			inet_address r;
			r.address.s_addr <<= i;
			return r;
		}

		inline const inet_address inet_address::operator <<= (const unsigned int i) {
			address.s_addr <<= i;
			return *this;
		}


		/*
		 *
		 *   inet_address::operator |= [bitwise OR reassign]
		 *
		 */

		inline const inet_address &inet_address::operator |= (const inet_address &a) {
			address.s_addr |= a.address.s_addr;
			return (*this);
		}

		inline const inet_address &inet_address::operator |= (const in_addr &a) {
			address.s_addr |= a.s_addr;
			return (*this);
		}

		inline const inet_address &inet_address::operator |= (const uint32_t &w) {
			address.s_addr |= htonl(w);
			return (*this);
		}

		inline const inet_address &inet_address::operator |= (const std::string &s) {
			inet_address a(s);
			address.s_addr |= a.address.s_addr;
			return (*this);
		}


		/*
		 *
		 *   inet_address::operator &= [bitwise AND reassign]
		 *
		 */

		inline const inet_address &inet_address::operator &= (const inet_address &a) {
			address.s_addr &= a.address.s_addr;
			return (*this);
		}

		inline const inet_address &inet_address::operator &= (const in_addr &a) {
			address.s_addr &= a.s_addr;
			return (*this);
		}

		inline const inet_address &inet_address::operator &= (const uint32_t &w) {
			address.s_addr &= htonl(w);
			return (*this);
		}

		inline const inet_address &inet_address::operator &= (const std::string &s) {
			inet_address a(s);
			address.s_addr &= a.address.s_addr;
			return (*this);
		}
	}

#endif  /* inaddr.h */
