//
// URL.cpp
//

#define DBG_LEVEL 0
#include <Raym/Log.h>
#include <Raym/URL.h>

namespace Raym
{

#define RELEASE(P1)         \
    if ((P1) != NULL)       \
    {                       \
        (P1)->release();    \
        (P1) = NULL;        \
    }

URL::URL()
{
    DebugLog2("URL::URL()\n");

    _scheme   = NULL;
    _host     = NULL;
    _port     = -1;
    _user     = NULL;
    _password = NULL;
    _path     = NULL;
}

URL::~URL()
{
    DebugLog2("URL::~URL()\n");

    RELEASE(_scheme);
    RELEASE(_host);
    _port = -1;
    RELEASE(_user);
    RELEASE(_password);
    RELEASE(_path);
}

URL *URL::URLWithString(String *URLString)
{
    DebugLog2("URL::URLWithString()\n");

    URL *result = NULL;
    if (URLString != NULL)
    {
        result = URL::alloc()->initWithString(URLString);
        if (result != NULL)
        {
            result->autorelease();
        }
    }
    return result;
}

URL *URL::URLWithString(const char *URLString)
{
    DebugLog2("URL::URLWithString()\n");

    return URL::URLWithString(String::stringWithUTF8String(URLString));
}

URL *URL::alloc()
{
    DebugLog2("URL::alloc()\n");

    return new URL();
}

URL *URL::initWithString(String *URLString)
{
    DebugLog2("URL::initWithString(String *)\n");

    if (URLString == NULL)
    {
        DebugLog0("warning: URLString is NULL\n");
        release();
        return NULL;
    }

    // URL解析で必要なのでコピーする
#ifdef _WIN32
    char *urls = _strdup(URLString->cString());
#else
    char *urls = strdup(URLString->cString());
#endif
    if (urls == NULL)
    {
        DebugLog0("warning: strdup NG.\n");
        release();
        return NULL;
    }

    // 最初のコロン":"をスキームの区切り文字として扱う
    char *colon = strchr(urls, ':');
    if (colon == NULL)
    {
        DebugLog0("warning: format error. (no colon)\n");
        free(urls);
        release();
        return NULL;
    }

    *colon = '\0';

    // スキーム名のチェック
    if ((strcmp(urls, "http") != 0) &&
        (strcmp(urls, "ftp") != 0) &&
        (strcmp(urls, "file") != 0) &&
        (strcmp(urls, "udp") != 0))
    {
        DebugLog0("warning: format error. (scheme)\n");
        free(urls);
        release();
        return NULL;
    }

    // スキーム名の保存
    _scheme = String::alloc()->initWithUTF8String(urls);

    char *urls2 = colon + 1;

    // http または ftp の場合
    if ((strcmp(urls, "http") == 0) || (strcmp(urls, "ftp") == 0) || (strcmp(urls, "udp") == 0))
    {
        // 先頭が "//" かチェック
        if (strncmp(urls2, "//", 2) != 0)
        {
            DebugLog0("warning: format error. (prefix)\n");
            free(urls);
            release();
            return NULL;
        }

        // "//" をスキップしたアドレスを設定
        char *host = urls2 + 2;

        // パスとの区切り文字を探す
        char *slash = strchr(host, '/');
        if (slash != NULL)
        {
            *slash = '\0';
        }

        // "@" を探す
        char *atmark = strchr(host, '@');
        if (atmark != NULL)
        {
            // user:password がある
            *atmark = '\0';

            // ":" を探す
            colon = strchr(host, ':');
            if (colon != NULL)
            {
                // user と pass
                *colon = '\0';
                _user = String::alloc()->initWithUTF8String(host);
                _password = String::alloc()->initWithUTF8String(colon + 1);
            }
            else
            {
                // ":"が無いので user のみ
                _user = String::alloc()->initWithUTF8String(host);
                _password = String::alloc()->init();
            }

            host = atmark + 1;
        }
        else
        {
            _user = String::alloc()->init();
            _password = String::alloc()->init();
        }

        // ":" を探す
        colon = strchr(host, ':');
        if (colon != NULL)
        {
            *colon = '\0';
            char *port = colon + 1;
            _port = atoi(port);
            char tmpport[80];
#ifdef _WIN32
            _snprintf_s(tmpport, sizeof(tmpport), _TRUNCATE, "%d", _port);
#else
            snprintf(tmpport, sizeof(tmpport), "%d", _port);
#endif
            if (strcmp(tmpport, port) != 0)
            {
                DebugLog0("warning: format error. (port)\n");
                free(urls);
                release();
                return NULL;
            }
        }
        else if (strcmp(urls, "http") == 0)
        {
            _port = 80;
        }
        else if (strcmp(urls, "ftp") == 0)
        {
            _port = 21;
        }
        else
        {
            _port = -1;
        }

        _host = String::alloc()->initWithUTF8String(host);

        if (slash == NULL)
        {
            _path = String::alloc()->initWithUTF8String("/");
        }
        else
        {
            *slash = '/';
            _path = String::alloc()->initWithUTF8String(slash);
        }
    }
    else if (strcmp(urls, "file") == 0)
    {
        DebugLog0("not implement.\n");
    }
    else
    {
        DebugLog0("not implement.\n");
    }

    free(urls);

    return this;
}

URL *URL::initWithString(const char *URLString)
{
    DebugLog2("URL::initWithString(const char *)\n");

    return initWithString(String::stringWithUTF8String(URLString));
}

URL *URL::retain()
{
    DebugLog2("URL::retain()");

    Object::retain();
    return this;
}

String *URL::scheme()
{
    return _scheme;
}

String *URL::host()
{
    return _host;
}

int URL::port()
{
    return _port;
}

String *URL::user()
{
    return _user;
}

String *URL::password()
{
    return _password;
}

String *URL::path()
{
    return _path;
}

const char *URL::className()
{
    return "URL";
}

} // Raym
