import 'dart:core'; import 'dart:convert'; import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart' show DateFormat; import 'package:flutter/services.dart'; class ScheduleService { static const Duration dayOffset = Duration(hours: 4); Future> getSchema() async { final source = await rootBundle .loadString('assets/schedule.json'); return jsonDecode(source); } Future?> getRule(DateTime day) async { final Map schema = await getSchema(); if(!schema.containsKey('rules')) return null; for(final Map rule in (schema['rules'] as List)) { final DateTime after = DateTime.parse(rule['after']); final DateTime? before = rule['before'] != null ? DateTime.parse(rule['before']) : null; if(day.isAfter(after) && (before == null || day.isBefore(before))) { return rule; } } return null; } Future?> getDataset(DateTime day) async { final Map? rule = await getRule(day); if(rule == null || !rule.containsKey('dataset')) return null; return (rule['dataset'] as Map).map((key, value) { return MapEntry(key, value.toString()); }); } String datetimeWeekdayToDatasetKey(int weekday) { switch(weekday){ case DateTime.monday: return 'monday'; case DateTime.tuesday: return 'tuesday'; case DateTime.wednesday:return 'wednesday'; case DateTime.thursday: return 'thursday'; case DateTime.friday: return 'friday'; case DateTime.saturday: return 'saturday'; case DateTime.sunday: return 'sunday'; } return 'monday'; } Future>?> getScheduleByKey(String key) async { final Map schema = await getSchema(); if(!schema.containsKey('schedules')) return null; if(!(schema['schedules'] as Map).containsKey(key)) return null; return (schema['schedules'][key] as List).map((element) { return element as Map; }).toList(); } Future>?> getScheduleByDate(DateTime datetime, {bool force = false}) async { final dateStart = datetime.copyWith(hour: 0, minute: 0, second: 0, microsecond: 0, millisecond: 0); final dateEnd = datetime.copyWith(hour: 23, minute: 59, second: 59, microsecond: 999, millisecond: 999); final datetimeStartWithOffset = dateStart.copyWith().add(dayOffset); final DateTime usedScheduleDatetime = datetime.isAfter(datetimeStartWithOffset) ? datetime : datetime.copyWith().subtract(const Duration(days: 1)); final Map? dataset = await getDataset(usedScheduleDatetime); if(dataset == null) return null; final String dayKey = DateFormat('y-M-d').format(usedScheduleDatetime); final String weekdayKey = datetimeWeekdayToDatasetKey(usedScheduleDatetime.weekday); String? scheduleKey; if(dataset.containsKey(dayKey)) { scheduleKey = dataset[dayKey]; } else if(dataset.containsKey(weekdayKey)) { scheduleKey = dataset[weekdayKey]; } if(scheduleKey == null) return null; List>? schedule = await getScheduleByKey(scheduleKey); if(schedule == null) return null; int durationOffset = 0; for (var i = 0; i < schedule.length; i++) { final scheduleItem = schedule[i]; final int duration = (scheduleItem['duration'] as int); DateTime start = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset)); DateTime end = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset + duration)); durationOffset += duration; if(!force) { if(i == 0) { final schedulePrevDay = await getScheduleByDate(datetime.copyWith().subtract(const Duration(days: 1)), force: true); final lastScheduleElement = schedulePrevDay?.last; if(lastScheduleElement != null && (lastScheduleElement['direction'] as String) == (scheduleItem['direction'] as String)) { start = start.subtract(Duration(minutes: lastScheduleElement['duration'] as int)); } } if(i == (schedule.length -1)) { final scheduleNextDay = await getScheduleByDate(datetime.copyWith().add(const Duration(days: 1)), force: true); final firstScheduleElement = scheduleNextDay?.first; if(firstScheduleElement != null && (firstScheduleElement['direction'] as String) == (scheduleItem['direction'] as String)) { end = end.add(Duration(minutes: firstScheduleElement['duration'] as int)); } } } schedule[i]['start'] = start; schedule[i]['show_start_date'] = !(start.isAfter(dateStart) && start.isBefore(dateEnd)); schedule[i]['end'] = end; schedule[i]['show_end_date'] = !(end.isAfter(dateStart) && end.isBefore(dateEnd)); } return schedule; } Future?> getCurrentStatus() async { final DateTime now = DateTime.now(); final List>? schedule = await getScheduleByDate(now); if(schedule == null) return null; for(var i = 0; i < schedule.length; i++) { final start = schedule[i]['start']; final end = schedule[i]['end']; if(now.isAfter(start) && now.isBefore(end)) { return schedule[i]; } } return null; } static String formatDirection(String direction) { switch(direction) { case 'in_city': return 'В город'; case 'out_city': return 'Из города'; default: return 'Переключение'; } } static IconData getIconByDirection(String? direction) { switch(direction) { case 'in_city': return Icons.input_outlined; case 'out_city': return Icons.output_outlined; default: return Icons.change_circle_outlined; } } }