/**
 * 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.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.spi.SelectorProvider;


/**
 * Implementation class of DatagramChannel.
 * to avoid the follwing bug
 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6230761
 * @author t-tsue
 */
public class DatagramChannelImplIPv6 extends DatagramChannel {
	/**
	 * SelectorProvider instance given by constructor.
	 */
	private SelectorProvider selectorProvider;
	
	
	/**
	 * FileDescriptor of socket.
	 */
	private FileDescriptor fd ;
	
	/**
	 * Socket instance.
	 */
	private DatagramSocket socket;
	
	/**
	 * SocketAddress instance.
	 */
	private SocketAddress address;
	/**
	 * indicates blocking mode.
	 */
	private boolean isBlocking = true;
	/**
	 * Constructor.
	 * @param selector SelectorProvider
	 */
	public DatagramChannelImplIPv6(SelectorProvider selectorProvider) {
		super(selectorProvider);
		this.selectorProvider = selectorProvider;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#connect(java.net.SocketAddress)
	 */
	@Override
	public DatagramChannel connect(SocketAddress address) throws IOException {
		socket.connect(address);
		this.address = address;
		return this;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#disconnect()
	 */
	@Override
	public DatagramChannel disconnect() throws IOException {
		socket.disconnect();
		return this;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#isConnected()
	 */
	@Override
	public boolean isConnected() {
		return socket.isConnected();
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#read(java.nio.ByteBuffer)
	 */
	@Override
	public int read(ByteBuffer buffer) throws IOException {
		int limit = buffer.limit();
		int position = buffer.position();
		byte[] buf = new byte[limit - position];
		DatagramPacket packet = new DatagramPacket(buf, buf.length);
		socket.receive(packet);
		int length = packet.getLength()-packet.getOffset();
		byte[] data = packet.getData();
		if (length>0) {
			//buffer.put(data,buffer.position(),length);
			buffer.put(data);
		}
		address = packet.getSocketAddress();
		return packet.getLength();
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#read(java.nio.ByteBuffer[], int, int)
	 */
	@Override
	public long read(ByteBuffer[] arg0, int arg1, int arg2) throws IOException {
		throw new IOException("Sorry, Not implemented by Tsue");
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#receive(java.nio.ByteBuffer)
	 */
	@Override
	public SocketAddress receive(ByteBuffer buf) throws IOException {
		synchronized(this) {
			read(buf);
		}
		return address;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#send(java.nio.ByteBuffer, java.net.SocketAddress)
	 */
	@Override
	public int send(ByteBuffer buf, SocketAddress addr) throws IOException {
		byte[] data = buf.array();
		int result = -1;
		try {
			if (!socket().isBound()) {
				Inet6Address i6addr = (Inet6Address)Inet6Address.getByName(System.getProperty("i6addr"));
				InetSocketAddress localaddr = new InetSocketAddress(i6addr,9035);
				socket().bind(localaddr);
			}
			result = WinsockIPv6.sendto(fd, (InetSocketAddress)addr, data);
		} catch (Exception e) {
			IPv6Logger.IPV6.severe("got an unexpected exception:"+e);
			throw new RuntimeException(e.getMessage(),e);
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#socket()
	 */
	@Override
	public DatagramSocket socket() {
		if (socket == null) {
			try {
				DatagramSocketImplIPv6 impl = new DatagramSocketImplIPv6();
				socket = new DatagramSocketIPv6(impl);
				fd = impl.getFD();
			} catch (Exception e) {
				IPv6Logger.IPV6.severe("got an unexpected exception"+e);
				throw new RuntimeException(e.getMessage(),e);
			}
		}
		return socket;
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer)
	 */
	@Override
	public int write(ByteBuffer buf) throws IOException {
		return send(buf,address);
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer[], int, int)
	 */
	@Override
	public long write(ByteBuffer[] arg0, int arg1, int arg2) throws IOException {
		throw new IOException("Not implement by Tsue");
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.spi.AbstractSelectableChannel#implCloseSelectableChannel()
	 */
	@Override
	protected void implCloseSelectableChannel() throws IOException {
		
		WinsockIPv6.close(fd);
	}

	/* (non-Javadoc)
	 * @see java.nio.channels.spi.AbstractSelectableChannel#implConfigureBlocking(boolean)
	 */
	@Override
	protected void implConfigureBlocking(boolean block) throws IOException {
		isBlocking = block;
		WinsockIPv6.blocking(fd, block ? 0 : -1);
	}

	public FileDescriptor getFd() {
		return fd;
	}

	public void setFd(FileDescriptor fd) {
		this.fd = fd;
	}
	

}
