//
// Copyright (c) Double Symmetry GmbH
// Commercial use requires a license. See https://rntp.dev/pricing
//

enum EmitEventType: String, CaseIterable {
  case PlaybackStateChanged = "event.playback-state-changed"
  case IsPlayingChanged = "event.is-playing-changed"
  case MediaItemTransition = "event.media-item-transition"
  case PlaybackError = "event.playback-error"
  case QueueChanged = "event.queue-changed"
  case RemotePlay = "event.remote-play"
  case RemotePause = "event.remote-pause"
  case RemoteNext = "event.remote-next"
  case RemotePrevious = "event.remote-previous"
  case RemoteStop = "event.remote-stop"
  case RemoteSeek = "event.remote-seek"
  case MetadataReceived = "event.metadata-received"
  case MediaMetadataChanged = "event.media-metadata-changed"
  case RemoteSkipForward = "event.remote-skip-forward"
  case RemoteSkipBackward = "event.remote-skip-backward"
  case PlaybackProgressUpdated = "event.playback-progress-updated"
  case SleepTimerTriggered = "event.sleep-timer-triggered"

  static func all() -> [String] {
    return allCases.map { $0.rawValue }
  }
}

protocol EmitEvent {
  var eventType: EmitEventType { get }
  var body: [String: Any] { get }
}

struct PlaybackStateChangedEvent: EmitEvent {
  let state: String

  var eventType: EmitEventType { .PlaybackStateChanged }
  var body: [String: Any] {
    return ["state": state]
  }
}

struct IsPlayingChangedEvent: EmitEvent {
  let playing: Bool

  var eventType: EmitEventType { .IsPlayingChanged }
  var body: [String: Any] {
    return ["playing": playing]
  }
}

struct MediaItemTransitionEvent: EmitEvent {
  let item: [String: Any]?
  let index: Int

  var eventType: EmitEventType { .MediaItemTransition }
  var body: [String: Any] {
    var result: [String: Any] = ["index": index]
    if let item = item {
      result["item"] = item
    }
    return result
  }
}

struct PlaybackErrorEvent: EmitEvent {
  let code: String
  let message: String

  var eventType: EmitEventType { .PlaybackError }
  var body: [String: Any] {
    return ["code": code, "message": message]
  }
}

struct QueueChangedEvent: EmitEvent {
  var eventType: EmitEventType { .QueueChanged }
  var body: [String: Any] { [:] }
}

struct RemotePlayEvent: EmitEvent {
  var eventType: EmitEventType { .RemotePlay }
  var body: [String: Any] { [:] }
}

struct RemotePauseEvent: EmitEvent {
  var eventType: EmitEventType { .RemotePause }
  var body: [String: Any] { [:] }
}

struct RemoteNextEvent: EmitEvent {
  var eventType: EmitEventType { .RemoteNext }
  var body: [String: Any] { [:] }
}

struct RemotePreviousEvent: EmitEvent {
  var eventType: EmitEventType { .RemotePrevious }
  var body: [String: Any] { [:] }
}

struct RemoteStopEvent: EmitEvent {
  var eventType: EmitEventType { .RemoteStop }
  var body: [String: Any] { [:] }
}

struct RemoteSeekEvent: EmitEvent {
  let position: Double

  var eventType: EmitEventType { .RemoteSeek }
  var body: [String: Any] {
    return ["position": position]
  }
}

struct MetadataReceivedEvent: EmitEvent {
  let metadata: StreamMetadata

  var eventType: EmitEventType { .MetadataReceived }
  var body: [String: Any] {
    var result: [String: Any] = [:]
    if let title = metadata.title { result["title"] = title }
    if let artist = metadata.artist { result["artist"] = artist }
    if let albumTitle = metadata.albumTitle { result["albumTitle"] = albumTitle }
    if let artworkUri = metadata.artworkUri { result["artworkUrl"] = artworkUri }
    if let genre = metadata.genre { result["genre"] = genre }
    return result
  }
}

/// Effective metadata of the currently active MediaItem after any merge of
/// stream-derived metadata onto user-supplied fields. Mirrors Android's
/// `MediaMetadataChangedEvent` and the JS `Event.MediaMetadataChanged`.
struct MediaMetadataChangedEvent: EmitEvent, Equatable {
  let title: String?
  let artist: String?
  let albumTitle: String?
  let artworkUrl: String?
  let genre: String?

  var eventType: EmitEventType { .MediaMetadataChanged }
  var body: [String: Any] {
    var result: [String: Any] = [:]
    if let title { result["title"] = title }
    if let artist { result["artist"] = artist }
    if let albumTitle { result["albumTitle"] = albumTitle }
    if let artworkUrl { result["artworkUrl"] = artworkUrl }
    if let genre { result["genre"] = genre }
    return result
  }

  var isEmpty: Bool {
    return title == nil && artist == nil && albumTitle == nil &&
      artworkUrl == nil && genre == nil
  }
}

struct RemoteSkipForwardEvent: EmitEvent {
  let interval: Double

  var eventType: EmitEventType { .RemoteSkipForward }
  var body: [String: Any] {
    return ["interval": interval]
  }
}

struct RemoteSkipBackwardEvent: EmitEvent {
  let interval: Double

  var eventType: EmitEventType { .RemoteSkipBackward }
  var body: [String: Any] {
    return ["interval": interval]
  }
}

struct PlaybackProgressUpdatedEvent: EmitEvent {
  let mediaId: String
  let position: Double
  let duration: Double
  let timestamp: Double

  var eventType: EmitEventType { .PlaybackProgressUpdated }
  var body: [String: Any] {
    return ["mediaId": mediaId, "position": position, "duration": duration, "timestamp": timestamp]
  }
}

struct SleepTimerTriggeredEvent: EmitEvent {
    let sleepType: String // "time" or "mediaItem"

    var eventType: EmitEventType { .SleepTimerTriggered }
    var body: [String: Any] {
        return ["type": sleepType]
    }
}
