import 'dart:math'; import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/constructs/construct_level_enum.dart'; /// One lemma and a list of construct uses for that lemma class ConstructUses { final List uses; final ConstructTypeEnum constructType; final String lemma; String? _category; DateTime? _lastUsed; ConstructUses({ required this.uses, required this.constructType, required this.lemma, required category, }) : _category = category; // Total points for all uses of this lemma int get points { return min( uses.fold( 0, (total, use) => total + use.xp, ), AnalyticsConstants.xpForFlower, ); } DateTime? get lastUsed { if (_lastUsed != null) return _lastUsed; final lastUse = uses.fold(null, (DateTime? last, use) { if (last == null) return use.timeStamp; return use.timeStamp.isAfter(last) ? use.timeStamp : last; }); return _lastUsed = lastUse; } void setLastUsed(DateTime time) { if (_lastUsed == null || time.isAfter(_lastUsed!)) { _lastUsed = time; } } String get category { if (_category == null || _category!.isEmpty) return "other"; return _category!.toLowerCase(); } bool get hasCorrectUse => uses.any((use) => use.xp > 0); bool get hasIncorrectUse => uses.any((use) => use.xp < 0); ConstructIdentifier get id => ConstructIdentifier( lemma: lemma, type: constructType, category: category, ); Map toJson() { final json = { 'construct_id': id.toJson(), 'xp': points, 'last_used': lastUsed?.toIso8601String(), 'uses': uses.map((e) => e.toJson()).toList(), }; return json; } factory ConstructUses.fromJson(Map json) { final constructId = ConstructIdentifier.fromJson( Map.from(json['construct_id']), ); List usesJson = []; if (json['uses'] is List) { usesJson = List.from(json['uses']); } final uses = usesJson .map((e) => OneConstructUse.fromJson(Map.from(e))) .toList(); return ConstructUses( uses: uses, constructType: constructId.type, lemma: constructId.lemma, category: constructId.category, ); } /// Get the lemma category, based on points ConstructLevelEnum get lemmaCategory { if (points < AnalyticsConstants.xpForGreens) { return ConstructLevelEnum.seeds; } else if (points >= AnalyticsConstants.xpForFlower) { return ConstructLevelEnum.flowers; } return ConstructLevelEnum.greens; } String get xpEmoji { if (points < 30) { // bean emoji return AnalyticsConstants.emojiForSeed; } else if (points < 100) { // sprout emoji return AnalyticsConstants.emojiForGreen; } else { // flower emoji return AnalyticsConstants.emojiForFlower; } } ConstructLevelEnum get constructLevel => switch (points) { < AnalyticsConstants.xpForGreens => ConstructLevelEnum.seeds, < AnalyticsConstants.xpForFlower => ConstructLevelEnum.greens, _ => ConstructLevelEnum.flowers, }; void merge(ConstructUses other) { if (other.lemma.toLowerCase() != lemma.toLowerCase() || other.constructType != constructType) { throw ArgumentError( 'Cannot merge ConstructUses with different lemmas or types', ); } uses.addAll(other.uses); if (other.lastUsed != null) { setLastUsed(other.lastUsed!); } if (category == 'other' && other.category != 'other') { _category = other.category; } } ConstructUses copyWith({ List? uses, ConstructTypeEnum? constructType, String? lemma, String? category, }) { return ConstructUses( uses: uses ?? this.uses, constructType: constructType ?? this.constructType, lemma: lemma ?? this.lemma, category: category ?? _category, ); } }