#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef _WIN32
# include <windows.h>
# include <shlwapi.h>
#endif

#ifndef _POSIX_HOST_NAME_MAX
#define _POSIX_HOST_NAME_MAX 255
#endif

/* define macros and variable for using the eina logging system  */
#define EFREET_MODULE_LOG_DOM /* no logging in this file */

#include "Efreet.h"
#include "efreet_private.h"

/* The URI standard is at http://tools.ietf.org/html/std66 */

EAPI Efreet_Uri *
efreet_uri_decode(const char *full_uri)
{
    Efreet_Uri *uri;
    const char *p;
    char scheme[64], authority[_POSIX_HOST_NAME_MAX], path[PATH_MAX];
    char *sep;
    int i = 0;

    EINA_SAFETY_ON_NULL_RETURN_VAL(full_uri, NULL);

    /* An uri should be in the form <scheme>:[<authority>][<path>][<query>][<fragment>] */

    /*
     * Specific code for Windows when the scheme part of full_uri is 'file',
     * for local Windows file path.
     * see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows
     *
     * Correct syntax :
     * file:///c:/path/to/file
     *
     * The returned path must be c:\path\to\file
     */
#ifdef _WIN32

    *scheme = 0;
    *authority = 0;
    *path = 0;

    if (strncasecmp(full_uri, "file://", strlen("file://")) == 0)
      {
         HRESULT res;
         DWORD len;
# ifdef UNICODE
         wchar_t buf[MAX_PATH];
         wchar_t *w_full_uri;
         char *uri;

         w_full_uri = evil_utf8_to_utf16(full_uri);
         if (!w_full_uri)
           return NULL;

         if (wcslen(w_full_uri) > 2048)
           {
              free(w_full_uri);
              return NULL;
           }

         len = sizeof(buf);
         res = PathCreateFromUrl(w_full_uri, buf, &len, 0UL);
         free(w_full_uri);
         if (res != S_OK)
           return NULL;
         uri = evil_utf16_to_utf8(buf);
         if (uri)
           {
              strncpy(path, uri, sizeof(path));
              path[sizeof(path)-1] = 0;
              goto win32_file_scheme;
           }
#else
         len = sizeof(path);
         res = PathCreateFromUrl(full_uri, path, &len, 0UL);
         if (res == S_OK)
           goto win32_file_scheme;
#endif
         return NULL;
      }
#endif

    sep = strchr(full_uri, ':');
    if (!sep) return NULL;
    /* check if we have a Windows PATH, that is a letter follow by a colon */
    if ((sep - full_uri) == 1) return NULL;

    memset(scheme, 0, 64);
    memset(authority, 0, _POSIX_HOST_NAME_MAX);
    memset(path, 0, PATH_MAX);

    /* parse scheme */
    p = full_uri;
    for (i = 0; *p != ':' && *p != '\0' && i < (64 - 1); p++, i++)
         scheme[i] = *p;
    if (i == 0) return NULL; /* scheme is required */
    scheme[i] = '\0';

    /* parse authority */
    p++;
    if (*p == '/')
    {
        p++;
        if (*p == '/')
        {
            p++;
            for (i = 0; *p != '/' && *p != '?' && *p != '#' && *p != '\0' && i < (_POSIX_HOST_NAME_MAX - 1); p++, i++)
                authority[i] = *p;
            authority[i] = '\0';
        }
        else /* It's a path, let path parser handle the leading slash */
            p--;
    }
    else
        authority[0] = '\0';

    /* parse path */
    /* See http://www.faqs.org/rfcs/rfc1738.html for the escaped chars */
    for (i = 0; *p != '\0' && i < (PATH_MAX - 1); i++, p++)
    {
        if (*p == '%')
        {
            path[i] = *(++p);
            path[i + 1] = *(++p);
            path[i] = (char)strtol(&(path[i]), NULL, 16);
            path[i + 1] = '\0';
        }
        else
            path[i] = *p;
    }

#ifdef _WIN32
 win32_file_scheme:
    strcpy(scheme, "file");
#endif

    uri = NEW(Efreet_Uri, 1);
    if (!uri) return NULL;

    uri->protocol = eina_stringshare_add(scheme);
    uri->hostname = eina_stringshare_add(authority);
    uri->path = eina_stringshare_add(path);

    return uri;
}

EAPI const char *
efreet_uri_encode(Efreet_Uri *uri)
{
    char dest[PATH_MAX * 3 + 4];
    const char *p;
    int i;

    EINA_SAFETY_ON_NULL_RETURN_VAL(uri, NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(uri->path, NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(uri->protocol, NULL);

    memset(dest, 0, PATH_MAX * 3 + 4);
    snprintf(dest, strlen(uri->protocol) + 4, "%s://", uri->protocol);

    /* Most app doesn't handle the hostname in the uri so it's put to NULL */
    for (i = strlen(uri->protocol) + 3, p = uri->path; *p != '\0'; p++, i++)
    {
        if (isalnum(*p) || strchr("/$-_.+!*'()", *p))
            dest[i] = *p;
        else
        {
            snprintf(&(dest[i]), 4, "%%%02X", (unsigned char) *p);
            i += 2;
        }
    }

    return eina_stringshare_add(dest);
}

EAPI void
efreet_uri_free(Efreet_Uri *uri)
{
    if (!uri) return;

    IF_RELEASE(uri->protocol);
    IF_RELEASE(uri->path);
    IF_RELEASE(uri->hostname);
    FREE(uri);
}
