/*
 * Copyright 2009 Funambol, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */


#include "CurlDownloader.h"
#include <Logger/LoggerMacroses.h>

#define HTTP_OK 200
#define HTTP_NOT_FOUND 404

const char *c_CurlDownloaderLog = "CurlDownloader";

using namespace NS_DM_Client::NS_Common;


CurlDownloader::CurlDownloader(const char *URL) : Downloader(URL)
{
    context = curl_easy_init();
    if (context)
    {
        m_error = curl_easy_setopt(context, CURLOPT_USERAGENT, "Funambol DM Client");
    }
    else
    {
        m_error = CURLE_FAILED_INIT;
    }
}


CurlDownloader::~CurlDownloader()
{
    if (context)
        curl_easy_cleanup(context);
}


Downloader::DownloadStatus CurlDownloader::Download()
{
    if (!context)
    {
        return DS_NOT_INITIALIZED;
    }

    if ((m_error = curl_easy_setopt(context, CURLOPT_URL, m_url)) ||
        (m_error = curl_easy_setopt(context, CURLOPT_WRITEFUNCTION, &writeDataCallback)) ||
        (m_error = curl_easy_setopt(context, CURLOPT_WRITEDATA, this)) ||
        (m_error = curl_easy_setopt(context, CURLOPT_HEADERFUNCTION, responseHeader)) ||
        (m_error = curl_easy_setopt(context, CURLOPT_WRITEHEADER, this)) ||
        (m_error = curl_easy_perform(context))
        )
    {
        // finalize
        // what ???
        long res_code = 0;
        if (CURLE_OK == curl_easy_getinfo(context, CURLINFO_RESPONSE_CODE, &res_code))
        {
            m_error = res_code;
        }
        return DS_FAILED;
    }
    else
    {
        // all exited with CURLE_OK = 0
        long res_code = 0;
        if (CURLE_OK == curl_easy_getinfo(context, CURLINFO_RESPONSE_CODE, &res_code))
        {
            m_error = res_code;
            if (HTTP_OK != res_code)
                return DS_MALFORMED_URL;
        }
        return DS_FINISHED;
    }
}


size_t CurlDownloader::responseHeader(void *buffer, size_t size, size_t nmemb, void *stream)
{
    CurlDownloader *downloader = (CurlDownloader*)stream;
    size_t curr = size * nmemb;

    char cbf[256];
    memset(cbf, 0, 256);
    if (curr < 256) {
        memcpy(cbf, buffer, curr);

        // find HTTP property with ':' as a delimiter
        const char *propName  = strtok(cbf, ":");
        const char *value = strtok(NULL, ":");
        const int   valsize = value ? strlen(value) : 0;
        char *propValue = NULL;

        if (value && valsize) {
            int len=0;
            bool skipchars = true;
            propValue = new char[valsize+1];
            if(propValue == NULL)
            {
                LOG_ERROR_(NS_Logging::GetLogger(c_CurlDownloaderLog), "malloc propValue");
                return 0;
            }

            memset(propValue, '\0', valsize+1);
            // skip first blank space ' ' and trailings \r\n
            while (*value != '\r' && *value != '\n' && *value != '\0' && len <= valsize) {
                skipchars = skipchars && (' ' == *value);
                if (skipchars) {
                    value++;
                    continue;
                }

                propValue[len] = *value;
                value++;
                len++;
            }
////            propValue[len] = 0;
        }

        if (propName && propValue)
            downloader->setProperty(propName, propValue);
        
        if (propValue != NULL) delete [] propValue;
    }
    return curr;
}


size_t CurlDownloader::writeDataCallback(void *ptr, size_t size, size_t nmemb, void *stream)
{
    CurlDownloader *downloader = (CurlDownloader*)stream;

    // if 0 is returned, curl will break downloading
    return downloader->storeBuffer(ptr, size*nmemb);
}

