//
// C++ Implementation: ipaddress
//
// Description:
//
//
// Author: Christian Hubinger <chubinger@irrsinnig.org>, (C) 2003
//
// Copyright: See COPYING file that comes with this distribution
//
//
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#include "ipaddress.h"

// QT includes

// KDE includes
#include <kdebug.h>

// Project includes
#include "kmfcheckinput.h"
#include "kmferror.h"
#include "xmlnames.h"
using namespace std;

namespace KMF {

IPAddress::IPAddress( int fi, int se, int th, int fo ) {
	m_checkInput = new KMFCheckInput();
	m_err = new KMFError();
	for ( int i = 0; i < NUMDIGITS; i++ ) {
		m_digits[i] = 0;
	}
	if ( !setAddress( fi, se, th, fo ) )
		kdDebug() << "ERROR: Tried to initialise IPAddress with invalid parameters." << endl;
}


IPAddress::~IPAddress() {
	delete m_checkInput;
	delete m_err;
}

int IPAddress::getDigit( int num ) const {
	if ( num >= 4 || num < 0 )
		return -1;
	return m_digits[ num ];
}

IPAddress&  IPAddress::plus(int num) {
	// 	kdDebug() << "IPAddress&  IPAddress::operator+( int " << num << " )" << endl;
	if ( m_digits[3] + num  < 256 &&  m_digits[3] + num  > -1 ) {
		m_digits[3] = m_digits[3] + num;
	}
	return *this;
}

int IPAddress::operator==( const IPAddress& addr ) {
	// 	kdDebug() << "IPAddress&  IPAddress::operator==( const IPAddress& addr " << addr.toString()<< " )" << endl;
	bool ident = true;
	int first_diff = 0;
	for ( int i = 0; i < NUMDIGITS && ident; i++ ) {
		if ( m_digits[i] != addr.getDigit(i) ) {
			ident = false;
			first_diff = i;
		}
	}
	if ( ident )
		return EQUAL;

	if ( m_digits[ first_diff ] > addr.getDigit( first_diff )  )
		return SMALLER;
	else
		return BIGGER;
}

bool IPAddress::setAddress( int fi, int se, int th, int fo ) {
	if ( fi < 0 || fi > 255 || se < 0 || se > 255 || th < 0 || th > 255 || fo < 0 || fo > 255 )
		return false;

	m_digits[0] = fi;
	m_digits[1] = se;
	m_digits[2] = th;
	m_digits[3] = fo;

	return true;
}

bool IPAddress::setAddress( const TQString& input ) {
	TQString inp = input;
	m_checkInput->checkInput( inp, "IP", m_err );
	if ( m_err->errType() != KMFError::OK ) {
		kdDebug() << "WARNING: Given wron IP address string: " << inp << endl;
		return false;
	}
	int pos;
	TQString str_num;
	bool valid = true;
	int counter = 0;
	while ( !inp.isEmpty() ) {
		pos = inp.find( "." );
		if ( pos > -1 ) {
			str_num = inp.left( pos );
			// 			kdDebug() << "IP Num Part: " << str_num << endl;
			inp = inp.right( inp.length() - ( pos + 1 ) );
			// 			kdDebug() << "Digit: " << str_num << endl;
			int val = str_num.toInt();
			//				kdDebug() << "Val: " << val << endl;
			if ( val < 0 || val > 255 ) {
				valid = false;
			} else {
				// 				kdDebug() << "IPAddress: Setting digit: " << counter  << " to "  << val << endl;
				m_digits[ counter ] = val;
				counter++;
			}
		} else {
			str_num = inp;
			//				kdDebug() << "IP Num Part: " << str_num << endl;
			inp = "";
			//				kdDebug() << "Rest: " << inp << endl;
			int val = str_num.toInt();
			// 			kdDebug() << "Digit: " << val << endl;
			if ( val < 0 || val > 255 ) {
				valid = false;
			} else {
				// 				kdDebug() << "IPAddress: Setting digit: " << counter  << " to "  << val << endl;
				m_digits[ counter ] = val;
				counter++;
			}
		}
	}
	return true;
}

const TQString& IPAddress::toString() const {
	TQString fi = "";
	TQString se = "";
	TQString th = "";
	TQString fo = "";
	return *( new TQString( fi.setNum( m_digits[0] ) + "." + se.setNum( m_digits[1] ) + "." + th.setNum( m_digits[2] ) + "." + fo.setNum( m_digits[3] ) ) );
}


// static stuff
IPAddress& IPAddress::calcNetworkMaskFromLength( int len ) {
	TQValueList<int> list;
	int nextOne = 0;
	int digit1 = IPAddress::calcLenthToMaskDigit( len, &nextOne );
	int digit2 = IPAddress::calcLenthToMaskDigit( nextOne, &nextOne );
	int digit3 = IPAddress::calcLenthToMaskDigit( nextOne, &nextOne );
	int digit4 = IPAddress::calcLenthToMaskDigit( nextOne, &nextOne );
	list.append( digit1 );
	list.append( digit2 );
	list.append( digit3 );
	list.append( digit4 );
	IPAddress *addr = new IPAddress( digit1,digit2,digit3,digit4 );

	return *addr;
}


int IPAddress::calcLenthToMaskDigit( int nMask, int *nextOne ) {
	if (nMask < 1 || nMask > 32 ) {
		return 0;
	}
	int nCalc = 0;
	for (int nX = 7;nX > -1 ; nX--) {
		int total=1;
		for ( int j=0; j < nX; j++) {
			total*=2;
		}

		nCalc=nCalc + total;

		nMask = nMask -1;
		*nextOne=nMask;
		if (nMask <1) {
			return nCalc;
		}
	}
	return nCalc;
}

int IPAddress::calcLenthToMask( IPAddress& addr) {
	// kdDebug() << "calcLenthToMask( " << addr.toString() << " )" << endl;
	if ( ! IPAddress::isValidMask( addr ) ) {
		kdDebug() << "Netmaks is not Valid!!!" << endl;
		return -1;
	}

	int m[4];
	for ( int i = 0; i< 4 ; i++ ) {
		m[i] = addr.getDigit( i );
	}


	int mask = 0;
	for (int loop=0; loop<4; loop++) {
		int div = 256;
		while ( div > 1) {
			div = div/2;
			int test = m[loop] - div;
			if ( test >-1) {
				mask=mask+1;
				m[loop]=test;
			} else {
				break;
			}
		}

	}
	// kdDebug()  << "Returning: " << mask << endl;
	return mask;
}

bool IPAddress::isValidAddress( IPAddress& addr) {
	int IP1 = addr.getDigit( 0 );
	int IP2 = addr.getDigit( 1 );
	int IP3 = addr.getDigit( 2 );
	int IP4 = addr.getDigit( 3 );

	if ((IP1 > 255) || (IP1 < 0)) {
		return false;
	}
	if ((IP2 > 255) || (IP2 < 0)) {
		return false;
	}
	if ((IP3 > 255) || (IP3 < 0)) {
		return false;
	}
	if ((IP4 > 255) || (IP4 < 0)) {
		return false;
	}
	return true;
}

bool IPAddress::isValidMask( IPAddress& addr) {
	// alert(IP1+"."+IP2+"."+IP3+"."+IP4)
	int IP1 = addr.getDigit( 0 );
	int IP2 = addr.getDigit( 1 );
	int IP3 = addr.getDigit( 2 );
	int IP4 = addr.getDigit( 3 );

	if ((IP1 > 255) || (IP1 < 0)) {
		return false;
	}
	if ((IP2 > 255) || (IP2 < 0)) {
		return false;
	}
	if ((IP3 > 255) || (IP3 < 0)) {
		return false;
	}
	if ((IP4 > 255) || (IP4 < 0)) {
		return false;
	}

	int IPX =5;
	// find where it changes
	if (IP1 < 255) {
		if((IP2 > 0) || (IP3 > 0) || (IP4 > 0)) {
			return false;
		}
		IPX = IP1;
	} else {
		if (IP2 < 255) {
			if((IP3 > 0) || (IP4 > 0)) {
				return false;
			}
			IPX = IP2;
		} else {
			if (IP3 < 255) {
				if((IP4 > 0)) {
					return false;
				}
				IPX = IP3;
			} else {
				IPX = IP4;
			}
		}
	}

	// determine if IPX is a good
	switch (IPX) {
	case 255:
	case 128:
	case 192:
	case 224:
	case 240:
	case 248:
	case 252:
	case 254:
	case 0:
		return true;
	default:
		return false;
	}

	return true;
}

bool IPAddress::hostsOnSameNetwork(  IPAddress& host1, IPAddress& host2, int len ) {
//	kdDebug() << "IPAddress::hostsOnSameNetwork( IPAddress&, IPAddress&, int )" << endl;
	IPAddress mask = IPAddress::calcNetworkMaskFromLength( len );
	return ( IPAddress::hostsOnSameNetwork( host1, host2, mask ) );
}

bool IPAddress::hostsOnSameNetwork( IPAddress& host1, IPAddress& host2, IPAddress& mask ) {
	kdDebug() << "IPAddress::hostsOnSameNetwork( IPAddress&, IPAddress&, int )" << endl;
	kdDebug() << "Host 1: " << host1.toString() <<  endl; 
	kdDebug() << "Host 2: " << host2.toString() << endl;
	kdDebug() << "Mask: " << mask.toString() << endl; 
//	IPAddress mask = IPAddress::calcNetworkMaskFromLength( len );

	int nOctA1=host1.getDigit(0) & mask.getDigit(0);
	int nOctA2=host1.getDigit(1) & mask.getDigit(1);
	int nOctA3=host1.getDigit(2) & mask.getDigit(2);
	int nOctA4=host1.getDigit(3) & mask.getDigit(3);


	int nOctB1=host2.getDigit(0) & mask.getDigit(0);
	int nOctB2=host2.getDigit(1) & mask.getDigit(1);
	int nOctB3=host2.getDigit(2) & mask.getDigit(2);
	int nOctB4=host2.getDigit(3) & mask.getDigit(3);

	if ((nOctA1==nOctB1) && (nOctA2==nOctB2) && (nOctA3==nOctB3) && (nOctA4==nOctB4)) {
		kdDebug() << "Hosts on same net." << endl;
		return true;
	} else {
		kdDebug() << "Hosts NOT on same net." << endl;
		return false;
	}
}
}
