/*
 *
 *
 *    udpsocket.cc
 *
 *    C++ source code for class udpsocket member functions and/or static member
 *    data.
 *
 *    =========================================================================
 *
 *    Copyright (C) 1999-2003 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
 *
 *    =========================================================================
 *
 *
 */


#include <my++/udpsocket.h>

void my::udpsocket::check_sd (void) {
	// if the socket is not allocated
	if (sd < 0) {
		// request a new socket from the kernel
		sd = ::socket (PF_INET, SOCK_DGRAM, 0);
		if (sd < 0)
			throw (udpsocket::exception(0, errno));
	}
}

my::udpsocket::udpsocket (void) : sd(-1) {
	// just initialize the socket descriptor
	check_sd();
}

my::udpsocket::udpsocket (const inet_address &a, int p) : sd(-1) {
	// there has to be a better way to do this without manually...
	// ...building a sockaddr_in
	check_sd();
	sockaddr_in a2;
	udpsocket::address(a, p).to_sockaddr(&a2);

	// try to bind; if that fails, throw an exception
	if (::bind(sd, (sockaddr*)(&a2), sizeof(sockaddr_in)) < 0)
		throw udpsocket::exception(0, errno);
}

my::udpsocket::udpsocket (const udpsocket::address &a) : sd(-1) {
	check_sd();
	sockaddr_in bind_addr;
	a.to_sockaddr(&bind_addr);

	// try to bind; if that fails, throw an exception
	if (::bind(sd, (sockaddr*)(&bind_addr), sizeof(sockaddr_in)) < 0)
		throw udpsocket::exception(0, errno);
}

my::udpsocket::udpsocket (const std::string &s, int p) : sd(-1) {
	check_sd();
	sockaddr_in bind_addr;
	udpsocket::address(s,p).to_sockaddr(&bind_addr);

	if (::bind(sd, (sockaddr*)(&bind_addr), sizeof(sockaddr_in)) < 0)
		throw udpsocket::exception(0, errno);
}

/*
   my::udpsocket::udpsocket (const udpsocket &s) : sd(-1) {
// check for previous socket allocation
if (sd != (-1))
::close(sd);

// time to pass the baton; copy the descriptor...
sd = s.sd;

// then invalidate the source object's descriptor
s.sd = (-1);
}
 */

void my::udpsocket::bind (const my::socket::address &a) {
	check_sd();
	const udpsocket::address &addr = dynamic_cast<const udpsocket::address&>(a);

	// build the sockaddr_in structure to pass to bind
	sockaddr_in bind_addr;
	addr.to_sockaddr(&bind_addr);

	// try to bind the address; if that fails, throw an exception
	if (::bind(sd, (sockaddr*)(&bind_addr), sizeof(sockaddr_in)) < 0)
		throw udpsocket::exception(this, errno);
}

void my::udpsocket::bind (const std::string &a) {
	// implement me -- FIXME
	// make 'a' into a URL, and bind
}

void my::udpsocket::ishutdown (void) {
	if (::shutdown (sd, SHUT_RD) < 0)
		throw udpsocket::exception(this, errno);
}

void my::udpsocket::oshutdown (void) {
	if (::shutdown (sd, SHUT_WR) < 0)
		throw udpsocket::exception(this, errno);
}

void my::udpsocket::ioshutdown (void) {
	if (::shutdown (sd, SHUT_RDWR) < 0)
		throw udpsocket::exception(this, errno);
}

size_t my::udpsocket::get_from (my::socket::address &a, unsigned char *b, size_t len) {
	check_sd();
	my::udpsocket::address &mta = dynamic_cast<my::udpsocket::address&>(a);
	sockaddr_in addr;
	socklen_t rtn_len;
	rtn_len = sizeof(addr);
	int count = ::recvfrom(sd, (void*)(b), len,
		#ifdef LINUX
		MSG_NOSIGNAL,
		#else
		0,
		#endif /* LINUX */
		(sockaddr*)(&addr), &rtn_len);
	mta = addr;

	if (count < 0)
		throw udpsocket::exception (this, errno);
	return count;
}

size_t my::udpsocket::get_from (my::socket::address &a, char *b, size_t len) {
	return (get_from (a, (unsigned char*)(b), len));
}

size_t my::udpsocket::get_from (my::socket::address &a, void *b, size_t len) {
	return (get_from (a, (unsigned char*)(b), len));
}

size_t my::udpsocket::put_to (const my::socket::address &a, const unsigned char *b, size_t len) {
	check_sd();
	const my::udpsocket::address &mta = dynamic_cast<const my::udpsocket::address&>(a);
	sockaddr_in addr;
	mta.to_sockaddr(&addr);
	int count = ::sendto(sd, (const void*)(b), len,
		#ifdef LINUX
		MSG_NOSIGNAL,
		#else
		0,
		#endif /* LINUX */
		(sockaddr*)&addr, sizeof(addr));
	if (count < 0)
		throw udpsocket::exception (this, errno);
	return count;
}

size_t my::udpsocket::put_to (const my::socket::address &a, const char *b, size_t len) {
	return (put_to (a, (unsigned char*)(b), len));
}

size_t my::udpsocket::put_to (const my::socket::address &a, const void *b, size_t len) {
	return (put_to (a, (unsigned char*)(b), len));
}

void my::udpsocket::name (socket::address &addr) {
	if (sd >= 0) {
		sockaddr_in bound_addr;
		socklen_t addr_len = sizeof(bound_addr);
		int rtn = ::getsockname(sd, (sockaddr*)(&bound_addr), &addr_len);
		if (rtn < 0)
			throw udpsocket::exception(this, errno);
		udpsocket::address &a2 = dynamic_cast<udpsocket::address&>(addr);
		a2 = bound_addr;
	}
}

/*
my::udpsocket &my::udpsocket::operator = (const int new_sd) {
	assign(new_sd);
	return (*this);
}

void my::udpsocket::assign (int new_sd) {
	if (sd >= 0)
		::close (sd);

	// duplicate the open descriptor for our new object
	sd = dup(new_sd);

	if (sd < 0) {
		sd = -1;
		throw udpsocket::exception(this, errno);
	}
}
*/
