// See the file "COPYING" in the main distribution directory for copyright.

#include "Foo.h"

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cerrno>
#ifdef _MSC_VER
#include <cstdlib>
#define random() rand()
#endif

#include "zeek/threading/Manager.h"
#include "zeek/threading/SerialTypes.h"

using namespace btest::input::reader;
using zeek::threading::Field;
using zeek::threading::Value;

Foo::Foo(zeek::input::ReaderFrontend* frontend) : zeek::input::ReaderBackend(frontend) {
    ascii = new zeek::threading::formatter::Ascii(this, zeek::threading::formatter::Ascii::SeparatorInfo());
}

Foo::~Foo() {
    DoClose();
    delete ascii;
}

void Foo::DoClose() {}

bool Foo::DoInit(const zeek::input::ReaderBackend::ReaderInfo& info, int num_fields, const Field* const* fields) {
    DoUpdate();
    return true;
}

std::string Foo::RandomString(const int len) {
    std::string s(len, ' ');

    static const char values[] =
        "0123456789!@#$%^&*()-_=+{}[]\\|"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";

    for ( int i = 0; i < len; ++i )
        // zeek::random_number() is not thread-safe; as we are only using one simultaneous thread
        // here, this should not matter in this case. If this test ever starts showing
        // random errors, this might be the culprit.
        s[i] = values[zeek::util::detail::random_number() / (zeek::util::detail::max_random() / sizeof(values))];

    return s;
}

// read the entire file and send appropriate thingies back to InputMgr
bool Foo::DoUpdate() {
    int linestosend = 5;
    for ( int i = 0; i < linestosend; i++ ) {
        Value** field = new Value*[NumFields()];
        for ( int j = 0; j < NumFields(); j++ )
            field[j] = EntryToVal(Fields()[j]->type, Fields()[j]->subtype);

        SendEntry(field);
    }

    EndCurrentSend();

    return true;
}

zeek::threading::Value* Foo::EntryToVal(zeek::TypeTag type, zeek::TypeTag subtype) {
    Value* val = new Value(type, true);

    // basically construct something random from the fields that we want.

    switch ( type ) {
        case zeek::TYPE_ENUM: assert(false); // no enums, please.

        case zeek::TYPE_STRING: {
            std::string rnd = RandomString(10);
            val->val.string_val.data = zeek::util::copy_string(rnd.c_str(), rnd.size());
            val->val.string_val.length = rnd.size();
            break;
        }

        case zeek::TYPE_BOOL:
            val->val.int_val = 1; // we never lie.
            break;

        case zeek::TYPE_INT: val->val.int_val = random(); break;

        case zeek::TYPE_TIME: val->val.double_val = 0; break;

        case zeek::TYPE_DOUBLE:
        case zeek::TYPE_INTERVAL: val->val.double_val = random(); break;

        case zeek::TYPE_COUNT: val->val.uint_val = random(); break;

        case zeek::TYPE_PORT:
            val->val.port_val.port = random() / (RAND_MAX / 60000);
            val->val.port_val.proto = TRANSPORT_UNKNOWN;
            break;

        case zeek::TYPE_SUBNET: {
            val->val.subnet_val.prefix = ascii->ParseAddr("192.168.17.1");
            val->val.subnet_val.length = 16;
        } break;

        case zeek::TYPE_ADDR: val->val.addr_val = ascii->ParseAddr("192.168.17.1"); break;

        case zeek::TYPE_TABLE:
        case zeek::TYPE_VECTOR:
            // First - common initialization
            // Then - initialization for table.
            // Then - initialization for vector.
            // Then - common stuff
            {
                // how many entries do we have...
                unsigned int length = random() / (RAND_MAX / 15);

                Value** lvals = new Value*[length];

                if ( type == zeek::TYPE_TABLE ) {
                    val->val.set_val.vals = lvals;
                    val->val.set_val.size = length;
                }
                else if ( type == zeek::TYPE_VECTOR ) {
                    val->val.vector_val.vals = lvals;
                    val->val.vector_val.size = length;
                }
                else
                    assert(false);

                if ( length == 0 )
                    break; // empty

                for ( unsigned int pos = 0; pos < length; pos++ ) {
                    Value* newval = EntryToVal(subtype, zeek::TYPE_ENUM);
                    if ( newval == 0 ) {
                        Error("Error while reading set");
                        delete val;
                        return 0;
                    }
                    lvals[pos] = newval;
                }

                break;
            }

        default:
            Error(Fmt("unsupported field format %d", type));
            delete val;
            return 0;
    }

    return val;
}

bool Foo::DoHeartbeat(double network_time, double current_time) { return true; }
