/*
 * Copyright (c) 2017 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef included_vcom_socket_h
#define included_vcom_socket_h

#include <string.h>

#include <vcl/vcom_glibc_socket.h>
#include <vppinfra/types.h>
#include <sys/socket.h>

#define INVALID_SESSION_ID (~0)
#define INVALID_FD (~0)

#define INVALID_VEP_IDX INVALID_SESSION_ID
#define INVALID_EPFD INVALID_FD

typedef enum
{
  SOCKET_TYPE_UNBOUND = 0,
  SOCKET_TYPE_KERNEL_BOUND,
  SOCKET_TYPE_VPPCOM_BOUND
} vcom_socket_type_t;

typedef enum
{
  EPOLL_TYPE_UNBOUND = 0,
  EPOLL_TYPE_KERNEL_BOUND,
  EPOLL_TYPE_VPPCOM_BOUND
} vcom_epoll_type_t;

typedef enum
{
  FD_TYPE_INVALID = 0,
  FD_TYPE_KERNEL,
  FD_TYPE_EPOLL,
  FD_TYPE_VCOM_SOCKET,
  /* add new types here */
  /* FD_TYPE_MAX should be the last entry */
  FD_TYPE_MAX
} vcom_fd_type_t;

typedef struct
{
  /* file descriptor -
   * fd 0, 1, 2 have special meaning and are reserved,
   * -1 denote invalid fd */
  i32 fd;

  /* session id - -1 denote invalid sid */
  i32 sid;

  /* socket type */
  vcom_socket_type_t type;

  /* vcom socket attributes here */

} vcom_socket_t;

typedef struct
{
  /* epoll file descriptor -
   * epfd 0, 1, 2 have special meaning and are reserved,
   * -1 denote invalid epfd */
  i32 epfd;

  /* vep idx - -1 denote invalid vep_idx */
  i32 vep_idx;

  /* epoll type */
  vcom_epoll_type_t type;

  /* flags - 0 or EPOLL_CLOEXEC */
  i32 flags;

  /* vcom epoll attributes here */

  /*
   * 00. count of file descriptors currently registered
   *     on this epoll instance.
   * 01. number of file descriptors in the epoll set.
   * 02. EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL
   *     update the count.
   * 03. cached for frequent access.
   * */
  i32 count;
  i32 vcl_cnt;
  i32 libc_cnt;

  /* close( ) called on this epoll instance */
  /* 0 - close ( ) not called, 1 - close( ) called. */
  u32 close;

} vcom_epoll_t;

typedef struct
{
  /* "container" of this item */
  i32 epfd;

  /* fd - file descriptor information this item refers to */
  i32 fd;
  /* next and prev fd in the "epoll set" of epfd */
  i32 next_fd;
  i32 prev_fd;

  /* vcom fd type */
  vcom_fd_type_t type;

  /* interested events and the source fd */
  struct epoll_event event;

  /* ready events and the source fd */
  struct epoll_event revent;

  /* epitem attributes here */

} vcom_epitem_t;

typedef union vcom_epitem_key
{
  struct
  {
    i32 fd;
    i32 epfd;
  };
  i64 key;
} __EPOLL_PACKED vcom_epitem_key_t;

static inline char *
vcom_socket_type_str (vcom_socket_type_t t)
{
  switch (t)
    {
    case SOCKET_TYPE_UNBOUND:
      return "SOCKET_TYPE_UNBOUND";

    case SOCKET_TYPE_KERNEL_BOUND:
      return "SOCKET_TYPE_KERNEL_BOUND";

    case SOCKET_TYPE_VPPCOM_BOUND:
      return "SOCKET_TYPE_VPPCOM_BOUND";

    default:
      return "SOCKET_TYPE_UNKNOWN";
    }
}

static inline char *
vcom_socket_epoll_type_str (vcom_epoll_type_t t)
{
  switch (t)
    {
    case EPOLL_TYPE_UNBOUND:
      return "EPOLL_TYPE_UNBOUND";

    case EPOLL_TYPE_KERNEL_BOUND:
      return "EPOLL_TYPE_KERNEL_BOUND";

    case EPOLL_TYPE_VPPCOM_BOUND:
      return "EPOLL_TYPE_VPPCOM_BOUND";

    default:
      return "EPOLL_TYPE_UNKNOWN";
    }
}

static inline char *
vcom_socket_vcom_fd_type_str (vcom_fd_type_t t)
{
  switch (t)
    {
    case FD_TYPE_KERNEL:
      return "FD_TYPE_KERNEL";

    case FD_TYPE_EPOLL:
      return "FD_TYPE_EPOLL";

    case FD_TYPE_VCOM_SOCKET:
      return "FD_TYPE_VCOM_SOCKET";

    default:
      return "FD_TYPE_UNKNOWN";
    }
}

static inline int
vcom_socket_type_is_vppcom_bound (vcom_socket_type_t t)
{
  return t == SOCKET_TYPE_VPPCOM_BOUND;
}

static inline int
vcom_socket_epoll_type_is_vppcom_bound (vcom_epoll_type_t t)
{
  return t == EPOLL_TYPE_VPPCOM_BOUND;
}

static inline void
vsocket_init (vcom_socket_t * vsock)
{
  memset (vsock, 0, sizeof (*vsock));

  vsock->fd = INVALID_FD;
  vsock->sid = INVALID_SESSION_ID;
  vsock->type = SOCKET_TYPE_UNBOUND;
  /* vcom socket attributes init here */
}

static inline void
vepoll_init (vcom_epoll_t * vepoll)
{
  memset (vepoll, 0, sizeof (*vepoll));

  vepoll->epfd = INVALID_EPFD;
  vepoll->vep_idx = INVALID_VEP_IDX;
  vepoll->type = EPOLL_TYPE_UNBOUND;
  vepoll->flags = 0;

  vepoll->count = 0;
  vepoll->close = 0;
  /* vcom epoll attributes init here */
}

static inline void
vepitem_init (vcom_epitem_t * vepitem)
{
  struct epoll_event event = {.events = 0,.data.fd = INVALID_FD };

  memset (vepitem, 0, sizeof (*vepitem));

  vepitem->epfd = INVALID_EPFD;

  vepitem->fd = INVALID_FD;
  vepitem->next_fd = INVALID_FD;
  vepitem->prev_fd = INVALID_FD;

  vepitem->type = FD_TYPE_INVALID;

  vepitem->event = event;
  vepitem->revent = event;
  /* vepoll attributes init here */
}

static inline void
vepitemkey_init (vcom_epitem_key_t * epfdfd)
{
  memset (epfdfd, 0, sizeof (*epfdfd));

  epfdfd->epfd = INVALID_EPFD;
  epfdfd->fd = INVALID_FD;
}

static inline void
vsocket_set (vcom_socket_t * vsock, i32 fd, i32 sid, vcom_socket_type_t type)
{
  vsock->fd = fd;
  vsock->sid = sid;
  vsock->type = type;
  /* vcom socket attributes set here */
}

static inline void
vepoll_set (vcom_epoll_t * vepoll,
	    i32 epfd, i32 vep_idx,
	    vcom_epoll_type_t type, i32 flags, i32 count, u32 close)
{
  vepoll->epfd = epfd;
  vepoll->vep_idx = vep_idx;
  vepoll->type = type;
  vepoll->flags = flags;

  vepoll->count = count;
  vepoll->close = close;
  /* vcom epoll attributes set here */
}

static inline void
vepitem_set (vcom_epitem_t * vepitem,
	     i32 epfd,
	     i32 fd, i32 next_fd, i32 prev_fd,
	     vcom_fd_type_t type,
	     struct epoll_event event, struct epoll_event revent)
{
  vepitem->epfd = epfd;

  vepitem->fd = fd;
  vepitem->next_fd = next_fd;
  vepitem->prev_fd = prev_fd;

  vepitem->type = type;

  vepitem->event = event;
  vepitem->revent = revent;
  /* vcom epitem attributes set here */
}

static inline void
vepitemkey_set (vcom_epitem_key_t * epfdfd, i32 epfd, i32 fd)
{
  epfdfd->epfd = epfd;
  epfdfd->fd = fd;
}

static inline int
vsocket_is_vppcom_bound (vcom_socket_t * vsock)
{
  return vcom_socket_type_is_vppcom_bound (vsock->type);
}

static inline int
vepoll_is_vppcom_bound (vcom_epoll_t * vepoll)
{
  return vcom_socket_epoll_type_is_vppcom_bound (vepoll->type);
}

int vcom_socket_main_init (void);

void vcom_socket_main_destroy (void);

void vcom_socket_main_show (void);

int vcom_socket_is_vcom_fd (int fd);

int vcom_socket_is_vcom_epfd (int epfd);

int vcom_socket_close (int __fd);

ssize_t vcom_socket_read (int __fd, void *__buf, size_t __nbytes);

ssize_t vcom_socket_readv (int __fd, const struct iovec *__iov, int __iovcnt);

ssize_t vcom_socket_write (int __fd, const void *__buf, size_t __n);

ssize_t vcom_socket_writev (int __fd, const struct iovec *__iov,
			    int __iovcnt);

int vcom_socket_fcntl_va (int __fd, int __cmd, va_list __ap);

int vcom_socket_ioctl_va (int __fd, unsigned long int __cmd, va_list __ap);

int
vcom_socket_select (int vcom_nfds, fd_set * __restrict vcom_readfds,
		    fd_set * __restrict vcom_writefds,
		    fd_set * __restrict vcom_exceptfds,
		    struct timeval *__restrict timeout);


int vcom_socket_socket (int __domain, int __type, int __protocol);

int
vcom_socket_socketpair (int __domain, int __type, int __protocol,
			int __fds[2]);

int vcom_socket_bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);

int
vcom_socket_getsockname (int __fd, __SOCKADDR_ARG __addr,
			 socklen_t * __restrict __len);

int
vcom_socket_connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);

int
vcom_socket_getpeername (int __fd, __SOCKADDR_ARG __addr,
			 socklen_t * __restrict __len);

ssize_t
vcom_socket_send (int __fd, const void *__buf, size_t __n, int __flags);

ssize_t vcom_socket_recv (int __fd, void *__buf, size_t __n, int __flags);

ssize_t
vcom_socket_sendfile (int __out_fd, int __in_fd, off_t * __offset,
		      size_t __len);

/*
 * RETURN   1 if __fd is (SOCK_STREAM, SOCK_SEQPACKET),
 * 0 otherwise
 * */
int vcom_socket_is_connection_mode_socket (int __fd);

ssize_t
vcom_socket_sendto (int __fd, const void *__buf, size_t __n,
		    int __flags, __CONST_SOCKADDR_ARG __addr,
		    socklen_t __addr_len);

ssize_t
vcom_socket_recvfrom (int __fd, void *__restrict __buf, size_t __n,
		      int __flags, __SOCKADDR_ARG __addr,
		      socklen_t * __restrict __addr_len);

ssize_t
vcom_socket_sendmsg (int __fd, const struct msghdr *__message, int __flags);

#ifdef __USE_GNU
int
vcom_socket_sendmmsg (int __fd, struct mmsghdr *__vmessages,
		      unsigned int __vlen, int __flags);
#endif

ssize_t vcom_socket_recvmsg (int __fd, struct msghdr *__message, int __flags);

#ifdef __USE_GNU
int
vcom_socket_recvmmsg (int __fd, struct mmsghdr *__vmessages,
		      unsigned int __vlen, int __flags,
		      struct timespec *__tmo);
#endif

int
vcom_socket_getsockopt (int __fd, int __level, int __optname,
			void *__restrict __optval,
			socklen_t * __restrict __optlen);

int
vcom_socket_setsockopt (int __fd, int __level, int __optname,
			const void *__optval, socklen_t __optlen);

int vcom_socket_listen (int __fd, int __n);

int
vcom_socket_accept (int __fd, __SOCKADDR_ARG __addr,
		    socklen_t * __restrict __addr_len);

int
vcom_socket_accept4 (int __fd, __SOCKADDR_ARG __addr,
		     socklen_t * __restrict __addr_len, int __flags);

int vcom_socket_shutdown (int __fd, int __how);

int vcom_socket_epoll_create1 (int __flags);

int
vcom_socket_epoll_ctl (int __epfd, int __op, int __fd,
		       struct epoll_event *__event);

int
vcom_socket_epoll_pwait (int __epfd, struct epoll_event *__events,
			 int __maxevents, int __timeout,
			 const __sigset_t * __ss);

/*
 * handle only vcom fds
 */
int vcom_socket_poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);

#ifdef __USE_GNU
int
vcom_socket_ppoll (struct pollfd *__fds, nfds_t __nfds,
		   const struct timespec *__timeout, const __sigset_t * __ss);
#endif

#endif /* included_vcom_socket_h */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */