#include "fmod_settings.h"

#include "fmod.h"
#include "fmod_net.h"
#include "fmod_string.h"
#include "fmod_memory.h"

#include <stdlib.h>


enum
{
    FMOD_NET_VERSION_HTTP_1_0 = 0,
    FMOD_NET_VERSION_HTTP_1_1,
    FMOD_NET_VERSION_ICY,
    CT_FMOD_NET_VERSION
};

static const char *FMOD_Net_VersionString[CT_FMOD_NET_VERSION] = 
{
    "HTTP/1.0",
    "HTTP/1.1",
    "ICY",
};

static const char base64_code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

char *FMOD_Net_ProxyString   = 0;
char *FMOD_Net_ProxyHostname = 0;
int   FMOD_Net_ProxyPort     = 0;
char *FMOD_Net_ProxyAuth     = 0;        // Base64 encoded, user:pass pair
int   FMOD_Net_Timeout       = 5000;



/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
FMOD_RESULT FMOD_Net_ParseHTTPStatus(char *buf, int buflen, int *version, int *statuscode)
{
    int i, j, k;

    for (i=0;(i < buflen) && (buf[i] != 0) && (buf[i] != ' ');i++);

    if (i < buflen)
    {
        buf[i] = 0;

        for (j=0;j < CT_FMOD_NET_VERSION;j++)
        {
            if (!FMOD_strcmp(buf, FMOD_Net_VersionString[j]))
            {
                *version = j;
                break;
            }
        }

        if (j < CT_FMOD_NET_VERSION)
        {
            i++;
            k = i;
            for (;(i < buflen) && (buf[i] != 0) && (buf[i] != ' ');i++);

            if (i < buflen)
            {
                buf[i] = 0;
                *statuscode = atoi(&buf[k]);
                return FMOD_OK;
            }
        }
    }

    return FMOD_ERR_INVALID_PARAM;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
#define CHECK_LENGTH if (out_cnt == outbuflen) { return FMOD_ERR_INVALID_PARAM; }

FMOD_RESULT FMOD_Net_EncodeBase64(char *inbuf, char *outbuf, int outbuflen)
{
    int bits = 0, char_count = 0, out_cnt = 0, c;

    if (!inbuf || !outbuf)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    while ((c = (unsigned char)*inbuf++))
    {
        bits += c;
        char_count++;
        if (char_count == 3)
        {
            CHECK_LENGTH
            outbuf[out_cnt++] = base64_code[bits >> 18];
            CHECK_LENGTH
            outbuf[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
            CHECK_LENGTH
            outbuf[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
            CHECK_LENGTH
            outbuf[out_cnt++] = base64_code[bits & 0x3f];
            bits = 0;
            char_count = 0;
        }
        else
        {
            bits <<= 8;
        }
    }

    if (char_count != 0)
    {
        bits <<= 16 - (8 * char_count);
        CHECK_LENGTH
        outbuf[out_cnt++] = base64_code[bits >> 18];
        CHECK_LENGTH
        outbuf[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
        if (char_count == 1)
        {
            CHECK_LENGTH
            outbuf[out_cnt++] = '=';
            CHECK_LENGTH
            outbuf[out_cnt++] = '=';
        }
        else
        {
            CHECK_LENGTH
            outbuf[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
            CHECK_LENGTH
            outbuf[out_cnt++] = '=';
        }
    }

    CHECK_LENGTH
    outbuf[out_cnt] = 0;

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
FMOD_RESULT FMOD_Net_SetProxy(const char *proxy)
{
    char *hostname, *username, *tmp, *p;
    FMOD_RESULT result;
    int port;

    if (FMOD_Net_ProxyString)
    {
        FMOD_Memory_Free(FMOD_Net_ProxyString);
        FMOD_Net_ProxyString = 0;
    }

    if (FMOD_Net_ProxyHostname)
    {
        FMOD_Memory_Free(FMOD_Net_ProxyHostname);
        FMOD_Net_ProxyHostname = 0;
    }

    if (FMOD_Net_ProxyAuth)
    {
        FMOD_Memory_Free(FMOD_Net_ProxyAuth);
        FMOD_Net_ProxyAuth = 0;
    }

    FMOD_Net_ProxyPort = 0;

    if (FMOD_strlen(proxy))
    {
        p = FMOD_strdup(proxy);
        if (!p)
        {
            return FMOD_ERR_MEMORY;
        }
    }
    else
    {
        /*
            Setting proxy server to null here
        */
        return FMOD_OK;
    }

    FMOD_Net_ProxyString = FMOD_strdup(proxy);
    if (!FMOD_Net_ProxyString)
    {
        return FMOD_ERR_MEMORY;
    }

    hostname = FMOD_strstr(p, "@");
    if (hostname)
    {
        *hostname = 0;
        hostname++;
        username = p;
    }
    else
    {
        hostname = p;
        username = 0;
    }

    if (username)
    {
        char buf[4096];

        result = FMOD_Net_EncodeBase64(username, buf, 4095);
        if (result != FMOD_OK)
        {
            FMOD_Memory_Free(p);
            return result;
        }

        FMOD_Net_ProxyAuth = FMOD_strdup(buf);
        if (!FMOD_Net_ProxyAuth)
        {
            return FMOD_ERR_MEMORY;
        }
    }

    tmp = FMOD_strstr(hostname, ":");
    if (tmp)
    {
        *tmp = 0;
        tmp++;
        port = atoi(tmp);
    }
    else
    {
        port = 80;
    }

    FMOD_Net_ProxyHostname = FMOD_strdup(hostname);
    if (!FMOD_Net_ProxyHostname)
    {
        return FMOD_ERR_MEMORY;
    }

    FMOD_Net_ProxyPort     = port;

    FMOD_Memory_Free(p);

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
FMOD_RESULT FMOD_Net_GetProxy(char *proxy, int proxylen)
{
    if (proxy && proxylen)
    {
        if (FMOD_Net_ProxyString)
        {
            FMOD_strncpy(proxy, FMOD_Net_ProxyString, proxylen);
            proxy[proxylen - 1] = 0;
        }
        else
        {
            proxy[0] = 0;
        }
    }

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
FMOD_RESULT FMOD_Net_SetTimeout(int timeout)
{
    FMOD_Net_Timeout = timeout;

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]

	[PARAMETERS]
 
	[RETURN_VALUE]

	[REMARKS]

	[SEE_ALSO]
]
*/
FMOD_RESULT FMOD_Net_GetTimeout(int *timeout)
{
    if (!timeout)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    *timeout = FMOD_Net_Timeout;

    return FMOD_OK;
}
