/*
 * © Copyright 1996-2012 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include <string>
#include <vector>

#include "pproc.h"
#include "mars_client_config.h"

#include "eckit/runtime/Main.h"
#include "eckit/io/Buffer.h"
#include "eckit/log/SysLog.h"
#include "eckit/net/UDPClient.h"
#include "eckit/log/JSON.h"
#include "eckit/utils/Tokenizer.h"
#include "eckit/utils/Translator.h"
#include "eckit/exception/Exceptions.h"

using eckit::Buffer;
using eckit::SysLog;
using eckit::net::UDPClient;
using eckit::JSON;
using eckit::Tokenizer;
using eckit::Translator;


static void add_to_json(const std::string& name, const request *r, JSON& msg, bool hidden) {

    msg << name;

    msg.startObject();

    parameter *par = r->params;
    while(par)
    {
        int    n = count_values(r, par->name);
        value *v = par->values;
        int    count = 0;

        if(*(par->name) == '_' and not hidden) { par = par->next; continue; }

        msg << lowcase(par->name);

        switch(n) {

            case 0:
                msg.null();
                break;

            case 1:
                if(v) msg << lowcase(no_quotes(v->name));
                break;

            default:
                msg.startList();
                while(v)
                {
                    if(count >= 1024) // don't send more than 1024 values
                    {
                        int remain = n - count - 1;
                        /// @todo do something with remain, print "(nnn)" ??
                        const char *last = get_value(r,par->name,n-1);
                        msg << lowcase(no_quotes(last));
                        break;
                    }

                    msg << lowcase(no_quotes(v->name));
                    v = v->next;
                    count++;
                }
                msg.endList();
                break;
        }

        par = par->next;
    }

    msg.endObject();
}

//--------------------------------------------------------------------------------------------------

extern "C" {

void mars_eckit_init(int *argc, char **argv) {
    eckit::Main::initialise(*argc, argv);
}

static void get_udp_servers(std::vector<std::string>& hostnames, std::vector<int>& ports) {

    if(mars.udp_server) {

        std::string hoststr = mars.udp_server;
        std::vector<std::string> tmp;

        Tokenizer(":")(hoststr, tmp);

        if(tmp.size() % 2 != 0) {
            marslog(LOG_EROR, "MARS UDP server config [%s] does not contain pairs of the form hostname1:port1:hostname2:port2:...", mars.udp_server);
            return;
        }

        hostnames.clear();
        ports.clear();

        for(int i = 0; i < tmp.size(); ++i, ++i) {
            hostnames.push_back(tmp[i]);
            ports.push_back(Translator<std::string,int>()(tmp[i+1]));
        }
    }
}

void send_udp_statistics(const request *stats, const request *env, const request *req) {

    if(mars.udp_server) {

        std::ostringstream message;
        JSON json(message);

        json.startObject();

        add_to_json("stats", stats, json, false);
        add_to_json("env", env, json, true);
        add_to_json("req", req, json, false);

        json.endObject();

        std::vector<std::string> hostnames;
        std::vector<int> ports;

        get_udp_servers(hostnames, ports);

        ASSERT(hostnames.size() == ports.size());

        for(int i = 0; i < hostnames.size(); ++i) {

            std::string hostname = hostnames[i];
            int port             = ports[i];

            try {
                UDPClient client(hostname, port);

                marslog(LOG_DBUG, "Sending UPD statistics to server %s port %d", hostname.c_str(), port);

                SysLog log(message.str(), 0, SysLog::Local7, SysLog::Info);
                log.appName("mars-client");
                Buffer buff(log);

                client.send(buff, buff.size());
            }
            catch (eckit::Exception& e) {
                marslog(LOG_EROR, "Failed to send statistics via UPD to server %s port %d -- %s", hostname.c_str(), port, e.what());
            }
        }
    }
}

} // extern "C"
