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

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

LandmarkType typeSightseeing = LandmarkType(name: 'sightseeing');
LandmarkType typeNature = LandmarkType(name: 'nature');
LandmarkType typeShopping = LandmarkType(name: 'shopping');
// LandmarkType museum = LandmarkType(name: 'Museum');
// LandmarkType restaurant = LandmarkType(name: 'Restaurant');
LandmarkType typeStart = LandmarkType(name: 'start');
LandmarkType typeFinish = LandmarkType(name: 'finish');


final class Landmark extends LinkedListEntry<Landmark>{
  // A linked node of a list of Landmarks
  final String uuid;
  final String name;
  final List<double> location;
  final LandmarkType type;
  final bool? isSecondary;

  // description to be shown in the overview
  final String? nameEN;
  final String? websiteURL;
  String? imageURL; // not final because it can be patched
  final String? description;
  final Duration? duration;
  bool visited;

  // Next node is implicitly available through the LinkedListEntry mixin
  // final Landmark? next;
  Duration? tripTime;
  // the trip time depends on the next landmark, so it is not final


  Landmark({
    required this.uuid,
    required this.name,
    required this.location,
    required this.type,
    this.isSecondary,

    this.nameEN,
    this.websiteURL,
    this.imageURL,
    this.description,
    this.duration,
    this.visited = false,

    // this.next,
    this.tripTime,
  });


  factory Landmark.fromJson(Map<String, dynamic> json) {
    if (json
      case { // automatically match all the non-optionals and cast them to the right type
        'uuid': String uuid,
        'name': String name,
        'location': List<dynamic> location,
        'type': String type,
      }) {
      // refine the parsing on a few fields
      List<double> locationFixed = List<double>.from(location);
      // parse the rest separately, they could be missing
      LandmarkType typeFixed = LandmarkType(name: type);
      final isSecondary = json['is_secondary'] as bool?;
      final nameEN = json['name_en'] as String?;
      final websiteURL = json['website_url'] as String?;
      final imageURL = json['image_url'] as String?;
      final description = json['description'] as String?;
      var duration = Duration(minutes: json['duration']);
      final visited = json['visited'] ?? false;
      var tripTime = Duration(minutes: json['time_to_reach_next'] ?? 0) as Duration?;

      return Landmark(
        uuid: uuid,
        name: name,
        location: locationFixed,
        type: typeFixed,
        isSecondary: isSecondary,
        nameEN: nameEN,
        websiteURL: websiteURL,
        imageURL: imageURL,
        description: description,
        duration: duration,
        visited: visited,
        tripTime: tripTime
      );
    } else {
      throw FormatException('Invalid JSON: $json');
    }
  }


  @override
  bool operator ==(Object other) {
    return other is Landmark && uuid == other.uuid;
  }


  Map<String, dynamic> toJson() => {
    'uuid': uuid,
    'name': name,
    'location': location,
    'type': type.name,
    'is_secondary': isSecondary,
    'name_en': nameEN,
    'website_url': websiteURL,
    'image_url': imageURL,
    'description': description,
    'duration': duration?.inMinutes,
    'visited': visited,
    'trip_time': tripTime?.inMinutes,
  };
}


class LandmarkType {
  final String name;
  // final String description;
  Icon icon;
  
  LandmarkType({required this.name, this.icon = const Icon(Icons.location_on)}) {
    switch (name) {
      case 'sightseeing':
        icon = const Icon(Icons.castle);
        break;
      case 'nature':
        icon = const Icon(Icons.eco);
        break;
      case 'shopping':
        icon = const Icon(Icons.shopping_cart);
        break;
      case 'start':
        icon = const Icon(Icons.play_arrow);
        break;
      case 'finish':
        icon = const Icon(Icons.flag);
        break;
      default:
        icon = const Icon(Icons.location_on);
    }
  }
  @override
  bool operator ==(Object other) {
    if (other is LandmarkType) {
      return name == other.name;
    } else {
      return false;
    }
  }
}


// Helpers
// Handling the landmarks requires a little bit of special care because the linked list is not directly representable in json
(Landmark, String?) getLandmarkFromPrefs(SharedPreferences prefs, String uuid) {
  String? content = prefs.getString('landmark_$uuid');
  Map<String, dynamic> json = jsonDecode(content!);
  String? nextUUID = json['next_uuid'];
  return (Landmark.fromJson(json), nextUUID);
}


void landmarkToPrefs(SharedPreferences prefs, Landmark current, Landmark? next) {
  Map<String, dynamic> json = current.toJson();
  json['next_uuid'] = next?.uuid;
  prefs.setString('landmark_${current.uuid}', jsonEncode(json));
}
