+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/net/inet.h>
+#include "libmarklin/control.h"
+#include "libmarklin/locomotive.h"
+#include "libmarklin/locotype.h"
+#include "server.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+Server::Server(TrafficManager &tm):
+ trfc_mgr(tm),
+ listen_sock(Net::INET),
+ event_disp(0)
+{
+ const list<Train *> &trains = trfc_mgr.get_trains();
+ for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ {
+ Locomotive &loco = (*i)->get_locomotive();
+ (*i)->signal_target_speed_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_speed_changed), sigc::ref(**i)));
+ loco.signal_function_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_function_changed), sigc::ref(**i)));
+ (*i)->signal_status_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_status_changed), sigc::ref(**i)));
+ }
+
+ listen_sock.listen(Net::InetAddr(0, 8315), 4);
+ listen_sock.signal_data_available.connect(sigc::mem_fun(this, &Server::incoming_connection));
+}
+
+void Server::use_event_dispatcher(IO::EventDispatcher &ed)
+{
+ event_disp = &ed;
+ event_disp->add(listen_sock);
+}
+
+void Server::incoming_connection()
+{
+ Net::StreamSocket *sock = listen_sock.accept();
+ if(event_disp)
+ event_disp->add(*sock);
+ connections.push_back(new Connection(*this, sock));
+}
+
+void Server::train_added(Train &train)
+{
+ Locomotive &loco = train.get_locomotive();
+ train.signal_target_speed_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_speed_changed), sigc::ref(train)));
+ loco.signal_function_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_function_changed), sigc::ref(train)));
+ train.signal_status_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_status_changed), sigc::ref(train)));
+
+ TrainInfoPacket pkt;
+ pkt.address = loco.get_address();
+ pkt.loco_type = loco.get_type().get_article_number();
+ pkt.name = train.get_name();
+ send(pkt);
+}
+
+void Server::train_speed_changed(const Train &train, unsigned speed)
+{
+ TrainSpeedPacket pkt;
+ pkt.address = train.get_locomotive().get_address();
+ pkt.speed = speed;
+ pkt.reverse = train.get_locomotive().get_reverse();
+ send(pkt);
+}
+
+void Server::train_function_changed(const Train &train, unsigned, bool)
+{
+ TrainFunctionPacket pkt;
+ pkt.address = train.get_locomotive().get_address();
+ pkt.functions = train.get_locomotive().get_functions();
+ send(pkt);
+}
+
+void Server::train_status_changed(const Train &train, const string &status)
+{
+ TrainStatusPacket pkt;
+ pkt.address = train.get_locomotive().get_address();
+ pkt.status = status;
+ send(pkt);
+}
+
+template<typename P>
+void Server::send(const P &pkt)
+{
+ for(vector<Connection *>::const_iterator i=connections.begin(); i!=connections.end(); ++i)
+ if(!(*i)->stale)
+ (*i)->comm.send(pkt);
+}
+
+
+Server::Connection::Connection(Server &s, Net::StreamSocket *o):
+ server(s),
+ socket(o),
+ comm(*socket, server.proto, *this)
+{
+ socket->signal_end_of_file.connect(sigc::mem_fun(this, &Connection::end_of_file));
+ comm.signal_handshake_done.connect(sigc::mem_fun(this, &Connection::handshake_done));
+ comm.initiate_handshake();
+}
+
+Server::Connection::~Connection()
+{
+ delete socket;
+}
+
+void Server::Connection::handshake_done()
+{
+ const list<Train *> &trains = server.trfc_mgr.get_trains();
+ for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ {
+ Locomotive &loco = (*i)->get_locomotive();
+
+ {
+ TrainInfoPacket pkt;
+ pkt.address = loco.get_address();
+ pkt.loco_type = loco.get_type().get_article_number();
+ pkt.name = (*i)->get_name();
+ comm.send(pkt);
+ }
+ {
+ TrainSpeedPacket pkt;
+ pkt.address = loco.get_address();
+ pkt.speed = (*i)->get_target_speed();
+ pkt.reverse = loco.get_reverse();
+ comm.send(pkt);
+ }
+ {
+ TrainFunctionPacket pkt;
+ pkt.address = loco.get_address();
+ pkt.functions = loco.get_functions();
+ comm.send(pkt);
+ }
+ {
+ TrainStatusPacket pkt;
+ pkt.address = loco.get_address();
+ pkt.status = (*i)->get_status();
+ comm.send(pkt);
+ }
+ }
+}
+
+void Server::Connection::end_of_file()
+{
+ socket->close();
+ stale = true;
+}
+
+void Server::Connection::receive(const TrainSpeedPacket &pkt)
+{
+ Locomotive &loco = server.trfc_mgr.get_control().get_locomotive(pkt.address);
+ Train &train = server.trfc_mgr.get_train_by_locomotive(loco);
+ if(pkt.reverse!=loco.get_reverse())
+ train.set_reverse(pkt.reverse);
+ else
+ train.set_speed(pkt.speed);
+}
+
+void Server::Connection::receive(const TrainFunctionPacket &pkt)
+{
+ Locomotive &loco = server.trfc_mgr.get_control().get_locomotive(pkt.address);
+ for(unsigned i=0; i<9; ++i)
+ if(((pkt.functions^loco.get_functions())>>i)&1)
+ loco.set_function(i, (pkt.functions>>i)&1);
+}
+
+} // namespace Marklin