// 
// Copyright 2020 New Vector Ltd
//
// 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

@objc enum PiPViewPosition: Int {
    case bottomLeft
    case bottomRight    //  default value
    case topRight
    case topLeft
}

@objc protocol PiPViewDelegate: AnyObject {
    @objc optional func pipView(_ view: PiPView, didMoveTo position: PiPViewPosition)
    @objc optional func pipViewDidTap(_ view: PiPView)
}

@objcMembers
class PiPView: UIView {
    
    private enum Defaults {
        static let margins: UIEdgeInsets = UIEdgeInsets(top: 64, left: 20, bottom: 64, right: 20)
        static let cornerRadius: CGFloat = 8
        static let animationDuration: TimeInterval = 0.25
    }
    
    var margins: UIEdgeInsets = Defaults.margins {
        didSet {
            guard self.superview != nil else { return }
            self.move(to: self.position, animated: true)
        }
    }
    var cornerRadius: CGFloat = Defaults.cornerRadius {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
    var position: PiPViewPosition = .bottomRight
    weak var delegate: PiPViewDelegate?
    
    private var originalCenter: CGPoint = .zero
    private var isMoving: Bool = false
    private lazy var tapGestureRecognizer: UITapGestureRecognizer = {
        return UITapGestureRecognizer(target: self, action: #selector(tapped(_:)))
    }()
    private lazy var panGestureRecognizer: UIPanGestureRecognizer = {
        return UIPanGestureRecognizer(target: self, action: #selector(panned(_:)))
    }()
    private var rotationObserver: NSObjectProtocol?
    
    init() {
        super.init(frame: .zero)
        setup()
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }
    
    var contentView: UIView? {
        willSet {
            if let contentView = contentView {
                //  restore properties of old content view
                contentView.isUserInteractionEnabled = true
            }
        } didSet {
            if let contentView = contentView {
                contentView.isUserInteractionEnabled = false
                addSubview(contentView)
NSLayoutConstraint.activate([
                    contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
                    contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
                    contentView.topAnchor.constraint(equalTo: topAnchor),
                    contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
                ])
            }
        }
    }
    
    func move(in view: UIView? = nil,
              to position: PiPViewPosition = .bottomRight,
              targetSize: CGSize? = nil,
              animated: Bool = false,
              completion: ((Bool) -> Void)? = nil) {
        
        let block = {
            let targetFrame = self.targetFrame(for: position, in: view, targetSize: targetSize)
            self.frame = targetFrame
        }
        
        if animated {
            UIView.animate(withDuration: Defaults.animationDuration, animations: block) { (completed) in
                self.position = position
                completion?(completed)
            }
        } else {
            block()
            self.position = position
            completion?(true)
        }
    }
    
    deinit {
        if let rotationObserver = rotationObserver {
            NotificationCenter.default.removeObserver(rotationObserver)
        }
    }
    
    //  MARK: - Private
    
    private func setup() {
        isUserInteractionEnabled = true
        clipsToBounds = true
        layer.cornerRadius = cornerRadius
        addGestureRecognizer(tapGestureRecognizer)
        addGestureRecognizer(panGestureRecognizer)
        rotationObserver = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil) { [weak self] (_) in
            guard let self = self else { return }
            guard self.superview != nil else { return }
            self.move(to: self.position, animated: true)
        }
    }
    
    private func targetFrame(for position: PiPViewPosition,
                             in view: UIView?,
                             targetSize: CGSize?) -> CGRect {
        guard let view = view ?? superview else {
            return .zero
        }
        let targetSize = targetSize ?? frame.size
        
        var superviewWidth: CGFloat = 0
        var superviewHeight: CGFloat = 0
        
        if UIDevice.current.orientation.isPortrait {
            superviewWidth = min(view.bounds.width, view.bounds.height)
            superviewHeight = max(view.bounds.width, view.bounds.height)
        } else {
            superviewWidth = max(view.bounds.width, view.bounds.height)
            superviewHeight = min(view.bounds.width, view.bounds.height)
        }
        
        switch position {
        case .bottomLeft:
            let origin = CGPoint(x: margins.left + view.safeAreaInsets.left,
                                 y: superviewHeight - view.safeAreaInsets.bottom - targetSize.height - margins.bottom)
            return CGRect(origin: origin,
                          size: targetSize)
        case .bottomRight:
            let origin = CGPoint(x: superviewWidth - view.safeAreaInsets.right - margins.right - targetSize.width,
                                 y: superviewHeight - view.safeAreaInsets.bottom - targetSize.height - margins.bottom)
            return CGRect(origin: origin,
                          size: targetSize)
        case .topRight:
            let origin = CGPoint(x: superviewWidth - view.safeAreaInsets.right - margins.right - targetSize.width,
                                 y: margins.top + view.safeAreaInsets.top)
            return CGRect(origin: origin,
                          size: targetSize)
        case .topLeft:
            let origin = CGPoint(x: margins.left + view.safeAreaInsets.left,
                                 y: margins.top + view.safeAreaInsets.top)
            return CGRect(origin: origin,
                          size: targetSize)
        }
    }
    
    @objc private func tapped(_ sender: UITapGestureRecognizer) {
        delegate?.pipViewDidTap?(self)
    }
    
    @objc private func panned(_ sender: UIPanGestureRecognizer) {
        switch sender.state {
        case .possible:
            isMoving = false
        case .began:
            originalCenter = center
            isMoving = true
        case .changed:
            let translation = sender.translation(in: superview)
            
            if isMoving {
                center = CGPoint(x: originalCenter.x + translation.x,
                                 y: originalCenter.y + translation.y)
            }
        case .ended:
            defer {
                isMoving = false
            }
            
            if isMoving {
                guard let superview = self.superview else { return }
                
                let translation = sender.translation(in: superview)
                
                let bounds = superview.bounds
                let midX = bounds.width / 2
                let midY = bounds.height / 2
                
                let onLeftHalf = originalCenter.x + translation.x < midX
                let onTopHalf = originalCenter.y + translation.y < midY
                
                var newPosition: PiPViewPosition = .bottomLeft
                
                if onLeftHalf {
                    if onTopHalf {
                        newPosition = .topLeft
                    } else {
                        newPosition = .bottomLeft
                    }
                } else {
                    if onTopHalf {
                        newPosition = .topRight
                    } else {
                        newPosition = .bottomRight
                    }
                }
                
                move(to: newPosition) { (_) in
                    self.delegate?.pipView?(self, didMoveTo: newPosition)
                }
            }
        case .cancelled:
            isMoving = false
        case .failed:
            isMoving = false
        @unknown default:
            isMoving = false
        }
    }
    
}
