// Represents a collection of landmarks that represent a journey
// Different instances of a Trip can be saved and loaded by the user

import 'dart:collection';
import 'dart:convert';
import 'dart:developer';

import 'package:anyway/structs/landmark.dart';
import 'package:flutter/foundation.dart';
import 'package:geocoding/geocoding.dart';
import 'package:shared_preferences/shared_preferences.dart';

class Trip with ChangeNotifier {
  String uuid;
  Duration totalTime;
  LinkedList<Landmark> landmarks;
  // could be empty as well
  String? errorDescription;

  Future<String> get cityName async {
    List<double>? location = landmarks.firstOrNull?.location; 
    if (GeocodingPlatform.instance == null) {
      return '$location';
    } else if (location == null) {
      return 'Unknown';
    } else{
      List<Placemark> placemarks = await placemarkFromCoordinates(location[0], location[1]);
      return placemarks.first.locality ?? 'Unknown';
    }
  }

  Future<int> landmarkPosition (Landmark landmark) async {
    int i = 0;
    for (Landmark l in landmarks) {
      if (l.uuid == landmark.uuid) {
        return i;
      } else if (l.type != typeStart && l.type != typeFinish) {
      i++;
      }
    }
    return -1;
  }


  Trip({
    this.uuid = 'pending',
    this.totalTime = Duration.zero,
    LinkedList<Landmark>? landmarks
    // a trip can be created with no landmarks, but the list should be initialized anyway
  }) : landmarks = landmarks ?? LinkedList<Landmark>();
  

  factory Trip.fromJson(Map<String, dynamic> json) {
    Trip trip = Trip(
      uuid: json['uuid'],
      totalTime: Duration(minutes: json['total_time']),
    );

    return trip;
  }

  void loadFromJson(Map<String, dynamic> json) {
    uuid = json['uuid'];
    totalTime = Duration(minutes: json['total_time']);
    notifyListeners();
  }

  void addLandmark(Landmark landmark) {
    landmarks.add(landmark);
    notifyListeners();
  }

  void updateUUID(String newUUID) {
    uuid = newUUID;
    notifyListeners();
  }

  void removeLandmark (Landmark landmark) async {
    Landmark? previous = landmark.previous;
    Landmark? next = landmark.next;
    landmarks.remove(landmark);
    // removing the landmark means we need to recompute the time between the two adjoined landmarks
    if (previous != null && next != null) {
      // previous.next = next happens automatically since we are using a LinkedList
      this.totalTime -= previous.tripTime ?? Duration.zero;
      previous.tripTime = null;
      // TODO
    }
    this.totalTime -= landmark.tripTime ?? Duration.zero;

    notifyListeners();
  }

  void updateError(String error) {
    errorDescription = error;
    notifyListeners();
  }

  void notifyUpdate(){
    notifyListeners();
  }

  factory Trip.fromPrefs(SharedPreferences prefs, String uuid) {
    String? content = prefs.getString('trip_$uuid');
    Map<String, dynamic> json = jsonDecode(content!);
    Trip trip = Trip.fromJson(json);
    String? firstUUID = json['first_landmark_uuid'];
    log('Loading trip $uuid with first landmark $firstUUID');
    LinkedList<Landmark> landmarks = readLandmarks(prefs, firstUUID);
    trip.landmarks = landmarks;
    return trip;
  }


  Map<String, dynamic> toJson() => {
    'uuid': uuid,
    'total_time': totalTime.inMinutes,
    'first_landmark_uuid': landmarks.first.uuid
  };


  void toPrefs(SharedPreferences prefs){
    Map<String, dynamic> json = toJson();
    log('Saving trip $uuid : $json');
    prefs.setString('trip_$uuid', jsonEncode(json));
    for (Landmark landmark in landmarks) {
      landmarkToPrefs(prefs, landmark, landmark.next);
    }
  }
}


// Helper
LinkedList<Landmark> readLandmarks(SharedPreferences prefs, String? firstUUID) {
  LinkedList<Landmark> landmarks = LinkedList<Landmark>();
  while (firstUUID != null) {
    var (head, nextUUID) = getLandmarkFromPrefs(prefs, firstUUID);
    landmarks.add(head);
    firstUUID = nextUUID;
  }
  return landmarks;
}