//
//  main.cpp
//
//
//  Created by David Sherman on 2014-12-30.
//
//

/*
	Aseba - an event-based framework for distributed robot control
	Copyright (C) 2007--2016:
	Stephane Magnenat <stephane at magnenat dot net>
	(http://stephane.magnenat.net)
	and other contributors, see authors.txt for details

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published
	by the Free Software Foundation, version 3 of the License.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <cstdlib>
#include <cstring>
#include <iostream>

#include "http.h"
#include "common/consts.h"
#include "common/types.h"
#include "common/utils/utils.h"
#include "common/msg/NodesManager.h"
#include "transport/dashel_plugins/dashel-plugins.h"

//! Show usage
void dumpHelp(std::ostream &stream, const char *programName)
{
    stream << "Aseba http, connects aseba components together and with HTTP, usage:\n";
    stream << programName << " [options] [additional targets]*\n";
    stream << "Options:\n";
    stream << "-v, --verbose   : makes the switch verbose\n";
    stream << "-d, --dump      : makes the switch dump the content of messages\n";
    stream << "-p, --http port : listens to incoming HTTP connection on this port\n";
    stream << "-s, --port port : listens to incoming Aseba connection on this port\n";
    stream << "-a, --aesl file : load program definitions from AESL file\n";
    stream << "--autorestart   : restart switch in case of error (else exit)\n";
    stream << "-K, --Kiter n   : run I/O loop n thousand times (for profiling)\n";
    stream << "-h, --help      : shows this help\n";
    stream << "-V, --version   : shows the version number\n";
    stream << "Additional targets are any valid Dashel targets." << std::endl;
    stream << "Report bugs to: david.sherman@inria.fr" << std::endl;
}

//! Show version
void dumpVersion(std::ostream &stream)
{
    stream << "asebahttp 2015-01-01 David James Sherman <david dot sherman at inria dot fr>" << std::endl;
    stream << "Aseba version " << ASEBA_VERSION << std::endl;
    stream << "Aseba protocol " << ASEBA_PROTOCOL_VERSION << std::endl;
    stream << "Aseba library licence LGPLv3: GNU LGPL version 3 <http://www.gnu.org/licenses/lgpl.html>\n";
}

// Main
int main(int argc, char *argv[])
{
    Dashel::initPlugins();

    std::string http_port = "3000";
    std::string aseba_port;
    std::string aesl_filename;
    std::vector<std::string> dashel_target_list;
    bool verbose = false;
    bool dump = false;
    bool autoRestart = false;
    int Kiterations = -1; // set to > 0 to limit run time e.g. for valgrind

    // process command line
    int argCounter = 1;
    while (argCounter < argc)
    {
        const char *arg = argv[argCounter++];

        if ((strcmp(arg, "-v") == 0) || (strcmp(arg, "--verbose") == 0))
            verbose = true;
        else if ((strcmp(arg, "-d") == 0) || (strcmp(arg, "--dump") == 0))
            dump = true;
        else if ((strcmp(arg, "-h") == 0) || (strcmp(arg, "--help") == 0))
            dumpHelp(std::cout, argv[0]), exit(1);
        else if ((strcmp(arg, "-V") == 0) || (strcmp(arg, "--version") == 0))
            dumpVersion(std::cout), exit(1);
        else if ((strcmp(arg, "-p") == 0) || (strcmp(arg, "--http") == 0))
            http_port = argv[argCounter++];
        else if ((strcmp(arg, "-s") == 0) || (strcmp(arg, "--port") == 0))
            aseba_port = argv[argCounter++];
        else if ((strcmp(arg, "-a") == 0) || (strcmp(arg, "--aesl") == 0))
            aesl_filename = argv[argCounter++];
        else if (strcmp(arg, "--autorestart") == 0)
            autoRestart = true;
        else if ((strcmp(arg, "-K") == 0) || (strcmp(arg, "--Kiter") == 0))
            Kiterations = atoi(argv[argCounter++]);
        else if (strncmp(arg, "-", 1) != 0)
            dashel_target_list.push_back(arg);
    }

    // initialize Dashel plugins
    Dashel::initPlugins();

    // create and run bridge, catch Dashel exceptions
    do
    {
        try
        {
            Aseba::HttpInterface network(dashel_target_list, http_port, aseba_port,
                                         Kiterations > 0 ? 1000*Kiterations : 5, dump, verbose);

            for (auto nodeId: network.allNodeIds())
                try {
                    if (aesl_filename.size() > 0)
                        network.aeslLoadFile(nodeId, aesl_filename);
                    else
                    {
                        const char* failsafe = "<!DOCTYPE aesl-source><network><keywords flag=\"true\"/><node nodeId=\"1\" name=\"thymio-II\"></node></network>\n";
                        network.aeslLoadMemory(nodeId, failsafe,strlen(failsafe));
                    }
                } catch ( std::runtime_error(e) ) {
                    std::cerr << "Error loading aesl for nodeId " << nodeId << std::endl;
                }

            do {
                network.pingNetwork(); // possibly not, if do_ping == false
            } while (network.run1s());
            return 0; // normal exit (exit autoRestart loop)
        }
        catch(Dashel::DashelException e)
        {
            std::cerr << "Unhandled Dashel exception: " << e.what() << std::endl;
        }
        catch (Aseba::InterruptException& e) {
            std::cerr << " attempting graceful network shutdown" << std::endl;
            return 0; // normal exit (exit autoRestart loop)
        }
    } while (autoRestart);

    return 1; // error following an exception
}
