#include "il2cpp-config.h"

#if IL2CPP_TARGET_POSIX || IL2CPP_SUPPORT_SOCKETS_POSIX_API

#include "Error.h"
#include "utils/PathUtils.h"

#include <cassert>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>

namespace il2cpp
{
namespace os
{
    ErrorCode SocketErrnoToErrorCode(int32_t code)
    {
        ErrorCode result = (ErrorCode) - 1;

        switch (code)
        {
            case 0: result = kErrorCodeSuccess; break;
            case EACCES: result = kWSAeacces; break;
    #ifdef EADDRINUSE
            case EADDRINUSE: result = kWSAeaddrinuse; break;
    #endif
    #ifdef EAFNOSUPPORT
            case EAFNOSUPPORT: result = kWSAeafnosupport; break;
    #endif
    #if EAGAIN != EWOULDBLOCK
            case EAGAIN: result = kWSAewouldblock; break;
    #endif
    #ifdef EALREADY
            case EALREADY: result = kWSAealready; break;
    #endif
            case EBADF: result = kWSAenotsock; break;
    #ifdef ECONNABORTED
            case ECONNABORTED: result = kWSAenetdown; break;
    #endif
    #ifdef ECONNREFUSED
            case ECONNREFUSED: result = kWSAeconnrefused; break;
    #endif
    #ifdef ECONNRESET
            case ECONNRESET: result = kWSAeconnreset; break;
    #endif
            case EFAULT: result = kWSAefault; break;
    #ifdef EHOSTUNREACH
            case EHOSTUNREACH: result = kWSAehostunreach; break;
    #endif
    #ifdef EINPROGRESS
            case EINPROGRESS: result = kWSAeinprogress; break;
    #endif
            case EINTR: result = kWSAeintr; break;
            case EINVAL: result = kWSAeinval; break;
                // FIXME: case EIO: result = WSAE????; break;
    #ifdef EISCONN
            case EISCONN: result = kWSAeisconn; break;
    #endif
            // FIXME: case ELOOP: result = WSA????; break;
            case EMFILE: result = kWSAemfile; break;
    #ifdef EMSGSIZE
            case EMSGSIZE: result = kWSAemsgsize; break;
    #endif
                // FIXME: case ENAMETOOLONG: result = kWSAeacces; break;
    #ifdef ENETUNREACH
            case ENETUNREACH: result = kWSAenetunreach; break;
    #endif
    #ifdef ENOBUFS
            case ENOBUFS: result = kWSAenobufs; break;
    #endif
            // case ENOENT: result = WSAE????; break;
            case ENOMEM: result = kWSAenobufs; break;
    #ifdef ENOPROTOOPT
            case ENOPROTOOPT: result = kWSAenoprotoopt; break;
    #endif
    #ifdef ENOSR
            case ENOSR: result = kWSAenetdown; break;
    #endif
    #ifdef ENOTCONN
            case ENOTCONN: result = kWSAenotconn; break;
    #endif
                // FIXME: case ENOTDIR: result = WSAE????; break;
    #ifdef ENOTSOCK
            case ENOTSOCK: result = kWSAenotsock; break;
    #endif
            case ENOTTY: result = kWSAenotsock; break;
    #ifdef EOPNOTSUPP
            case EOPNOTSUPP: result = kWSAeopnotsupp; break;
    #endif
            case EPERM: result = kWSAeacces; break;
            case EPIPE: result = kWSAeshutdown; break;
    #ifdef EPROTONOSUPPORT
            case EPROTONOSUPPORT: result = kWSAeprotonosupport; break;
    #endif
    #if ERESTARTSYS
            case ERESTARTSYS: result = kWSAenetdown; break;
    #endif
                // FIXME: case EROFS: result = WSAE????; break;
    #ifdef ESOCKTNOSUPPORT
            case ESOCKTNOSUPPORT: result = kWSAesocktnosupport; break;
    #endif
    #ifdef ETIMEDOUT
            case ETIMEDOUT: result = kWSAetimedout; break;
    #endif
    #ifdef EWOULDBLOCK
            case EWOULDBLOCK: result = kWSAewouldblock; break;
    #endif
    #ifdef EADDRNOTAVAIL
            case EADDRNOTAVAIL: result = kWSAeaddrnotavail; break;
    #endif
            case ENOENT: result = kWSAeconnrefused; break;
    #ifdef EDESTADDRREQ
            case EDESTADDRREQ: result = kWSAedestaddrreq; break;
    #endif
            case ENODEV: result = kWSAenetdown; break;
    #ifdef EHOSTDOWN
            case EHOSTDOWN: result = kWSAehostdown; break;
    #endif
    #ifdef ENXIO
            case ENXIO: result = kWSAhostNotFound; break;
    #endif
            default:
                result = kWSAsyscallfailure;
                break;
        }

        return result;
    }

    ErrorCode FileErrnoToErrorCode(int32_t code)
    {
        ErrorCode ret;
        /* mapping ideas borrowed from wine. they may need some work */

        switch (code)
        {
            case EACCES: case EPERM: case EROFS:
                ret = kErrorCodeAccessDenied;
                break;

            case EAGAIN:
                ret = kErrorCodeSharingViolation;
                break;

            case EBUSY:
                ret = kErrorCodeLockViolation;
                break;

            case EEXIST:
                ret = kErrorCodeFileExists;
                break;

            case EINVAL: case ESPIPE:
                ret = kErrorSeek;
                break;

            case EISDIR:
                ret = kErrorCodeCannotMake;
                break;

            case ENFILE: case EMFILE:
                ret = kErrorCodeTooManyOpenFiles;
                break;

            case ENOENT: case ENOTDIR:
                ret = kErrorCodeFileNotFound;
                break;

            case ENOSPC:
                ret = kErrorCodeHandleDiskFull;
                break;

            case ENOTEMPTY:
                ret = kErrorCodeDirNotEmpty;
                break;

            case ENOEXEC:
                ret = kErrorBadFormat;
                break;

            case ENAMETOOLONG:
                ret = kErrorCodeFileNameExcedRange;
                break;

#ifdef EINPROGRESS
            case EINPROGRESS:
                ret = kErrorIoPending;
                break;
#endif

            case ENOSYS:
                ret = kErrorNotSupported;
                break;

            case EBADF:
                ret = kErrorCodeInvalidHandle;
                break;

            case EIO:
                ret = kErrorCodeInvalidHandle;
                break;

            case EINTR:
                ret = kErrorIoPending;
                break;

            case EPIPE:
                ret = kErrorCodeWriteFault;
                break;

            default:
                ret = kErrorCodeGenFailure;
                break;
        }

        return ret;
    }

    ErrorCode PathErrnoToErrorCode(const std::string& path, int32_t code)
    {
        if (code == ENOENT)
        {
            const std::string dirname(il2cpp::utils::PathUtils::DirectoryName(path));
#if !IL2CPP_HAS_NOACCESS
            if (access(dirname.c_str(), F_OK) == 0)
                return kErrorCodeFileNotFound;
#else
            struct stat statInfo;
            if (stat(dirname.c_str(), &statInfo) == 0 && S_ISDIR(statInfo.st_mode))
                return kErrorCodeFileNotFound;
#endif
            else
                return kErrorCodePathNotFound;
        }
        else
        {
            return FileErrnoToErrorCode(code);
        }
    }
}
}

#endif
