//
//  StoryboardCutsceneViewController.swift
//  
//  Copyright © 2016-2019 Apple Inc. All rights reserved.
//

import UIKit
import PlaygroundSupport

public protocol CutsceneAnimatable {
    func shouldAnimateTowards(forward: Bool) -> Bool
}

@objc(StoryboardCutsceneViewController)
public class StoryboardCutsceneViewController: UIPageViewController {
    private var previousButton: UIButton?
    private var nextButton: UIButton?
    private var startCodingButton: UIButton?
    private var pageIndicatorLabel: UILabel?
    private let buttonSize = CGSize(width: 44, height: 44)
    private let defaultPageButtonYConstant: CGFloat = -40
    private var pagingButtonCenterYConstraint: NSLayoutConstraint?
    
    private var storyboardName: String = ""
    private var plistName: String = ""
    private var displayPagingButtons: Bool = true
    
    var pageNames = [String]()
    
    var currentPageIndex = 0
    private var transitioningPageIndex: Int?
    
    public convenience init(storyboardName: String, plistName: String, displayPagingButtons: Bool) {
        self.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
        self.storyboardName = storyboardName
        self.plistName = plistName
        self.displayPagingButtons = displayPagingButtons
    }
    
    override public init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) {
        super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
    }
    
    required public init?(coder: NSCoder) {
        super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
    }
    
    override public func viewDidLoad() {
        super.viewDidLoad()
        
        loadPages()
        
        if displayPagingButtons {
            setupPagingButtons()
        }
        
        goToPage(at: 0)
    }
    
    private func setupPagingButtons() {
        
        let previousButton = UIButton()
        let nextButton = UIButton()
        let startCodingButton = UIButton()
        let pageIndicatorLabel = UILabel()
        
        view.addSubview(previousButton)
        view.addSubview(nextButton)
        view.addSubview(pageIndicatorLabel)
        view.addSubview(startCodingButton)
        
        previousButton.backgroundColor = view.tintColor
        nextButton.backgroundColor = view.tintColor
        startCodingButton.backgroundColor = view.tintColor
        previousButton.tintColor = .white
        nextButton.tintColor = .white
        startCodingButton.tintColor = .white
        let cornerRadius = 0.5 * buttonSize.height
        previousButton.layer.cornerRadius = cornerRadius
        nextButton.layer.cornerRadius = cornerRadius
        startCodingButton.layer.cornerRadius = cornerRadius
        let chevronRight = UIImage(named: "chevron")!.scaledToFit(within: CGSize(width: 24, height: 24))
        let chevronLeft = UIImage(cgImage: chevronRight.cgImage!, scale: chevronRight.scale, orientation: .upMirrored)
        previousButton.setImage(chevronLeft.withRenderingMode(.alwaysTemplate), for: .normal)
        nextButton.setImage(chevronRight.withRenderingMode(.alwaysTemplate), for: .normal)
        let startCodingFont = UIFont.boldSystemFont(ofSize: 22)
        startCodingButton.titleLabel?.font = startCodingFont
        let startCodingTitle = NSLocalizedString("Start Coding", comment: "start coding")
        startCodingButton.setTitle(startCodingTitle, for: .normal)
        let startCodingWidth = (startCodingTitle as NSString).size(withAttributes: [NSAttributedString.Key.font : startCodingFont]).width
        
        previousButton.translatesAutoresizingMaskIntoConstraints = false
        nextButton.translatesAutoresizingMaskIntoConstraints = false
        startCodingButton.translatesAutoresizingMaskIntoConstraints = false
        pageIndicatorLabel.translatesAutoresizingMaskIntoConstraints = false
        
        previousButton.addTarget(self, action: #selector(didPressPreviousButton), for: .touchUpInside)
        nextButton.addTarget(self, action: #selector(didPressNextButton), for: .touchUpInside)
        startCodingButton.addTarget(self, action: #selector(didPressStartCodingButton), for: .touchUpInside)
        
        pageIndicatorLabel.font = UIFont.systemFont(ofSize: 28)
        pageIndicatorLabel.textColor = .darkGray
        
        let buttonCenterYConstraint = previousButton.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: defaultPageButtonYConstant)
        pagingButtonCenterYConstraint = buttonCenterYConstraint
        
        NSLayoutConstraint.activate([
            buttonCenterYConstraint,
            previousButton.widthAnchor.constraint(equalToConstant: buttonSize.width),
            previousButton.heightAnchor.constraint(equalToConstant: buttonSize.height),
            previousButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -150),
            ])
        
        NSLayoutConstraint.activate([
            nextButton.centerYAnchor.constraint(equalTo: previousButton.centerYAnchor),
            nextButton.widthAnchor.constraint(equalToConstant: buttonSize.width),
            nextButton.heightAnchor.constraint(equalToConstant: buttonSize.height),
            nextButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 150),
            ])
        
        NSLayoutConstraint.activate([
            startCodingButton.centerYAnchor.constraint(equalTo: previousButton.centerYAnchor),
            startCodingButton.widthAnchor.constraint(equalToConstant: startCodingWidth + 60),
            startCodingButton.heightAnchor.constraint(equalToConstant: buttonSize.height),
            startCodingButton.leftAnchor.constraint(equalTo: previousButton.rightAnchor, constant: 50),
            ])
        
        NSLayoutConstraint.activate([
            pageIndicatorLabel.centerYAnchor.constraint(equalTo: previousButton.centerYAnchor),
            pageIndicatorLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            ])
        
        self.previousButton = previousButton
        self.nextButton = nextButton
        self.startCodingButton = startCodingButton
        self.pageIndicatorLabel = pageIndicatorLabel
    }
    
    private func onTransitionCompletedToPageWith(index pageIndex: Int) {
        self.currentPageIndex = pageIndex
        
        previousButton?.isEnabled = currentPageIndex > 0
        nextButton?.isEnabled = currentPageIndex < pageNames.count - 1
        
        let lastPage = (pageIndex == pageNames.count - 1)
        
        nextButton?.isHidden = lastPage
        pageIndicatorLabel?.isHidden = lastPage
        startCodingButton?.isHidden = !lastPage
        
        pageIndicatorLabel?.text = String(format: NSLocalizedString("%d of %d", comment: "Cutscene page indicator: page x of n"), currentPageIndex+1, pageNames.count)
        pageIndicatorLabel?.sizeToFit()
    }
    
    private func loadPages() {

        guard let cutscenePlistPath = Bundle.main.path(forResource: plistName, ofType: "plist") else {
            print("=== Error: unable to locate plist \(plistName), \(Bundle.main)")
            return
        }
        
        var definition: CutsceneDefinition?
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: cutscenePlistPath))
            let decoder = PropertyListDecoder()
            definition = try decoder.decode(CutsceneDefinition.self, from: data)
        } catch {
            print(error)
        }
        
        guard let cutsceneDefinition = definition else {
            print("=== Unable to decode CutsceneDefinition from \(cutscenePlistPath)")
            return
        }
        
        self.pageNames = cutsceneDefinition.pages
    }
    
    func goToPage(at pageIndex: Int) {
        guard pageIndex >= 0, pageIndex < pageNames.count else { return }
        var direction: UIPageViewController.NavigationDirection = .forward
        if (pageIndex < currentPageIndex) {
            direction = UIPageViewController.NavigationDirection.reverse
        }
        
        let storyboardId = pageNames[pageIndex]
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        let viewController = storyboard.instantiateViewController(withIdentifier: storyboardId)
        viewController.view.bounds = view.bounds
        
        var animated = true
        if let animatableViewController = viewController as? CutsceneAnimatable {
            animated = animatableViewController.shouldAnimateTowards(forward: direction == .forward)
        }
        
        setViewControllers([viewController], direction: direction, animated: animated, completion: { completed in
            if completed && animated {
                self.onTransitionCompletedToPageWith(index: pageIndex)
            }
        })
        
        // If animated is false the completion closure is never called in setViewControllers above so make sure it's called now.
        if !animated {
            onTransitionCompletedToPageWith(index: pageIndex)
        }
    }
    
    //MARK: Actions
    @objc
    func didPressPreviousButton(_ sender: UIButton) {
        goToPage(at: currentPageIndex - 1)
    }
    
    @objc
    func didPressNextButton(_ sender: UIButton) {
        goToPage(at: currentPageIndex + 1)
    }
    
    @objc func didPressStartCodingButton(_ sender: UIButton) {
        PlaygroundPage.current.navigateTo(page: .next)
    }
    
    public func makeAdjustments(cutoffPercentage: CGFloat) {
        pagingButtonCenterYConstraint?.constant = defaultPageButtonYConstant - (view.bounds.height * cutoffPercentage / 2)
    }
}
