/**
 * This benchmarks GC in a producer-consumer program.
 *
 * Copyright: Copyright Martin Nowak 2014 -.
 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
 * Authors:   Martin Nowak
 */
import std.algorithm, std.concurrency, std.conv, std.file, std.json, std.range;

JSONValue buildVal(in dchar[] word)
{
    JSONValue[string] res;
    res["word"] = word.to!string;
    res["length"] = word.length;
    auto pos = new size_t[word.length];
    foreach (i; 0 .. pos.length)
        pos[i] = i;
    res["array"] = pos;
    return JSONValue(res);
}

void producer(Tid consumer)
{
    auto text = cast(string)read("extra-files/dante.txt");
    foreach (word; text.splitter.map!(to!(dchar[])))
    {
        foreach (_; 0 .. 3)
        {
            immutable val = buildVal(word);
            consumer.send(val);
            if (!nextPermutation(word)) break;
        }
    }
}

void serialize(in JSONValue val, ref ubyte[] buf)
{
    with (JSON_TYPE) switch (val.type)
    {
    case OBJECT:
        foreach (k, v; val.object)
        {
            buf ~= cast(ubyte[])k;
            serialize(v, buf);
        }
        break;

    case ARRAY:
        foreach (v; val.array)
            serialize(v, buf);
        break;

    case UINTEGER:
        ulong v = val.uinteger;
        buf ~= (cast(ubyte*)&v)[0 .. v.sizeof];
        break;

    case STRING:
        buf ~= cast(ubyte[])val.str;
        break;

    default:
        assert(0);
    }
}

struct Socket
{
    static void send(ubyte[] buf) { _buf = buf; }
    static ubyte[] _buf; // keep a reference
}

void log(string s)
{
    __gshared size_t dummy;
    dummy = s.length;
}

void consumer()
{
    scope (failure) assert(0);
    while (true)
    {
        auto msg = receiveOnly!(Variant);
        if (msg.peek!OwnerTerminated) return;
        auto val = msg.get!(immutable JSONValue);
        ubyte[] buf; serialize(val, buf);
        Socket.send(buf);
    }
}

void main(string[] args)
{
    producer(spawn(&consumer));
}
