+#include "departures.h"
+#include "layout.h"
+#include "timetable.h"
+#include "train.h"
+#include "trainrouter.h"
+#include "zone.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+bool departure_time_order(const R2C2::Departures::Departure &d1, const R2C2::Departures::Departure &d2)
+{
+ return d1.time<d2.time;
+}
+
+}
+
+
+namespace R2C2 {
+
+Departures::Departures(const Layout &layout, const string &group):
+ zones(layout.get_zones(group))
+{
+ const map<unsigned, Train *> &trains = layout.get_trains();
+ for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ {
+ Timetable *timetable = i->second->get_ai_of_type<Timetable>();
+ (void)timetable;
+ TrainRouter *router = i->second->get_ai_of_type<TrainRouter>();
+ router->signal_route_changed.connect(sigc::bind(sigc::mem_fun(this, &Departures::train_route_changed), sigc::ref(*i->second)));
+ router->signal_departed.connect(sigc::bind(sigc::mem_fun(this, &Departures::train_departed), sigc::ref(*i->second)));
+
+ update_departures(*i->second);
+ }
+}
+
+void Departures::train_route_changed(const Route *, Train &train)
+{
+ update_departures(train);
+}
+
+void Departures::train_departed(Train &train)
+{
+ update_departures(train);
+}
+
+void Departures::update_departures(Train &train)
+{
+ const Clock &clock = train.get_layout().get_clock();
+ Time::TimeDelta t = clock.get_current_time();
+ float rate = clock.get_rate();
+
+ list<Departure> collected_departures;
+
+ Timetable *timetable = train.get_ai_of_type<Timetable>();
+ unsigned tt_length = timetable->get_length();
+ for(unsigned i=0; i<tt_length; ++i)
+ {
+ const Timetable::Row &row = timetable->get_row(i);
+ if(row.type==Timetable::DEPART)
+ if(Zone *zone = get_attached_zone(*row.target))
+ {
+ Time::TimeDelta dt = row.time;
+ while(dt<t)
+ dt += Time::day;
+ collected_departures.push_back(Departure(*zone, train, dt));
+ }
+ }
+
+ collected_departures.sort(departure_time_order);
+
+ TrainRouter *router = train.get_ai_of_type<TrainRouter>();
+ Time::TimeDelta departure_delay = router->get_departure_delay();
+ if(departure_delay)
+ if(Zone *zone = get_attached_zone(*train.get_block_allocator().last()))
+ {
+ Departure dep(*zone, train, t+departure_delay*rate);
+
+ list<Departure>::iterator i = collected_departures.begin();
+ bool duplicate = false;
+ for(; (i!=collected_departures.end() && !duplicate); ++i)
+ {
+ if(i->time>dep.time)
+ {
+ duplicate = (i->time<dep.time+Time::min);
+ break;
+ }
+ else
+ duplicate = (i->time+Time::min>dep.time);
+ }
+
+ if(!duplicate)
+ collected_departures.push_back(dep);
+ }
+
+ merge_departures(train, collected_departures);
+}
+
+void Departures::merge_departures(Train &train, const std::list<Departure> &collected_departures)
+{
+ list<Departure>::iterator i = departures.begin();
+ list<Departure>::const_iterator j = collected_departures.begin();
+ while(i!=departures.end() && j!=collected_departures.end())
+ {
+ if(i->time>j->time)
+ {
+ list<Departure>::iterator k = departures.insert(i, *j);
+ signal_departure_added.emit(*k);
+ ++j;
+ }
+ else if(i->train==&train)
+ {
+ if(i->time==j->time)
+ {
+ ++i;
+ ++j;
+ }
+ else
+ {
+ signal_departure_removed.emit(*i);
+ departures.erase(i++);
+ }
+ }
+ else
+ ++i;
+ }
+
+ for(; j!=collected_departures.end(); ++j)
+ {
+ departures.push_back(*j);
+ signal_departure_added.emit(departures.back());
+ }
+
+ for(; i!=departures.end(); )
+ {
+ if(i->train==&train)
+ {
+ signal_departure_removed.emit(*i);
+ departures.erase(i++);
+ }
+ else
+ ++i;
+ }
+}
+
+Zone *Departures::get_attached_zone(const TrackChain &chain) const
+{
+ const TrackChain::TrackSet &tracks = chain.get_tracks();
+ for(vector<Zone *>::const_iterator i=zones.begin(); i!=zones.end(); ++i)
+ {
+ for(TrackChain::TrackSet::const_iterator j=tracks.begin(); j!=tracks.end(); ++j)
+ if((*i)->has_track(**j))
+ return *i;
+ }
+
+ return 0;
+}
+
+
+Departures::Departure::Departure(Zone &z, Train &t, const Time::TimeDelta &m):
+ zone(&z),
+ train(&t),
+ time(m)
+{ }
+
+} // namespace R2C2