module lunch.http;

import std.stdio;
import std.file;
import std.traits;
import std.string;
import std.net.curl :
    c_get = get,
    c_post = post,
    c_put = put,
    HTTP,
    CurlException,
    AutoProtocol;
import core.thread;
import lunch.logger;



private ReturnType!fun _retry(alias fun)(Parameters!fun args)
{
    CurlException last_ex;
    for (int n = 0; n < 10; n++)
    {
        try
            return fun(args);
        catch (CurlException ex)
            last_ex = ex;
        Thread.sleep(dur!"msecs"(500));
    }
    throw last_ex;
}


private const(char)[] _sanitize(const(char)[] url)
{
    return url.replace(" ", "%20");
}


T[] get(Conn = AutoProtocol, T = char)(const(char)[] url, Conn conn = Conn())
{
    try
    {
        T[] res = _retry!(c_get!(Conn, T))(_sanitize(url), conn);
        infof("GET %s", url);
        return res;
    }
    catch (CurlException ex)
    {
        erroref("GET %s", ex, url);
        assert(0);
    }
}

T[] post(T = char, PostUnit)(const(char)[] url, const(PostUnit)[] postData, HTTP conn = HTTP())
{
    try
    {
        T[] res = _retry!(c_post!(T, PostUnit))(_sanitize(url), postData, conn);
        infof("POST %s", url);
        return res;
    }
    catch (CurlException ex)
    {
        erroref("POST %s", ex, url);
        assert(0);
    }
}

T[] put(Conn = AutoProtocol, T = char, PutUnit)(const(char)[] url, const(PutUnit)[] putData, Conn conn = Conn())
{
    try
    {
        T[] res = _retry!(c_put!(Conn, T, PutUnit))(_sanitize(url), putData, conn);
        infof("PUT %s", url);
        return res;
    }
    catch (CurlException ex)
    {
        erroref("PUT %s", ex, url);
        assert(0);
    }
}

private void _download(const(char)[] url, string file, void delegate(size_t size) progress)
{
    scope (failure)
    if (exists(file))
        remove(file);

    File handle = File(file, "wb");

    HTTP http = HTTP(_sanitize(url));
    http.onReceive = (ubyte[] data)
    {
        handle.rawWrite(data);
        if (progress)
            progress(data.length);
        return data.length;
    };
    http.perform();
}

void download(const(char)[] url, string file, void delegate(size_t size) progress = null)
{
    try
    {
        _retry!_download(url, file, progress);
        infof("DL %s", url);
    }
    catch (CurlException ex)
    {
        erroref("DL %s", cast(Exception)ex, url);
        assert(0);
    }
}
