/**
 * Created on 30 Oct. 2009
 * Created by Takaki Tsue, Masato Kawahashi
 * Copyright (C) 2009 HIMACS,Ltd. 
 * Licensed under the Apache License, Version 2.0
 *
 */
/**
 * To avoid the follwing bug
 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6230761
 */
package jp.himacs.avoidbugs.ipv6.bug6230761;

import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.DatagramPacket;
import java.net.DatagramSocketImpl;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketOptions;

/**
 * To avoid the follwing bug
 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6230761
 * @author t-tsue
 */
public class DatagramSocketImplIPv6 extends DatagramSocketImpl {

	/**
	 * default constructor. 
	 */
	public DatagramSocketImplIPv6() throws SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
		super();
		try {
			create();
			localPort = -1;
		} catch(Exception e) {
			IPv6Logger.IPV6.severe("got an unexpected exception"+e);
		}
	}
	
	public FileDescriptor getFD() {
		return fd;
	}
	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#bind(int, java.net.InetAddress)
	 */
	@Override
	protected void bind(int port, InetAddress addr) throws SocketException {
		InetSocketAddress sockaddr = new InetSocketAddress(addr,port);
		try {
			WinsockIPv6.bind(fd, sockaddr);
		} catch (Exception e) {
			IPv6Logger.IPV6.severe("got an unexpected exception"+e);
		}
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#close()
	 */
	@Override
	protected void close() {
		WinsockIPv6.close(fd);
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#create()
	 */
	@Override
	protected void create() throws SocketException {
		if (fd != null) return;
		try {
			fd = WinsockIPv6.newFD(WinsockIPv6.SOCK_DGRAM, WinsockIPv6.IPPROTO_UDP);
		} catch (Exception e) {
			IPv6Logger.IPV6.severe("got an unexpected exception"+e);
		}
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#getTTL()
	 */
	@Override
	protected byte getTTL() throws IOException {
		// Not yet implemented.
		return 0;
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#getTimeToLive()
	 */
	@Override
	protected int getTimeToLive() throws IOException {
		// Not yet implemented.
		return 0;
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#join(java.net.InetAddress)
	 */
	@Override
	protected void join(InetAddress addr) throws IOException {
		// Not yet implemented.

	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#joinGroup(java.net.SocketAddress, java.net.NetworkInterface)
	 */
	@Override
	protected void joinGroup(SocketAddress addr, NetworkInterface inf)
			throws IOException {
		// Not yet implemented.

	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#leave(java.net.InetAddress)
	 */
	@Override
	protected void leave(InetAddress addr) throws IOException {
		// Not yet implemented.

	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#leaveGroup(java.net.SocketAddress, java.net.NetworkInterface)
	 */
	@Override
	protected void leaveGroup(SocketAddress addr, NetworkInterface inf)
			throws IOException {
		// Not yet implemented.

	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#peek(java.net.InetAddress)
	 */
	@Override
	protected int peek(InetAddress addr) throws IOException {
		// Not yet implemented.
		return 0;
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#peekData(java.net.DatagramPacket)
	 */
	@Override
	protected int peekData(DatagramPacket packet) throws IOException {
		// Not yet implemented.
		return 0;
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#receive(java.net.DatagramPacket)
	 */
	@Override
	protected void receive(DatagramPacket packet) throws IOException {
		// Not yet implemented.
		byte[] data = new byte[packet.getLength()];
		try {
			DatagramPacket result = WinsockIPv6.recvfrom(fd, data);
			packet.setData(result.getData());
			packet.setSocketAddress(result.getSocketAddress());
		} catch (Exception e) {
			IPv6Logger.IPV6.severe("got an unexpected exception"+e);
			throw new IOException(e.getMessage());
		}
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#send(java.net.DatagramPacket)
	 */
	@Override
	protected void send(DatagramPacket packet) throws IOException {
		// Not yet implemented.
		byte[] data = new byte[packet.getLength()];
		InetSocketAddress address = (InetSocketAddress)packet.getSocketAddress();
		int result = -1;
		try {
			result = WinsockIPv6.sendto(fd, address, data);
		} catch (Exception e) {
			e.printStackTrace();
			throw new IOException(e.getMessage());
		}
		if (result<0) {
			throw new IOException("datagram packet send failed");
		}
	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#setTTL(byte)
	 */
	@Override
	protected void setTTL(byte ttl) throws IOException {
		// Not yet implemented.

	}

	/* (non-Javadoc)
	 * @see java.net.DatagramSocketImpl#setTimeToLive(int)
	 */
	@Override
	protected void setTimeToLive(int ttl) throws IOException {
		// Not yet implemented.

	}

	/* (non-Javadoc)
	 * @see java.net.SocketOptions#getOption(int)
	 */
	public Object getOption(int opt) throws SocketException {
		Object result = null;
		int iResult = -1;
		switch(opt) {
		case SocketOptions.SO_REUSEADDR:
			iResult = WinsockIPv6.getsockopt(fd, 0xffff, 0x4);
			if (iResult!=0) {
				result = new Boolean(true);
			} else {
				result = new Boolean(false);
			}
			break;
		case SocketOptions.SO_SNDBUF:
			iResult = WinsockIPv6.getsockopt(fd, 0xffff, 0x1001);
			result = new Integer(iResult);
			break;
		case SocketOptions.SO_RCVBUF:
			iResult = WinsockIPv6.getsockopt(fd, 0xffff, 0x1002);
			result = new Integer(iResult);
			break;
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see java.net.SocketOptions#setOption(int, java.lang.Object)
	 */
	public void setOption(int opt, Object val) throws SocketException {
		int iVal = 0;
		switch(opt) {
		case SocketOptions.SO_REUSEADDR:
			iVal = ((Boolean)val).booleanValue() ? -1:0;
			WinsockIPv6.setsockopt(fd, 0xffff, 0x4, iVal);
			break;
		case SocketOptions.SO_SNDBUF:
			iVal = ((Integer)val).intValue();
			WinsockIPv6.setsockopt(fd, 0xffff, 0x1001, iVal);
			break;
		case SocketOptions.SO_RCVBUF:
			iVal = ((Integer)val).intValue();
			WinsockIPv6.setsockopt(fd, 0xffff, 0x1002, iVal);
			break;
		}
	}

}
