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

import com.sun.jna.LastErrorException;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;

import java.nio.Buffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CLibrary {

	private static final Logger logger = Logger.getLogger(CLibrary.class.getName());

	public static final int AF_UNIX = 1;

	public static final int AF_INET = 2;

	public static final int SOCK_STREAM = 1;

	public static final int SOCK_DGRAM = 2;

	public static final int PROTOCOL = 0;

	public static final CLibrary clib = new CLibrary();

	native public int fcntl(int fd, int cmd, int arg) throws LastErrorException;

	native public int ioctl(int fd, int cmd, byte[] arg) throws LastErrorException;

	native public int ioctl(int fd, int cmd, Pointer p) throws LastErrorException;

	native public int open(String path, int flags) throws LastErrorException;

	native public int close(int fd) throws LastErrorException;

	native public int write(int fd, Buffer buffer, int count) throws LastErrorException;

	native public int read(int fd, Buffer buffer, int count) throws LastErrorException;

	native public int socket(int domain, int type, int protocol) throws LastErrorException;

	native public int connect(int sockfd, SockAddrUn sockaddr, int addrlen) throws LastErrorException;

	native public int bind(int sockfd, SockAddrUn sockaddr, int addrlen) throws LastErrorException;

	native public int accept(int sockfd, SockAddrUn rem_addr, Pointer opt) throws LastErrorException;

	native public int listen(int sockfd, int channel) throws LastErrorException;

	native public int getsockopt(int s, int level, int optname, byte[] optval, IntByReference optlen);

	native public int setsockopt(int s, int level, int optname, byte[] optval, int optlen);

	native public int recv(int s, Buffer buf, int len, int flags) throws LastErrorException;

	native public int recvfrom(int s, Buffer buf, int len, int flags, SockAddrUn from, IntByReference fromlen);

	native public int send(int s, Buffer msg, int len, int flags) throws LastErrorException;

	native public int getpid() throws LastErrorException;

	static {
		try {
			Native.register("c");
		} catch (Exception e) {
			logger.log(Level.WARNING, "Native.register(\"c\") failed", e);
		}
	}

	private CLibrary() {
	}

	public static CLibrary getInstance() {
		return clib;
	}

	public static class SockAddrUn extends Structure implements Structure.ByReference {

		public short family = AF_UNIX;

		public byte[] addr = new byte[108];

		public SockAddrUn(String path) {
			System.arraycopy(path.getBytes(StandardCharsets.US_ASCII), 0, addr, 0, Math.min(108, path.length()));
		}

		@Override
		protected List<String> getFieldOrder() {
			return List.of("family", "addr");
		}
	}
}
