

import 'package:freezed_annotation/freezed_annotation.dart';

part 'library.freezed.dart';
part 'library.g.dart';

@freezed
abstract class EmbyRawUserData with _$EmbyRawUserData {
  const factory EmbyRawUserData({
    @JsonKey(fromJson: _intOrNull, includeIfNull: false)
    int? PlaybackPositionTicks,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? PlayCount,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? UnplayedItemCount,
    @JsonKey(includeIfNull: false) bool? IsFavorite,
    @JsonKey(includeIfNull: false) bool? Played,
    @JsonKey(includeFromJson: false, includeToJson: false)
    @Default(<String, dynamic>{})
    Map<String, dynamic> extra,
  }) = _EmbyRawUserData;

  factory EmbyRawUserData.fromJson(Map<String, dynamic> json) =>
      _$EmbyRawUserDataFromJson(json).copyWith(extra: json);
}

@freezed
abstract class EmbyRawItem with _$EmbyRawItem {
  const factory EmbyRawItem({
    required String Id,
    required String Name,
    @JsonKey(includeIfNull: false) String? Type,
    @JsonKey(includeIfNull: false) String? Overview,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? ProductionYear,
    @JsonKey(fromJson: _doubleOrNull, includeIfNull: false)
    double? CommunityRating,
    @JsonKey(includeIfNull: false) String? PremiereDate,
    @JsonKey(includeIfNull: false) String? SeriesName,
    @JsonKey(includeIfNull: false) String? SeriesId,
    @JsonKey(includeIfNull: false) String? SeasonId,
    @JsonKey(includeIfNull: false) String? SeasonName,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? IndexNumber,
    @JsonKey(includeIfNull: false) String? ParentId,
    @JsonKey(includeIfNull: false) String? PrimaryImageTag,
    @JsonKey(fromJson: _stringMapOrNull, includeIfNull: false)
    Map<String, String>? ImageTags,
    @JsonKey(fromJson: _stringListOrNull, includeIfNull: false)
    List<String>? BackdropImageTags,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? RunTimeTicks,
    @JsonKey(includeIfNull: false) EmbyRawUserData? UserData,
    @JsonKey(includeFromJson: false, includeToJson: false)
    @Default(<String, dynamic>{})
    Map<String, dynamic> extra,
  }) = _EmbyRawItem;

  factory EmbyRawItem.fromJson(Map<String, dynamic> json) =>
      _$EmbyRawItemFromJson(json).copyWith(extra: json);
}

@freezed
abstract class EmbyRawSeason with _$EmbyRawSeason {
  const factory EmbyRawSeason({
    required String Id,
    required String Name,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? IndexNumber,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? ProductionYear,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? ChildCount,
    @JsonKey(includeIfNull: false) String? PrimaryImageTag,
    @JsonKey(fromJson: _stringMapOrNull, includeIfNull: false)
    Map<String, String>? ImageTags,
  }) = _EmbyRawSeason;

  factory EmbyRawSeason.fromJson(Map<String, dynamic> json) =>
      _$EmbyRawSeasonFromJson(json);
}

@freezed
abstract class EmbyRawLibrary with _$EmbyRawLibrary {
  const factory EmbyRawLibrary({
    required String Id,
    required String Name,
    @JsonKey(includeIfNull: false) String? CollectionType,
    @JsonKey(fromJson: _stringMapOrNull, includeIfNull: false)
    Map<String, String>? ImageTags,
  }) = _EmbyRawLibrary;

  factory EmbyRawLibrary.fromJson(Map<String, dynamic> json) =>
      _$EmbyRawLibraryFromJson(json);
}

@freezed
abstract class EmbyRawLibraries with _$EmbyRawLibraries {
  const factory EmbyRawLibraries({required List<EmbyRawLibrary> Items}) =
      _EmbyRawLibraries;

  factory EmbyRawLibraries.fromJson(Map<String, dynamic> json) =>
      _$EmbyRawLibrariesFromJson(json);
}

@freezed
abstract class EmbyRawMediaStream with _$EmbyRawMediaStream {
  const factory EmbyRawMediaStream({
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? Index,
    @JsonKey(includeIfNull: false) String? Type,
    @JsonKey(includeIfNull: false) String? Language,
    @JsonKey(includeIfNull: false) String? Codec,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? Channels,
    @JsonKey(includeIfNull: false) String? DisplayTitle,
    @JsonKey(includeIfNull: false) String? Title,
    @JsonKey(includeIfNull: false) bool? IsDefault,
    @JsonKey(includeIfNull: false) bool? IsForced,
    @JsonKey(includeIfNull: false) bool? IsExternal,
    @JsonKey(includeIfNull: false) String? DeliveryMethod,
    @JsonKey(includeIfNull: false) String? DeliveryUrl,
    @JsonKey(includeFromJson: false, includeToJson: false)
    @Default(<String, dynamic>{})
    Map<String, dynamic> extra,
  }) = _EmbyRawMediaStream;

  factory EmbyRawMediaStream.fromJson(Map<String, dynamic> json) =>
      _$EmbyRawMediaStreamFromJson(json).copyWith(extra: json);
}

@freezed
abstract class EmbyRawMediaSource with _$EmbyRawMediaSource {
  const factory EmbyRawMediaSource({
    @JsonKey(includeIfNull: false) String? Id,
    @JsonKey(includeIfNull: false) String? Name,
    @JsonKey(includeIfNull: false) String? Container,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? Bitrate,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? Size,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? Width,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false) int? Height,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false)
    int? DefaultSubtitleStreamIndex,
    @JsonKey(fromJson: _intOrNull, includeIfNull: false)
    int? DefaultAudioStreamIndex,
    @JsonKey(includeIfNull: false) List<EmbyRawMediaStream>? MediaStreams,
  }) = _EmbyRawMediaSource;

  factory EmbyRawMediaSource.fromJson(Map<String, dynamic> json) =>
      _$EmbyRawMediaSourceFromJson(json);
}

@freezed
abstract class LibraryLatestReq with _$LibraryLatestReq {
  const LibraryLatestReq._();
  const factory LibraryLatestReq({required String parentId, int? limit}) =
      _LibraryLatestReq;

  Map<String, dynamic> toJson() => {
    'parentId': parentId,
    if (limit != null) 'limit': limit,
  };
}

@freezed
abstract class LibraryLatestRes with _$LibraryLatestRes {
  const factory LibraryLatestRes({
    required String parentId,
    required List<EmbyRawItem> items,
    @Default(0) int totalCount,
  }) = _LibraryLatestRes;

  factory LibraryLatestRes.fromJson(Map<String, dynamic> json) =>
      _$LibraryLatestResFromJson(json);
}

@freezed
abstract class LibraryResumeRes with _$LibraryResumeRes {
  const factory LibraryResumeRes({required List<EmbyRawItem> Items}) =
      _LibraryResumeRes;

  factory LibraryResumeRes.fromJson(Map<String, dynamic> json) =>
      _$LibraryResumeResFromJson(json);
}

typedef LibraryViewsRes = EmbyRawLibraries;

@freezed
abstract class MediaDetailReq with _$MediaDetailReq {
  const MediaDetailReq._();
  const factory MediaDetailReq({required String itemId}) = _MediaDetailReq;

  Map<String, dynamic> toJson() => {'itemId': itemId};
}

@freezed
abstract class EmbyRawMediaDetailSeriesExtra
    with _$EmbyRawMediaDetailSeriesExtra {
  const factory EmbyRawMediaDetailSeriesExtra({
    required List<EmbyRawItem> nextUp,
    required List<EmbyRawSeason> seasons,
  }) = _EmbyRawMediaDetailSeriesExtra;
}

@freezed
abstract class EmbyRawMediaDetailSeriesContext
    with _$EmbyRawMediaDetailSeriesContext {
  const factory EmbyRawMediaDetailSeriesContext({
    required EmbyRawItem seriesBase,
    required EmbyRawMediaDetailSeriesExtra seriesExtra,
  }) = _EmbyRawMediaDetailSeriesContext;
}

@freezed
abstract class MediaDetailRes with _$MediaDetailRes {
  const factory MediaDetailRes({
    required EmbyRawItem base,
    EmbyRawMediaDetailSeriesExtra? seriesExtra,
    EmbyRawMediaDetailSeriesContext? seriesContext,
    List<EmbyRawItem>? similarItems,
    List<EmbyRawItem>? boxSetItems,
  }) = _MediaDetailRes;
}

@freezed
abstract class FavoriteSetReq with _$FavoriteSetReq {
  const FavoriteSetReq._();
  const factory FavoriteSetReq({
    required String itemId,
    required bool isFavorite,
  }) = _FavoriteSetReq;

  Map<String, dynamic> toJson() => {'itemId': itemId, 'isFavorite': isFavorite};
}

@freezed
abstract class FavoriteSetRes with _$FavoriteSetRes {
  const factory FavoriteSetRes({
    required String itemId,
    required bool isFavorite,
  }) = _FavoriteSetRes;

  factory FavoriteSetRes.fromJson(Map<String, dynamic> json) =>
      _$FavoriteSetResFromJson(json);
}

@freezed
abstract class PlayedSetReq with _$PlayedSetReq {
  const PlayedSetReq._();
  const factory PlayedSetReq({required String itemId, required bool played}) =
      _PlayedSetReq;

  Map<String, dynamic> toJson() => {'itemId': itemId, 'played': played};
}

@freezed
abstract class PlayedSetRes with _$PlayedSetRes {
  const factory PlayedSetRes({required String itemId, required bool played}) =
      _PlayedSetRes;

  factory PlayedSetRes.fromJson(Map<String, dynamic> json) =>
      _$PlayedSetResFromJson(json);
}

@freezed
abstract class HideFromResumeReq with _$HideFromResumeReq {
  const HideFromResumeReq._();
  const factory HideFromResumeReq({
    required String itemId,
    required bool hide,
  }) = _HideFromResumeReq;

  Map<String, dynamic> toJson() => {'itemId': itemId, 'hide': hide};
}

@freezed
abstract class HideFromResumeRes with _$HideFromResumeRes {
  const factory HideFromResumeRes({
    required String itemId,
    required bool hide,
  }) = _HideFromResumeRes;

  factory HideFromResumeRes.fromJson(Map<String, dynamic> json) =>
      _$HideFromResumeResFromJson(json);
}

@freezed
abstract class MediaEpisodesReq with _$MediaEpisodesReq {
  const MediaEpisodesReq._();
  const factory MediaEpisodesReq({
    required String seriesId,
    required String seasonId,
  }) = _MediaEpisodesReq;

  Map<String, dynamic> toJson() => {'seriesId': seriesId, 'seasonId': seasonId};
}

@freezed
abstract class MediaEpisodesRes with _$MediaEpisodesRes {
  const factory MediaEpisodesRes({
    required String seriesId,
    required String seasonId,
    required List<EmbyRawItem> items,
  }) = _MediaEpisodesRes;
}

@freezed
abstract class SeriesSeasonsReq with _$SeriesSeasonsReq {
  const SeriesSeasonsReq._();
  const factory SeriesSeasonsReq({required String seriesId}) =
      _SeriesSeasonsReq;

  Map<String, dynamic> toJson() => {'seriesId': seriesId};
}

@freezed
abstract class SeriesSeasonsRes with _$SeriesSeasonsRes {
  const factory SeriesSeasonsRes({
    required String seriesId,
    required List<EmbyRawSeason> items,
  }) = _SeriesSeasonsRes;
}

@freezed
abstract class SimilarItemsReq with _$SimilarItemsReq {
  const SimilarItemsReq._();
  const factory SimilarItemsReq({required String itemId, int? limit}) =
      _SimilarItemsReq;

  Map<String, dynamic> toJson() => {
    'itemId': itemId,
    if (limit != null) 'limit': limit,
  };
}

@freezed
abstract class SimilarItemsRes with _$SimilarItemsRes {
  const factory SimilarItemsRes({
    required String itemId,
    required List<EmbyRawItem> items,
  }) = _SimilarItemsRes;
}

@freezed
abstract class AdditionalPartsReq with _$AdditionalPartsReq {
  const factory AdditionalPartsReq({required String itemId}) =
      _AdditionalPartsReq;
}

@freezed
abstract class AdditionalPartsRes with _$AdditionalPartsRes {
  const factory AdditionalPartsRes({
    required String itemId,
    required List<EmbyRawItem> items,
  }) = _AdditionalPartsRes;
}

@freezed
abstract class CrossServerSourceCard with _$CrossServerSourceCard {
  const factory CrossServerSourceCard({
    required String serverId,
    required String serverName,
    required String serverUrl,
    required String token,
    required String userId,
    required String landingItemId,
    required String mediaSourceId,
    required EmbyRawItem item,
    required EmbyRawMediaSource mediaSource,
    MediaQualityInfo? quality,
  }) = _CrossServerSourceCard;
}

@freezed
abstract class MediaQualityInfo with _$MediaQualityInfo {
  const MediaQualityInfo._();
  const factory MediaQualityInfo({
    int? height,
    int? width,
    String? videoCodec,
    String? container,
    String? videoRange,
    String? videoRangeType,
    int? bitDepth,
    double? fps,
  }) = _MediaQualityInfo;

  factory MediaQualityInfo.fromMediaSource(EmbyRawMediaSource source) {
    final streams = source.MediaStreams ?? const [];
    for (final stream in streams) {
      if (stream.Type != 'Video') continue;
      final ex = stream.extra;
      final realFps = ex['RealFrameRate'] as num?;
      final avgFps = ex['AverageFrameRate'] as num?;
      return MediaQualityInfo(
        height: (ex['Height'] as num?)?.toInt() ?? source.Height,
        width: (ex['Width'] as num?)?.toInt() ?? source.Width,
        videoCodec: stream.Codec,
        container: source.Container,
        videoRange: ex['VideoRange'] as String?,
        videoRangeType: ex['VideoRangeType'] as String?,
        bitDepth: (ex['BitDepth'] as num?)?.toInt(),
        fps: realFps?.toDouble() ?? avgFps?.toDouble(),
      );
    }
    final nameFallback = fromSourceName(
      source.Name,
      fallbackHeight: source.Height,
      container: source.Container,
    );
    if (nameFallback != null) return nameFallback;
    return MediaQualityInfo(
      height: source.Height,
      width: source.Width,
      container: source.Container,
    );
  }


  static MediaQualityInfo? fromSourceName(
    String? name, {
    int? fallbackHeight,
    String? container,
  }) {
    final height = _heightFromName(name) ?? fallbackHeight;
    final range = _rangeFromName(name);
    if (height == null && range == null) return null;
    return MediaQualityInfo(
      height: height,
      container: container,
      videoRange: range,
    );
  }

  static int? _heightFromName(String? name) {
    if (name == null || name.isEmpty) return null;
    final lower = name.toLowerCase();
    if (RegExp(r'(?:^|[^0-9])2160(?![0-9])p?|\b4k\b|\buhd\b').hasMatch(lower)) {
      return 2160;
    }
    if (RegExp(r'(?:^|[^0-9])1440(?![0-9])p?|\b2k\b').hasMatch(lower)) {
      return 1440;
    }
    if (RegExp(r'(?:^|[^0-9])1080(?![0-9])p?|\bfhd\b').hasMatch(lower)) {
      return 1080;
    }
    if (RegExp(r'(?:^|[^0-9])720(?![0-9])p?|\bhd\b').hasMatch(lower)) {
      return 720;
    }
    if (RegExp(r'(?:^|[^0-9])480(?![0-9])p?').hasMatch(lower)) return 480;
    return null;
  }

  static String? _rangeFromName(String? name) {
    if (name == null || name.isEmpty) return null;
    final lower = name.toLowerCase();
    if (RegExp(r'\bdv\b|dolby[\s._-]*vision|dovi').hasMatch(lower)) {
      return 'DolbyVision';
    }
    if (RegExp(r'hdr10\+|hdr10plus').hasMatch(lower)) return 'HDR10Plus';
    if (RegExp(r'\bhlg\b').hasMatch(lower)) return 'HLG';
    if (RegExp(r'hdr10').hasMatch(lower)) return 'HDR10';
    if (RegExp(r'\bhdr\b').hasMatch(lower)) return 'HDR';
    return null;
  }

  String get resolutionLabel {
    final h = height;
    if (h == null) return '';
    if (h >= 2160) return '4K';
    if (h >= 1440) return '2K';
    if (h >= 1080) return '1080P';
    if (h >= 720) return '720P';
    return '${h}P';
  }

  String get dynamicRangeLabel {
    final typeKnown = _canonicalDynamicRange(videoRangeType);
    if (typeKnown != null) return typeKnown;
    final rangeKnown = _canonicalDynamicRange(videoRange);
    if (rangeKnown != null) return rangeKnown;
    final typeRaw = _rawDynamicRange(videoRangeType);
    if (typeRaw != null) return typeRaw;
    final rangeRaw = _rawDynamicRange(videoRange);
    if (rangeRaw != null) return rangeRaw;
    return 'SDR';
  }


  static String? _canonicalDynamicRange(String? raw) {
    if (raw == null) return null;
    final compact = raw.replaceAll(' ', '');
    if (compact.isEmpty) return null;
    final upper = compact.toUpperCase();
    if (upper.contains('DOVI') || upper.contains('DOLBYVISION')) {
      return 'Dolby Vision';
    }
    if (upper == 'HDR10PLUS' || upper == 'HDR10+') return 'HDR10+';
    if (upper == 'HDR10') return 'HDR10';
    if (upper == 'HLG') return 'HLG';
    if (upper == 'HDR') return 'HDR';
    if (upper == 'SDR') return 'SDR';
    return null;
  }


  static String? _rawDynamicRange(String? raw) {
    if (raw == null) return null;
    final trimmed = raw.trim();
    return trimmed.isEmpty ? null : trimmed;
  }

  bool get isDolbyVision => dynamicRangeLabel == 'Dolby Vision';
  bool get isHDR => dynamicRangeLabel != 'SDR';

  String get codecLabel {
    final c = videoCodec?.toLowerCase();
    if (c == null || c.isEmpty) return '';
    if (c == 'hevc' || c == 'h265') return 'H265';
    if (c == 'h264' || c == 'avc') return 'H264';
    if (c == 'av1') return 'AV1';
    if (c == 'vp9') return 'VP9';
    return c.toUpperCase();
  }

  String get bitDepthLabel {
    if (bitDepth == null) return '';
    return '${bitDepth}bit';
  }

  String get fpsLabel {
    if (fps == null) return '';
    final f = fps!;
    if ((f - f.roundToDouble()).abs() < 0.1) return '${f.round()}fps';
    return '${f.toStringAsFixed(1)}fps';
  }

  String get detailLine {
    final parts = <String>[
      codecLabel,
      bitDepthLabel,
      fpsLabel,
    ].where((s) => s.isNotEmpty).toList();
    return parts.join('  ');
  }

  String get summary {
    final parts = <String>[];
    final res = resolutionLabel;
    if (res.isNotEmpty) parts.add(res);
    final dr = dynamicRangeLabel;
    if (dr.isNotEmpty) parts.add(dr);
    if (codecLabel.isNotEmpty) parts.add(codecLabel);
    return parts.join(' · ');
  }
}

@freezed
abstract class CrossServerSearchReq with _$CrossServerSearchReq {
  const factory CrossServerSearchReq({
    required String anchorType,
    required String itemName,
    required Map<String, String> providerIds,
    Map<String, String>? seriesProviderIds,
    int? parentIndexNumber,
    int? indexNumber,


    String? excludedServerId,
  }) = _CrossServerSearchReq;
}


@freezed
abstract class TmdbAvailabilityReq with _$TmdbAvailabilityReq {
  const factory TmdbAvailabilityReq({
    required String tmdbId,
    required String mediaType,


    String? imdbId,
  }) = _TmdbAvailabilityReq;
}


@freezed
abstract class TmdbLibraryHit with _$TmdbLibraryHit {
  const factory TmdbLibraryHit({
    required String serverId,
    required String serverName,
    required String itemId,
    required String itemType,
    required String name,
    int? productionYear,


    int? playbackPositionTicks,


    int? runTimeTicks,
  }) = _TmdbLibraryHit;
}

enum SortOrder {
  ascending('Ascending'),
  descending('Descending');

  final String value;
  const SortOrder(this.value);

  static SortOrder? fromString(String? value) {
    if (value == null) return null;
    return SortOrder.values.firstWhere(
      (e) => e.value == value,
      orElse: () => SortOrder.ascending,
    );
  }
}

@freezed
abstract class LibraryItemsReq with _$LibraryItemsReq {
  const factory LibraryItemsReq({
    String? parentId,
    String? includeItemTypes,
    List<String>? genreIds,
    String? searchTerm,
    String? anyProviderIdEquals,
    String? fields,
    String? enableImageTypes,
    bool? groupProgramsBySeries,
    int? imageTypeLimit,
    int? startIndex,
    int? limit,
    bool? recursive,
    String? sortBy,
    SortOrder? sortOrder,
  }) = _LibraryItemsReq;
}

@freezed
abstract class LibraryItemsRes with _$LibraryItemsRes {
  const factory LibraryItemsRes({
    required String parentId,
    required List<EmbyRawItem> items,
  }) = _LibraryItemsRes;
}

@freezed
abstract class ItemCountsRes with _$ItemCountsRes {
  const factory ItemCountsRes({
    @JsonKey(name: 'MovieCount', fromJson: _intOrZero) required int movieCount,
    @JsonKey(name: 'EpisodeCount', fromJson: _intOrZero)
    required int episodeCount,
  }) = _ItemCountsRes;

  factory ItemCountsRes.fromJson(Map<String, dynamic> json) =>
      _$ItemCountsResFromJson(json);
}

@freezed
abstract class GlobalSearchReq with _$GlobalSearchReq {
  const factory GlobalSearchReq({
    required String keyword,
    String? includeItemTypes,
    int? limitPerServer,
    String? serverId,
  }) = _GlobalSearchReq;
}

@freezed
abstract class GlobalSearchServerResult with _$GlobalSearchServerResult {
  const factory GlobalSearchServerResult({
    required String serverId,
    required String serverName,
    required String serverUrl,
    required String token,
    required String userId,
    required List<EmbyRawItem> items,
  }) = _GlobalSearchServerResult;
}

int? _intOrNull(Object? value) => (value as num?)?.toInt();
double? _doubleOrNull(Object? value) => (value as num?)?.toDouble();
int _intOrZero(Object? value) => (value as num?)?.toInt() ?? 0;


List<EmbyRawMediaSource> mediaSourcesOfItem(EmbyRawItem? item) {
  final raw = item?.extra['MediaSources'];
  if (raw is! List) return const [];
  return raw
      .whereType<Map<String, dynamic>>()
      .map(EmbyRawMediaSource.fromJson)
      .toList(growable: false);
}


Map<String, String> providerIdsOfItem(EmbyRawItem item) {
  final raw = item.extra['ProviderIds'];
  if (raw is! Map) return const {};
  final ids = <String, String>{};
  for (final entry in raw.entries) {
    final key = entry.key?.toString() ?? '';
    final value = entry.value?.toString() ?? '';
    if (key.isNotEmpty && value.isNotEmpty) ids[key] = value;
  }
  return ids;
}

Map<String, String> normalizeProviderIds(Map<String, String> raw) {
  if (raw.isEmpty) return const {};
  final out = <String, String>{};
  for (final e in raw.entries) {
    final k = e.key.trim().toLowerCase();
    final v = e.value.trim();
    if (k.isNotEmpty && v.isNotEmpty) out[k] = v;
  }
  return out;
}

Map<String, String> normalizedProviderIdsOfItem(EmbyRawItem item) =>
    normalizeProviderIds(providerIdsOfItem(item));

Map<String, String>? _stringMapOrNull(Object? value) {
  if (value is! Map) return null;
  return value.map((k, v) => MapEntry(k as String, v as String));
}

List<String>? _stringListOrNull(Object? value) {
  if (value is! List) return null;
  return value.cast<String>();
}
