package org.xbib.net.socket.v4.unix;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import com.sun.jna.LastErrorException;
import static org.xbib.net.socket.v4.unix.CLibrary.AF_UNIX;
import static org.xbib.net.socket.v4.unix.CLibrary.PROTOCOL;
import static org.xbib.net.socket.v4.unix.CLibrary.SOCK_DGRAM;

public class UnixDatagramSocket extends Socket {

	private final CLibrary library;

	private final int sockfd;

	private final CLibrary.SockAddrUn sockAddrUn;

	private InputStream is;

	private OutputStream os;

	public UnixDatagramSocket(String path) throws SocketException {
		this.library = CLibrary.getInstance();
		this.sockfd = library.socket(AF_UNIX, SOCK_DGRAM, PROTOCOL);
		this.sockAddrUn = new CLibrary.SockAddrUn(path);
		int rc = connect(sockfd, sockAddrUn, sockAddrUn.size());
		if (rc != 0) {
			throw new SocketException("can not connect");
		}
		this.is = new UnixSocketInputStream(this);
		this.os = new UnixSocketOutputStream(this);
	}

	private int connect(int sockfd, CLibrary.SockAddrUn sockaddr, int addrlen) throws SocketException {
		try {
            return library.connect(sockfd, sockaddr, addrlen);
		} catch (LastErrorException lee) {
			throw new SocketException("connect: could not connect to socket", lee);
		}
	}
	
	public int read(byte[] buf, int count) throws IOException {
		try {
			ByteBuffer buffer = ByteBuffer.wrap(buf);
            return library.read(sockfd, buffer, count);
		} catch (LastErrorException lee) {
			throw new IOException("read: could not read from socket", lee);
		}
	}

	public int write(byte[] buf, int count) throws IOException {
		try {
            return library.write(sockfd, ByteBuffer.wrap(buf), count);
		} catch (LastErrorException lee) {
			throw new IOException("write: could not write to socket", lee);
		}
	}

	public int send(byte[] buf, int count) throws IOException {
		try {
			return library.send(sockfd, ByteBuffer.wrap(buf), count, 0);
		} catch (LastErrorException lee) {
			throw new IOException("send: could not send to socket", lee);
		}
	}

	@Override
	public InputStream getInputStream() throws IOException {
		return is;
	}

	@Override
	public OutputStream getOutputStream() throws IOException {
		return os;
	}

	@Override
	public void shutdownInput() throws IOException {
		is = null;
	}

	@Override
	public void shutdownOutput() throws IOException {
		os = null;
	}

	@Override
	public synchronized void close() throws IOException {
		try {
			shutdownInput();
			shutdownOutput();
			library.close(sockfd);
		} catch (LastErrorException lee) {
			throw new IOException("close: could not close socket", lee);
		}
	}
}
