/**
 * @file
 */
#ifndef UI_UTIL_SYS_HPP
#define UI_UTIL_SYS_HPP

// STDC++
#include <cstdio>
#include <ctime>

// POSIX C
#include <sys/types.h>
#include <sys/stat.h>

#ifdef __linux__
	#include <sys/statvfs.h>
	#include <sys/capability.h>
	#include <sys/prctl.h>
#endif

#ifdef WIN32
#include <atlstr.h>
#include "win32/UID.h"
// Get PATH_MAX per magic for WIN32
#if !defined( PATH_MAX )
	#include <Windows.h>
	#define PATH_MAX MAX_PATH
#endif
#if !defined( ssize_t )
	typedef int ssize_t;
#endif
#if !defined( mode_t )
	typedef unsigned int	mode_t;
#endif
#if !defined( socklen_t )
	typedef int socklen_t;
#endif
#if !defined( pid_t )
	typedef int pid_t;
#endif
#if !defined( caddr_t )
	typedef char* caddr_t;
#endif
#include <shlwapi.h>
#else
#include <unistd.h>
#include <sys/file.h>  // BSD type locking
#endif

#ifndef WIN32
#include <pwd.h>
#include <grp.h>
#else
// dummy passwd struct *UNTESTED COMPAT FOR WIN32*
struct passwd {};
struct group {};
#endif

#ifndef WIN32
// System C: Resources
#include <sys/resource.h>

// System C: gettimeofday et.al
#include <sys/time.h>
#endif

// System C: Sockets
#ifdef WIN32
#define SD_RECEIVE      0x00
#define SD_SEND         0x01
#define SD_BOTH         0x02
#else
#include <sys/un.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/socket.h> // BSD sockets
#include <arpa/inet.h>
#endif

// System libraries
#include <iconv.h>

// C++ libraries
#include <ui-utilcpp/Exception.hpp>

namespace UI {
namespace Util {

/** @brief Namespace for system/library calls.
 *
 * @section Sys_Purpose Purpose
 *
 *  - Error handling via exceptions (so no if-err-then-throw-style code needs to be repeated).
 *  - Automatically produce readable error strings from "errno" value in exception strings.
 *  - Transport a copy of the "errno" value up in exception object for detailed error handling if needed.
 *  - Configuration for different systems (linux,win32; ideally, there should be no ifdefs needed but in Sys.?pp).
 *
 * If you use a call that matches any of the points above, please include a wrapper here.
 *
 * @section Sys_Example Example usage
 *
 * Call any of the wrapper somewhere in your code:
@verbatim
...
myStr = getPath();
UI::Util::Sys::remove(myStr);
// as usual, assume success
...
@endverbatim
 *
 * Somewhere in your context-specific exception handler:
@verbatim
...
catch (UI::Util::Sys::Exception const & e)
{
  std::cerr << "System error        : " << e.what() << std::endl;
  std::cerr << "Errno error code was: " << e.getCode() << std::endl;
  std::cerr << "Exception debug     : " << e.getDebug() << std::endl;
}
...
@endverbatim
 *
 * @section Sys_Dev_Howto How to add wrappers
 *
 *  - Use syntax & name from the man page.
 *  - If return value is only used for error condition:yes/no: make it return void.
 *  - Just call the system/library function. On error condition (see man
 *  page), use UI_THROW_ERRNO("MY NICE PREFIX") to throw w/
 *  errno + errno text support in the exception.
 */
namespace Sys {

/** @brief Use this exception class if you want to catch failures on system/library calls. */
class Exception: public UI::Util::Exception
{
public:
	Exception(std::string const & what=NoWhatGiven_, std::string const & debug=NoDebugGiven_, int const & errNo=0)
		:UI::Util::Exception(what, debug, errNo) {}
	virtual ~Exception() throw() {};
};

#ifdef WIN32
void wsaStartup();
void wsaCleanup();
std::string convertPath(std::string const & path);
#endif

/** @name Drop-in replacements for system(2) and library(3) calls w/ exception handling on errors.
 * @{ */
void * calloc(size_t nmemb, size_t size);
void * malloc(size_t size);
void free(void * ptr);

int system(char const * s);
char * getenv(char const * name);

void realpath(char const * path, char * resolved_path);

FILE * fopen(char const * path, char const * mode);
FILE * fdopen(int fildes, char const * mode);
FILE * freopen(char const * path, char const * mode, FILE * stream);
void fclose(FILE * fp);

mode_t umask(mode_t mask);
void chdir(char const * path);
void chown(char const * path, uid_t owner, gid_t group=-1);
void chmod(char const * path, mode_t mode);

void stat(char const * file_name, struct stat * buf);
void fstat(int filedes, struct stat *buf);
void lstat(char const * file_name, struct stat * buf);
void statvfs(char const * path, struct statvfs * buf);

void unlink(char const * pathname);
void remove(char const * pathname);
void rename(char const * oldpath, char const * newpath);

int open(char const * pathname, int flags);
int open(char const * pathname, int flags, mode_t mode);
void close(int fd);

void mkdir(char const * pathname, mode_t mode);
void rmdir(char const * pathname);

ssize_t write(int fd, void const * buf, size_t count);
ssize_t read(int fd, void * buf, size_t count);

int dup(int oldfd);
int dup2(int oldfd, int newfd);

int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock * lock);
int flock(int fd, int operation);

struct passwd * getpwnam(char const * name);
struct group * getgrnam(char const * name);

#ifndef WIN32
pid_t fork(void);
#endif
pid_t getpid();
pid_t gettid();
pid_t getppid();
pid_t getpgid(pid_t pid);
#ifndef WIN32
pid_t waitpid(pid_t pid, int * status, int options);
#endif
pid_t setsid();
uid_t getuid();
gid_t getgid();
uid_t geteuid();
gid_t getegid();
void seteuid(uid_t euid);
void setegid(gid_t egid);
void setuid(uid_t uid);
void setgid(gid_t gid);

uid_t getfsuid(pid_t const pid=gettid());
gid_t getfsgid(pid_t const pid=gettid());
void setfsuid(uid_t fsuid);
void setfsgid(gid_t fsgid);

void getrlimit(int resource, struct rlimit * rlim);
void getrusage(int who, struct rusage * usage);
void setrlimit(int resource, struct rlimit const * rlim);

unsigned int sleep(unsigned int seconds);

int socket(int domain, int type, int protocol);
void getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
void getnameinfo(const struct sockaddr * sa, socklen_t salen, char * host, size_t hostlen, char * serv, size_t servlen, int flags);
std::string getnameinfo(const struct sockaddr * sa, socklen_t salen);
void getpeername(int s, struct sockaddr * name, socklen_t * namelen);
void getsockname(int s, struct sockaddr * name, socklen_t * namelen);
void setsockopt(int s, int level, int optname, void const * optval, socklen_t optlen);
void setsockopt_to(int s, int level, int optname, struct timeval const & tv);

ssize_t recv(int s, void * buf, size_t len, int flags);
ssize_t send(int s, void const * buf, size_t len, int flags);
void listen(int s, int backlog);
void bind(int sockfd, struct sockaddr * my_addr, socklen_t addrlen);
void connect(int sockfd, const struct sockaddr * serv_addr, socklen_t addrlen);
int select(int n, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout);
void socketpair(int d, int type, int protocol, int sv[2]);

void gettimeofday(struct timeval * tv, struct timezone * tz);
void settimeofday(struct timeval const * tv , struct timezone const * tz);

iconv_t iconv_open(char const * tocode, char const * fromcode);
void iconv_close(iconv_t cd);

void quotactl(int cmd, char const * special, int id, caddr_t addr);

size_t confstr(int name, char * buf, size_t len);

/** @brief Loosely like the shell utility "getconf". */
std::string getconf(int id);

// POSIX Capapilities
#ifdef __linux__
cap_t cap_init(void);
void cap_free(void * obj);
cap_t cap_dup(cap_t c);

cap_t cap_get_proc(void);
void cap_set_proc(cap_t c);
void cap_clear(cap_t c);
#ifdef HAVE_CAP_CLEAR_FLAG
void cap_clear_flag(cap_t c, cap_flag_t f);
#endif
void cap_get_flag(cap_t c, cap_value_t v, cap_flag_t f, cap_flag_value_t *vp);
void cap_set_flag(cap_t c, cap_flag_t f, int n, const cap_value_t * va, cap_flag_value_t v);
#ifdef HAVE_CAP_COMPARE
int cap_compare(cap_t c1, cap_t c2);
#endif

cap_t cap_from_text(char const * s);
char * cap_to_text(cap_t c, ssize_t * l);
/** @} */

/** @brief Linux: prctl. */
int prctl(int option, unsigned long arg2=-1, unsigned long arg3=-1, unsigned long arg4=-1, unsigned long arg5=-1);
#endif

}}}
#endif
