// Copyright 2019-present the Material Components for iOS authors. All Rights Reserved.
//
// 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 CoreGraphics
import UIKit
import MaterialComponents.MaterialAppBar
import MaterialComponents.MaterialAppBar_Theming
import MaterialComponents.MaterialContainerScheme
import MaterialComponents.MaterialFlexibleHeader_CanAlwaysExpandToMaximumHeight
import MaterialComponents.MaterialTabs

// This example demonstrates issues with flexible header tabs and animations.
class AppBarInheritedAnimatedJumpExample: UIViewController {

  lazy var appBarViewController: MDCAppBarViewController = self.makeAppBar()
  @objc var containerScheme: MDCContainerScheming = MDCContainerScheme()

  fileprivate let tabs = [
    ChildOfTrackingScrollViewViewController(title: "First"),
    ChildOfTrackingScrollViewViewController(title: "Second"),
    ChildOfTrackingScrollViewViewController(title: "Third"),
  ]
  private var currentTab: ChildOfTrackingScrollViewViewController? = nil

  lazy var tabBar: MDCTabBar = {
    let tabBar = MDCTabBar()

    tabBar.items = self.tabs.map { $0.tabBarItem }

    tabBar.delegate = self
    return tabBar
  }()

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

    self.title = "Manual Tabs Jump (Animated, UITableViewController)"
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    for (offset, tab) in zip(0..., tabs) {
      tab.tabBarItem.tag = offset
    }

    appBarViewController.applyPrimaryTheme(withScheme: containerScheme)

    // Need to update the status bar style after applying the theme.
    appBarViewController.view.isOpaque = false
    setNeedsStatusBarAppearanceUpdate()

    view.isOpaque = false
    view.backgroundColor = containerScheme.colorScheme.backgroundColor
    view.addSubview(appBarViewController.view)
    appBarViewController.didMove(toParent: self)

    switchToTab(tabs[0], animated: false)
  }

  fileprivate func switchToTab(_ tab: ChildOfTrackingScrollViewViewController, animated: Bool = true) {

    appBarViewController.headerView.trackingScrollWillChange(toScroll: tab.tableView)

    // Hide old tab.
    let removeOld: (() -> Void)
    let animateOut: (() -> Void)
    if let currentTab = currentTab {
      currentTab.willMove(toParent: nil)

      animateOut = {
        currentTab.view.alpha = 0
      }

      removeOld = {
        currentTab.headerView = nil
        currentTab.view.removeFromSuperview()
        currentTab.removeFromParent()
      }
    } else {
      removeOld = {}
      animateOut = {}
    }

    if let tabView = tab.view {
      tabView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
      tabView.frame = view.bounds
    }

    // Show new tab.
    view.addSubview(tab.view)
    view.sendSubviewToBack(tab.view)
    tab.didMove(toParent: self)
    tab.headerView = self.appBarViewController.headerView

    tab.view.alpha = 0
    let animateIn = {
      tab.view.alpha = 1
    }

    let finishMove = {
      self.appBarViewController.headerView.trackingScrollView = tab.tableView
      self.currentTab = tab
    }

    if animated {
      UIView.animate(withDuration: 1, animations: {
        animateOut()
        animateIn()
      }, completion: { _ in
        removeOld()
        finishMove()
      })
    } else {
      animateOut()
      removeOld()
      animateIn()
      finishMove()
    }
  }

  @objc func changeAlignmentDidTouch(sender: UIButton) {
    tabs[0].title = "First"
    switchToTab(tabs[0])
  }

  @objc func changeAppearance(fromSender sender: UIButton) {
    tabs[1].title = "Second"
    switchToTab(tabs[1])
  }

  // MARK: Private

  private func makeAppBar() -> MDCAppBarViewController {
    let appBarViewController = MDCAppBarViewController()

    addChild(appBarViewController)

    // Give the tab bar enough height to accommodate all possible item appearances.
    appBarViewController.headerView.minMaxHeightIncludesSafeArea = false
    appBarViewController.inferTopSafeAreaInsetFromViewController = true
    appBarViewController.headerView.canAlwaysExpandToMaximumHeight = true
    appBarViewController.headerView.sharedWithManyScrollViews = true

    appBarViewController.headerView.minimumHeight = 56
    appBarViewController.headerView.maximumHeight = 128

    appBarViewController.headerStackView.bottomBar = tabBar

    return appBarViewController
  }

  override var childForStatusBarStyle: UIViewController? {
    return appBarViewController
  }
}

extension AppBarInheritedAnimatedJumpExample: MDCTabBarDelegate {
  func tabBar(_ tabBar: MDCTabBar, didSelect item: UITabBarItem) {
    switchToTab(tabs[item.tag])
  }
}

extension AppBarInheritedAnimatedJumpExample {

  @objc class func catalogMetadata() -> [String: Any] {
    return [
      "breadcrumbs": ["App Bar", "Manual Tabs Jump (Animated, UITableViewController)"],
      "primaryDemo": false,
      "presentable": false,
    ]
  }

  @objc func catalogShouldHideNavigation() -> Bool {
    return true
  }
}
