/*
    libmaus2
    Copyright (C) 2019 German Tischler

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#if ! defined(LIBMAUS2_POSIX_POSIXFUNCTIONS_HPP)
#define LIBMAUS2_POSIX_POSIXFUNCTIONS_HPP

#include <libmaus2/types/types.hpp>
#include <string>
#include <cassert>
#include <sstream>

namespace libmaus2
{
	namespace posix
	{
		struct PosixFunctions
		{
			enum trans_type { trans_type_openmode_t, trans_type_pid_t, trans_type_off_t };

			static void translate(unsigned char * out, unsigned int outsize, unsigned char const * in, unsigned int const insize);
			static void translateSigned(unsigned char * out, unsigned int outsize, unsigned char const * in, unsigned int const insize);

			template<typename number_type>
			static number_type decode(unsigned char const * p, unsigned int const size)
			{
				number_type n = 0;

				unsigned int shift = size*8;

				while ( shift )
				{
					shift -= 8;

					n <<= 8;
					n |= *(p++);
				}

				return n;
			}

			template<typename number_type>
			static number_type decodeSigned(unsigned char const * p, unsigned int const size)
			{
				if ( std::is_signed<number_type>::value )
				{
					number_type n = 0;

					if ( size )
					{
						if ( (p[0] & 0x80) != 0 )
						{
							unsigned int shift = size*8;

							{
								shift -= 8;
								n <<= 8;
								n |= *(p++) & (~(0x80));
							}

							while ( shift )
							{
								shift -= 8;

								n <<= 8;
								n |= *(p++);
							}

							n = -n;
							n -= 1;
						}
						else
						{
							n = decode<number_type>(p,size);
						}
					}

					return n;
				}
				else
				{
					return decode<number_type>(p,size);
				}
			}

			template<typename number_type>
			static std::string decodeToString(unsigned char const * p, unsigned int const size)
			{
				number_type n = 0;

				unsigned int shift = size*8;

				while ( shift )
				{
					shift -= 8;

					n <<= 8;
					n |= *(p++);
				}

				std::ostringstream ostr;
				ostr << n;

				return ostr.str();
			}

			template<typename number_type>
			static void encode(number_type const num, unsigned char * p, unsigned int const size)
			{
				unsigned int shift = size*8;

				while ( shift )
				{
					shift -= 8;

					*(p++) = (num >> shift) & 0xFF;
				}

				assert ( decode<number_type>(p-size,size) == num );
			}

			template<typename number_type>
			static void encodeSigned(number_type const num, unsigned char * p, unsigned int const size)
			{
				if ( std::is_signed<number_type>::value )
				{
					if ( size )
					{
						if ( num < 0 )
						{
							number_type const inum = -(num+1);
							encode<number_type>(inum,p,size);
							assert ( (p[0] & 0x80) == 0 );
							p[0] |= 0x80;
						}
						else
						{
							encode<number_type>(num,p,size);
						}
					}

					bool const ok = (decodeSigned<number_type>(p,size) == num);
					assert ( ok );
				}
				else
				{
					encode<number_type>(num,p,size);
				}
			}

			static int open(const char *pathname, int flags);
			static int open(const char *pathname, int flags, unsigned char const * inmode);
			static unsigned int getOpenModeSize();

			static int close(int fd);
			static int pipe(int pipefd[2]);

			static unsigned int getPidTSize();
			static bool pidIsMinusOne(unsigned char const * pid);
			static bool pidIsZero(unsigned char const * pid);
			static void getpid(unsigned char * pid);
			static std::string getPidAsString();
			static std::string pidToString(unsigned char const * upid);

			static void fork(unsigned char * upid);

			static void waitpid(unsigned char * opid, unsigned char const * pid, int * wstatus, int options);

			static int get_O_RDONLY();
			static int get_O_WRONLY();
			static int get_O_RDWR();
			static int get_O_CREAT();
			static int get_O_TRUNC();
			static int get_O_LARGEFILE();
			static int get_O_BINARY();

			static unsigned int getOffTSize();
			static bool offTIsMinusOne(unsigned char const * offt);

			static void posix_lseek(unsigned char * off_out, int const fd, unsigned char const * off_in, int whence);
			static int get_SEEK_SET();
			static int get_SEEK_CUR();
			static int get_SEEK_END();

			static ::libmaus2::ssize_t read(int fd, void * buf, ::std::size_t count);
			static ::libmaus2::ssize_t write(int fd, void const * buf, ::std::size_t count);
			static int64_t getFileSizeViaFstat(int const fd);

			static int fsync(int fd);

			static unsigned int getFStatModeSize();

			static int fstatSizeAndMode(int const fd, libmaus2::off_t & size, unsigned char * mode);
			static bool modeIsReg(unsigned char const * mode);

			static int64_t getFIOsizeViaFStatFS(int const fd);

			static int getpagesize();
			static long sysconf(int);

			static int posix_WIFEXITED(int status);
			static int posix_WEXITSTATUS(int status);
		};
	}
}
#endif
