+template<typename T>
+Timetable::Row::Row(RowType t, const T &p):
+ type(t)
+{
+ params.push_back(p);
+}
+
+template<typename T>
+const T &Timetable::Row::get_param(unsigned i) const
+{
+ if(i>=params.size())
+ throw InvalidParameterValue("Parameter index out of range");
+ return params[i].value<T>();
+}
+
+string Timetable::Row::str() const
+{
+ switch(type)
+ {
+ case GOTO:
+ return "set route to "+get_param<string>(0);
+ case TRAVEL:
+ return "travel to "+get_param<string>(0);
+ case WAIT_TIME:
+ return format("wait for %d seconds", get_param<unsigned>(0));
+ case WAIT_TRAIN:
+ return format("wait for train %d at %s", get_param<unsigned>(0), get_param<string>(1));
+ case ARRIVE:
+ return "travel until arrival";
+ case SPEED:
+ return format("set speed %d km/h", get_param<unsigned>(0));
+ case REVERSE:
+ return "reverse";
+ case ROUTE:
+ return "set route "+get_param<string>(0);
+ default:
+ return "invalid row";
+ }
+}
+
+DataFile::Statement Timetable::Row::save() const
+{
+ switch(type)
+ {
+ case GOTO:
+ return DataFile::Statement("goto"), get_param<string>(0);
+ case TRAVEL:
+ return DataFile::Statement("travel"), get_param<string>(0);
+ case WAIT_TIME:
+ return DataFile::Statement("wait"), get_param<unsigned>(0);
+ case WAIT_TRAIN:
+ return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<string>(1);
+ case ARRIVE:
+ return DataFile::Statement("arrive");
+ case SPEED:
+ return DataFile::Statement("speed"), get_param<unsigned>(0);
+ case REVERSE:
+ return DataFile::Statement("reverse");
+ case ROUTE:
+ return DataFile::Statement("route"), get_param<string>(0);
+ default:
+ return DataFile::Statement();
+ }
+}
+
+Timetable::Row Timetable::Row::parse(const string &s)
+{
+ if(!s.compare(0, 7, "travel "))
+ {
+ if(!s.compare(7, 3, "to "))
+ return Row(TRAVEL, s.substr(10));
+ else if(!s.compare(7, string::npos, "until arrival"))
+ return Row(ARRIVE);
+ }
+ else if(!s.compare(0, 9, "wait for "))
+ {
+ if(isdigit(s[9]))
+ {
+ unsigned nondigit = 10;
+ while(nondigit<s.size() && isdigit(s[nondigit]))
+ ++nondigit;
+ return Row(WAIT_TIME, lexical_cast<unsigned>(s.substr(9, nondigit-9)));
+ }
+ else if(!s.compare(9, 6, "train "))
+ {
+ string::size_type at = s.find(" at ", 15);
+ if(at!=string::npos)
+ {
+ Row row(WAIT_TRAIN, lexical_cast<unsigned>(s.substr(15, at-15)));
+ row.params.push_back(s.substr(at+4));
+ return row;
+ }
+ }
+ }
+ else if(!s.compare(0, 10, "set speed "))
+ {
+ unsigned nondigit = 11;
+ while(nondigit<s.size() && (isdigit(s[nondigit]) || s[nondigit]=='-'))
+ ++nondigit;
+ return Row(SPEED, lexical_cast<unsigned>(s.substr(10, nondigit-10)));
+ }
+ else if(s=="reverse")
+ return Row(REVERSE);
+ else if(!s.compare(0, 10, "set route "))
+ {
+ if(!s.compare(10, 3, "to "))
+ return Row(GOTO, s.substr(13));
+ return Row(ROUTE, s.substr(10));
+ }
+
+ throw InvalidParameterValue("Invalid row");
+}