/*
 * Copyright 2017 Google
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import UIKit

import FirebaseDev

enum Row: String {
  case apnsToken = "apnsToken"
  case apnsStatus = "apnsStatus"
  case requestAPNSPermissions = "requestAPNSPermissions"
  case fcmToken = "fcmToken"
}

enum PermissionsButtonTitle: String {
  case requestPermissions = "Request User Notifications"
  case noAPNS = "Cannot Request Permissions (No APNs)"
  case alreadyRequested = "Already Requested Permissions"
  case simulator = "Cannot Request Permissions (Simulator)"
}

class MessagingViewController: UIViewController {

  let tableView: UITableView

  var sections = [[Row]]()
  var sectionHeaderTitles = [String?]()

  var allowedNotificationTypes: [NotificationsControllerAllowedNotificationType]?

  // Cached rows by Row type. Since this is largely a fixed table view, we'll
  // keep track of our created cells and UI, rather than have all the logic

  required init?(coder aDecoder: NSCoder) {
    tableView = UITableView(frame: CGRect.zero, style: .grouped)
    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44
    // Allow UI Controls within the table to be immediately responsive
    tableView.delaysContentTouches = false
    super.init(coder: aDecoder)
    tableView.dataSource = self
    tableView.delegate = self
  }

  override func loadView() {
    super.loadView()
    view = UIView(frame: CGRect.zero)
    view.addSubview(self.tableView)
    // Ensure that the tableView always is the size of the view
    tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    let center = NotificationCenter.default
    center.addObserver(self,
                       selector: #selector(onAPNSTokenReceived) ,
                       name: APNSTokenReceivedNotification,
                       object: nil)
    center.addObserver(self,
                       selector: #selector(onUserNotificationSettingsChanged),
                       name: UserNotificationsChangedNotification,
                       object: nil)
    center.addObserver(self,
                       selector: #selector(onFCMTokenRefreshed),
                       name: Notification.Name.MessagingRegistrationTokenRefreshed,
                       object: nil)
    updateAllowedNotificationTypes {
      self.resetTableContents()
      self.tableView.reloadData()
    }
  }

  func onAPNSTokenReceived() {
    // Reload the appropriate cells
    updateAllowedNotificationTypes {
      if let tokenPath = self.indexPathFor(.apnsToken),
         let statusPath = self.indexPathFor(.apnsStatus),
         let requestPath = self.indexPathFor(.requestAPNSPermissions) {
        self.updateIndexPaths(indexPaths: [tokenPath, statusPath, requestPath])
      }
    }
  }

  func onFCMTokenRefreshed() {
    if let indexPath = indexPathFor(.fcmToken) {
      updateIndexPaths(indexPaths: [indexPath])
    }
  }

  func onUserNotificationSettingsChanged() {
    updateAllowedNotificationTypes {
      if let statusPath = self.indexPathFor(.apnsStatus),
         let requestPath = self.indexPathFor(.requestAPNSPermissions) {
        self.updateIndexPaths(indexPaths: [statusPath, requestPath])
      }
    }
  }

  private func updateIndexPaths(indexPaths: [IndexPath]) {
    tableView.beginUpdates()
    tableView.reloadRows(at: indexPaths, with: .none)
    tableView.endUpdates()
  }

  fileprivate func updateAllowedNotificationTypes(_ completion: (() -> Void)?) {
    NotificationsController.shared.getAllowedNotificationTypes { (types) in
      self.allowedNotificationTypes = types
      self.updateRequestAPNSButton()
      completion?()
    }
  }

  fileprivate func updateRequestAPNSButton() {
    guard !Environment.isSimulator else {
      requestPermissionsButton.isEnabled = false
      requestPermissionsButton.setTitle(PermissionsButtonTitle.simulator.rawValue, for: .normal)
      return
    }
    guard let allowedTypes = allowedNotificationTypes else {
      requestPermissionsButton.isEnabled = false
      requestPermissionsButton.setTitle(PermissionsButtonTitle.noAPNS.rawValue, for: .normal)
      return
    }

    requestPermissionsButton.isEnabled =
        (allowedTypes.count == 1 && allowedTypes.first! == .silent)

    let title: PermissionsButtonTitle =
        (requestPermissionsButton.isEnabled ? .requestPermissions : .alreadyRequested)
    requestPermissionsButton.setTitle(title.rawValue, for: .normal)
  }

  // MARK: UI (Cells and Buttons) Defined as lazy properties
  lazy var apnsTableCell: UITableViewCell = {
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: Row.apnsToken.rawValue)
    cell.textLabel?.numberOfLines = 0
    cell.textLabel?.lineBreakMode = .byWordWrapping
    return cell
  }()

  lazy var apnsStatusTableCell: UITableViewCell = {
    let cell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: Row.apnsStatus.rawValue)
    cell.textLabel?.text = "Allowed:"
    cell.detailTextLabel?.numberOfLines = 0
    cell.detailTextLabel?.lineBreakMode = .byWordWrapping
    return cell
  }()

  lazy var requestPermissionsButton: UIButton = {
    let button = UIButton(type: .system)
    button.setTitle(PermissionsButtonTitle.requestPermissions.rawValue, for: .normal)
    button.setTitleColor(UIColor.gray, for: .highlighted)
    button.setTitleColor(UIColor.gray, for: .disabled)
    button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
    button.addTarget(self,
                     action: #selector(onRequestUserNotificationsButtonTapped),
                     for: .touchUpInside)
    return button
  }()

  lazy var apnsRequestPermissionsTableCell: UITableViewCell = {
    let cell = UITableViewCell(style: .default,
                               reuseIdentifier: Row.requestAPNSPermissions.rawValue)
    cell.selectionStyle = .none
    cell.contentView.addSubview(self.requestPermissionsButton)
    self.requestPermissionsButton.frame = cell.contentView.bounds
    self.requestPermissionsButton.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    return cell
  }()

  lazy var fcmTokenTableCell: UITableViewCell = {
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: Row.fcmToken.rawValue)
    cell.textLabel?.numberOfLines = 0
    cell.textLabel?.lineBreakMode = .byCharWrapping
    return cell
  }()
}

// MARK: - Configuring the table view and cells with information
extension MessagingViewController {
  func resetTableContents() {
    sections.removeAll()
    sectionHeaderTitles.removeAll()

    // APNS
    let apnsSection: [Row] = [.apnsToken, .apnsStatus, .requestAPNSPermissions]
    sections.append(apnsSection)
    sectionHeaderTitles.append("APNs")

    // FCM
    let fcmSection: [Row] = [.fcmToken]
    sections.append(fcmSection)
    sectionHeaderTitles.append("FCM Token")

  }

  func indexPathFor(_ rowId: Row) -> IndexPath? {
    var sectionIndex = 0
    for section in sections {
      var rowIndex = 0
      for row in section {
        if row == rowId {
          return IndexPath(row: rowIndex, section: sectionIndex)
        }
        rowIndex += 1
      }
      sectionIndex += 1
    }
    return nil
  }

  func configureCell(_ cell: UITableViewCell, withAPNSToken apnsToken: Data?) {
    guard !Environment.isSimulator else {
      cell.textLabel?.text = "APNs notifications are not supported in the simulator."
      cell.detailTextLabel?.text = nil
      return
    }
    if let apnsToken = apnsToken {
      cell.textLabel?.text = apnsToken.hexByteString
      cell.detailTextLabel?.text = "Tap to Share"
    } else {
      cell.textLabel?.text = "None"
      cell.detailTextLabel?.text = nil
    }
  }

  func configureCellWithAPNSStatus(_ cell: UITableViewCell) {
    if let allowedNotificationTypes = allowedNotificationTypes {
      let displayableTypes: [String] = allowedNotificationTypes.map { return $0.rawValue }
      cell.detailTextLabel?.text = displayableTypes.joined(separator: ", ")
    } else {
      cell.detailTextLabel?.text = "Retrieving..."
    }
  }

  func configureCell(_ cell: UITableViewCell, withFCMToken fcmToken: String?) {
    if let fcmToken = fcmToken {
      cell.textLabel?.text = fcmToken
      cell.detailTextLabel?.text = "Tap to Share"
    } else {
      cell.textLabel?.text = "None"
      cell.detailTextLabel?.text = nil
    }
  }
}

// MARK: - UITableViewDataSource
extension MessagingViewController: UITableViewDataSource {
  func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count
  }

  public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return sections[section].count
  }

  public func tableView(_ tableView: UITableView,
                        cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let row = sections[indexPath.section][indexPath.row]

    let cell: UITableViewCell
    switch row {
    case .apnsToken:
      cell = apnsTableCell
      configureCell(cell, withAPNSToken: Messaging.messaging().apnsToken)
    case .apnsStatus:
      cell = apnsStatusTableCell
      configureCellWithAPNSStatus(cell)
    case .requestAPNSPermissions:
      cell = apnsRequestPermissionsTableCell
    case .fcmToken:
      cell = fcmTokenTableCell
      configureCell(cell, withFCMToken: Messaging.messaging().fcmToken)
    }
    return cell
  }

  func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sectionHeaderTitles[section]
  }
}

// MARK: - UITableViewDelegate
extension MessagingViewController: UITableViewDelegate {
  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)

    let row = sections[indexPath.section][indexPath.row]
    switch row {
    case .apnsToken:
      if let apnsToken = Messaging.messaging().apnsToken {
        showActivityViewControllerFor(sharedItem: apnsToken.hexByteString)
      }
    case .fcmToken:
      if let fcmToken = Messaging.messaging().fcmToken {
        showActivityViewControllerFor(sharedItem: fcmToken)
      }
    default: break
    }
  }
}

// MARK: - UI Controls
extension MessagingViewController {
  func onRequestUserNotificationsButtonTapped(sender: UIButton) {
    NotificationsController.shared.registerForUserFacingNotificationsFor(UIApplication.shared)
  }
}

// MARK: - Activity View Controller
extension MessagingViewController {
  func showActivityViewControllerFor(sharedItem: Any) {
    let activityViewController = UIActivityViewController(activityItems: [sharedItem],
                                                          applicationActivities: nil)
    present(activityViewController, animated: true, completion: nil)
  }
}

