import 'dart:developer'; import 'dart:io'; import 'package:calorimeter/food_entry/food_entry_bloc.dart'; import 'package:path_provider/path_provider.dart'; import 'package:universal_platform/universal_platform.dart'; class FoodStorage { static late FoodStorage _instance; late String path; final Map _foodLookupDatabase = {}; FoodStorage._create(); static Future create() async { var storage = FoodStorage._create(); Directory dir = Directory(''); if (UniversalPlatform.isDesktop) { dir = await getApplicationCacheDirectory(); } else if (UniversalPlatform.isAndroid) { dir = await getApplicationDocumentsDirectory(); } storage.path = dir.path; _instance = storage; return _instance; } static FoodStorage getInstance() => _instance; Future> getEntriesForDate(DateTime date) async { List entries = []; var filePath = '$path/${date.year}/${date.month}/${date.day}'; var file = File(filePath); var exists = await file.exists(); if (!exists) return []; var lines = await file.readAsLines(); for (var line in lines) { var fields = line.splitWithIgnore(',', ignoreIn: '"'); int mass = 0; int kcalPerMass = 0; try { mass = int.parse(fields[2]); } catch (e) { mass = double.parse(fields[2]).toInt(); } try { kcalPerMass = int.parse(fields[3]); } catch (e) { kcalPerMass = double.parse(fields[3]).toInt(); } var entry = FoodEntryState( name: fields[1].replaceAll('"', ""), mass: mass, kcalPer100: kcalPerMass, waitingForNetwork: false, isSelected: false, ); entries.add(entry); } return entries; } Future writeEntriesForDate( DateTime date, List foodEntries) async { var filePath = '$path/${date.year}/${date.month}/${date.day}'; var file = File(filePath); var exists = await file.exists(); if (!exists) { await file.create(recursive: true); } String fullString = ''; for (var entry in foodEntries) { fullString += '${entry.toString()}\n'; } await file.writeAsString(fullString); } Future updateLimit(double limit) async { var filePath = '$path/limit'; var file = File(filePath); var exists = await file.exists(); if (!exists) { await file.create(); } await file.writeAsString(limit.toString()); } Future readLimit() async { var filePath = '$path/limit'; var file = File(filePath); var exists = await file.exists(); if (!exists) { return 2000; } var line = await file.readAsLines(); double limit; try { limit = double.parse(line[0]); } catch (e) { limit = 2000; } return limit; } Future readBrightness() async { var filePath = '$path/brightness'; var file = File(filePath); var exists = await file.exists(); if (!exists) { return 'dark'; } var line = await file.readAsLines(); if (line.isEmpty || (line[0] != 'dark' && line[0] != 'light')) { return 'dark'; } return line[0]; } Future writeBrightness(String brightness) async { var filePath = '$path/brightness'; var file = File(filePath); var exists = await file.exists(); if (!exists) { file.create(); } await file.writeAsString(brightness); } Future buildFoodLookupDatabase() async { // get a list of dates of the last 365 days var dates = List.generate(365, (idx) { var durationToPast = Duration(days: idx); return DateTime.now().subtract(durationToPast); }); for (var date in dates.reversed) { addFoodEntryToLookupDatabaseFor(date); } } Future addFoodEntryToLookupDatabaseFor(DateTime date) async { var entriesForDate = await getEntriesForDate(date); for (var entry in entriesForDate) { _foodLookupDatabase[entry.name] = entry.kcalPer100; log("Added entry: ${entry.name}/${entry.kcalPer100}"); } } void addFoodEntryToLookupDatabase(FoodEntryState entry) { _foodLookupDatabase[entry.name] = entry.kcalPer100; log("Added entry: ${entry.name}/${entry.kcalPer100}"); } Map get getFoodEntryLookupDatabase => _foodLookupDatabase; } extension SplitWithIgnore on String { List splitWithIgnore(String delimiter, {String? ignoreIn}) { List parts = []; if (ignoreIn == null) { return split(delimiter); } int index = -1; int indexCharAfterDelimiter = 0; bool inIgnore = false; for (var rune in runes) { var char = String.fromCharCode(rune); index += 1; if (char == ignoreIn) { inIgnore = !inIgnore; continue; } if (inIgnore) { continue; } if (char == delimiter) { parts.add(substring(indexCharAfterDelimiter, index)); indexCharAfterDelimiter = index + 1; } if (index + 1 == length) { parts.add(substring(indexCharAfterDelimiter, length)); } } return parts; } }