/*
 * CameraVideoView.swift
 
 * View that handles displaying video frames
 * For now, this view displays video correctly only when created programmatically
 */

import UIKit
import XPMobileSDK

class CameraVideoView: UIView, XPSDKVideoConnectionDelegate {
    weak var delegate: CameraVideoViewDelegate?
    var videoConnection: XPSDKDownstreamVideoConnection?
    var lastFrame: XPSDKVideoConnectionFrame?
    var lastImage: UIImage?
    var videoID = XPSDKVideoID()
    var originalRect = CGRect.zero
    var waitingFirstFrame = false
    var loadingIndicator: UIActivityIndicatorView?
    var transformSourceCSToDrawCS: CGAffineTransform?
    var errorMessageLabel: UILabel?
    var lostConnection = false
    var isLoadingIndicatorVisible = false
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        clearsContextBeforeDrawing = false
        isOpaque = false
        backgroundColor = UIColor.clear
        contentMode = .center
        loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: (frame.size.width - 37) / 2, y: (frame.size.height - 37) / 2, width: 37, height: 37))
        loadingIndicator!.activityIndicatorViewStyle = .gray
        addSubview(loadingIndicator!)
        loadingIndicator!.hidesWhenStopped = true
        loadingIndicator!.autoresizingMask = [.flexibleTopMargin, .flexibleBottomMargin, .flexibleLeftMargin, .flexibleRightMargin]
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    
    override func draw(_ rect: CGRect) {
        let context: CGContext? = UIGraphicsGetCurrentContext()
        context!.clear(rect)
        let isOrientationPortrait: Bool = bounds.size.width < bounds.size.height
        if (self.videoConnection != nil) {
            let coef: CGFloat = (self.videoConnection!.sourceSize.width / self.videoConnection!.sourceSize.height)
            let width = CGFloat((isOrientationPortrait ? self.bounds.size.width : self.bounds.size.height * coef))
            let height = CGFloat((isOrientationPortrait ? self.bounds.size.width / coef : self.bounds.size.height - 32))
            let x: Int = isOrientationPortrait ? 0 : Int((self.bounds.size.width - width) / 2)
            let y: Int = isOrientationPortrait ? Int(bounds.size.height - height) / 2 : 32
            let imageRect = CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(width), height: CGFloat(height))
            if self.lastImage != nil {
                self.lastImage!.draw(in: imageRect)
            }
        }
    }
    
    // MARK: Helper methods
    
    func setLoadingIndicatorVisible(_ visible: Bool) {
        if visible {
            loadingIndicator!.startAnimating()
        }
        else {
            loadingIndicator!.stopAnimating()
        }
    }
    
    func dismissCurrentFrame() {
        if (lastImage != nil) {
            lastImage = nil
            setNeedsDisplay()
        }
    }
    
    func dismissLastFrame() {
        if lastFrame != nil {
            lastFrame = nil
        }
    }
    
    func showErrorMessage(_ errorMessage: String) {
        if errorMessage == "" {
            if (errorMessageLabel != nil) {
                errorMessageLabel!.removeFromSuperview()
                errorMessageLabel = nil
            }
        }
        else {
            if !(errorMessageLabel != nil) {
                errorMessageLabel = UILabel(frame: bounds)
                errorMessageLabel!.font = UIFont.systemFont(ofSize: 14)
                errorMessageLabel!.textAlignment = .center
                errorMessageLabel!.numberOfLines = 0
                errorMessageLabel!.isOpaque = false
                errorMessageLabel!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
                addSubview(errorMessageLabel!)
            }
            errorMessageLabel!.text = errorMessage
            isLoadingIndicatorVisible = false
        }
    }
    
    //MARK: XPSDKVideoConnectionDelegate methods
    
    func videoConnection(_ videoConnection: XPSDKDownstreamVideoConnection, receivedResponse headers: [Any]) {
        print("Camera video view received connection response")
        waitingFirstFrame = true
    }
    
    func videoConnection(_ vc: XPSDKDownstreamVideoConnection, receivedFrame frame: XPSDKVideoConnectionFrame) {
        isLoadingIndicatorVisible = false
        lastFrame = frame
        if frame.hasSizeInformation {
            originalRect = frame.sourceRect
            if !frame.sourceSize.equalTo(videoConnection!.sourceSize) {
                print("Camera video view received NEW size: \(NSValue(cgRect: originalRect)) image size: \(NSValue(cgSize: frame.image.size))")
                videoConnection!.sourceSize = frame.sourceSize
                delegate!.cameraVideoViewReceivedResolutionChange(self)
            }
        }
        
        if (frame.image != nil) {
            if lostConnection {
                showErrorMessage("Connection lost")
                delegate!.cameraVideoViewRestoredConnection(self)
                lostConnection = false
            }
            lastImage = frame.image
            setNeedsDisplay()
            if waitingFirstFrame {
                delegate!.cameraVideoViewReceivedFirstFrame(self)
                isOpaque = true
                waitingFirstFrame = false
            }
        }
        
        if videoConnection!.signal == XPSDKVideoConnectionSignalPlayback && frame.hasPlaybackInformation {
            print("Playback state: %d", frame.currentPlaybackEvents)
            if frame.changedPlaybackEvents & XPSDKVideoConnectionFrameDatabaseStart.rawValue != 0 || frame.currentPlaybackEvents & XPSDKVideoConnectionFrameDatabaseStart.rawValue != 0 {
                // Server bug  we shouldn't need to check for play stopped
                if frame.currentPlaybackEvents & XPSDKVideoConnectionFramePlayStopped.rawValue != 0 {
                    delegate!.cameraVideoViewDatabaseStartReached(self)
                }
            }
            if frame.changedPlaybackEvents & XPSDKVideoConnectionFrameDatabaseEnd.rawValue != 0 || frame.currentPlaybackEvents & XPSDKVideoConnectionFrameDatabaseEnd.rawValue != 0 {
                // Server bug  we shouldn't need to check for play stopped
                if frame.currentPlaybackEvents & XPSDKVideoConnectionFramePlayStopped.rawValue != 0 {
                    delegate!.cameraVideoViewDatabaseEndReached(self)
                    
                    
                }
            }
        }
        
    }
    
    func videoConnection(_ vc: XPSDKDownstreamVideoConnection, receivedHeadersFor frame: XPSDKVideoConnectionFrame) {
        
        if vc.signal == XPSDKVideoConnectionSignalPlayback && frame.hasPlaybackInformation {
            print("Playback state: %d", frame.currentPlaybackEvents)
            if frame.changedPlaybackEvents & XPSDKVideoConnectionFrameDatabaseStart.rawValue != 0 && frame.currentPlaybackEvents & XPSDKVideoConnectionFrameDatabaseStart.rawValue != 0 {
                // Case where the start of the database has been reached
                // We need to check if playing has stopped
                if frame.currentPlaybackEvents & XPSDKVideoConnectionFramePlayStopped.rawValue != 0 {
                    delegate!.cameraVideoViewDatabaseStartReached(self)
                }
            }
            
            if frame.changedPlaybackEvents & XPSDKVideoConnectionFrameDatabaseEnd.rawValue != 0 && frame.currentPlaybackEvents & XPSDKVideoConnectionFrameDatabaseEnd.rawValue != 0 {
                // Case where the end of the database has been reached
                // We need to check if playing has stopped
                if frame.currentPlaybackEvents & XPSDKVideoConnectionFramePlayStopped.rawValue != 0 {
                    delegate!.cameraVideoViewDatabaseEndReached(self)
                }
            }
            
            if frame.changedPlaybackEvents & XPSDKVideoConnectionFrameDatabaseError.rawValue != 0 && frame.currentPlaybackEvents & XPSDKVideoConnectionFrameDatabaseError.rawValue != 0 {
                delegate!.cameraVideoViewDatabaseError(self)
            }
        }
    }
    
    func videoConnection(_ videoConnection: XPSDKDownstreamVideoConnection, failedWithError error:Error)  {
        showErrorMessage("Connection lost")
        isLoadingIndicatorVisible = false
        delegate!.cameraVideoViewLostConnection(self)
    }
    
    func videoConnectionFinished(_ vc: XPSDKDownstreamVideoConnection) {
        lastImage = nil
        videoConnection = nil
        videoID = ""
    }
    
    func videoConnection(_ videoConnection: XPSDKDownstreamVideoConnection!, receivedPlaylist segment: XPSDKVideoConnectionSegment!) {
        print("Received playlist")
    }
    
}

protocol CameraVideoViewDelegate: NSObjectProtocol {
    func cameraVideoViewReceivedFirstFrame(_ cameraVideoView: CameraVideoView)
    
    func cameraVideoViewLostConnection(_ cameraVideoView: CameraVideoView)
    
    func cameraVideoViewRestoredConnection(_ cameraVideoView: CameraVideoView)
    
    func cameraVideoViewReceivedResolutionChange(_ cameraVideoView: CameraVideoView)
    
    func cameraVideoViewDatabaseStartReached(_ cameraVideoView: CameraVideoView)
    
    func cameraVideoViewDatabaseEndReached(_ cameraVideoView: CameraVideoView)
    
    func cameraVideoViewDatabaseError(_ cameraVideoView: CameraVideoView)
    
    func cameraVideoViewReceivedSegment(_ cameraVideoView: CameraVideoView)
}
