import "dart:io";

import "package:csv/csv.dart";

import "utils.dart";

class BcStopParser extends Parser<Stop> {
  static final tripsFile = File(bcDataDir / "stop_times.txt");
  static final routesFile = File(bcDataDir / "trips.txt");
  static final stopsFile = File(bcDataDir / "stops.txt");
  static final routeNamesFile = File(bcDataDir / "routes.txt");

  static final converter = CsvCodec(shouldParseNumbers: false).decoder;

  Future<Map<TripID, List<StopID>>> getTrips() async {
    final result = <TripID, List<StopID>>{};
    for (final row in await readCsv(tripsFile)) {
      result.addToList(TripID(row[0]), StopID(row[3]));
    }
    return result;
  }

  Future<Map<TripID, RouteID>> getRoutes() async => {
    for (final row in await readCsv(routesFile))
      TripID(row[2]): RouteID(Provider.bc, row[0]),
  };

  Future<Map<RouteID, String>> getRouteNames() async => {
    for (final row in await readCsv(routeNamesFile))
      if (row[2].isNotEmpty)
        RouteID(Provider.bc, row[0]): "${row[2]}) ${row[3]}",
  };

  Future<Map<StopID, Stop>> getStops() async {
    final result = <StopID, Stop>{};
    for (final row in await readCsv(stopsFile)) {
      final stopID = StopID(row[0]);
      final name = row[2];
      final description = row[3];
      final latitude = double.parse(row[4]);
      final longitude = double.parse(row[5]);
      final stop = Stop(
        id: stopID,
        name: name,
        description: description,
        coordinates: (lat: latitude, long: longitude),
        provider: Provider.bc,
      );
      result[stopID] = stop;
    }
    return result;
  }

  void findRoutesForStops({
    required Iterable<Stop> stops,
    required Map<TripID, List<StopID>> trips,
    required Map<TripID, RouteID> routes,
    required Map<RouteID, String> routeNames,
  }) {
    for (final stop in stops) {
      for (final (tripID, trip) in trips.records) {
        if (!trip.contains(stop.id)) continue;
        final routeID = routes[tripID]!;
        final routeName = routeNames[routeID];
        if (routeName == null) continue;  // old route
        stop.addRoute(routeID, routeName);
      }
    }
  }

  @override
  Future<Iterable<Stop>> parse() async {
    final trips = await getTrips();
    final routes = await getRoutes();
    final stops = await getStops();
    final routeNames = await getRouteNames();
    findRoutesForStops(stops: stops.values, trips: trips, routes: routes, routeNames: routeNames);
    return stops.values;
  }
}
