//
//  XPSDKConnection+Video.swift
//  XPMobileSDK
//
//  Created by Aleksandra Spirovska on 11/29/17.
//  Copyright © 2017 Milestone Systems A/S. All rights reserved.
//

import Foundation

extension XPSDKConnection {
    
    //MARK: Request Stream
    public func requestStream(forCameraID cameraID: XPSDKViewID,
                              size: CGSize,
                              method: XPSDKVideoConnectionMethod,
                              signal: XPSDKVideoConnectionSignal,
                              fps:Int,
                              compressionLevel:Int,
                              userInitiatedDownsampling:Bool,
                              keyFramesOnly:Bool,
                              resizeSupported: Bool,
                              time: NSDate?,
                              usesTranscoding: Bool,
                              successHandler: @escaping (XPSDKResponse, XPSDKVideoConnection) -> Void,
                              failureHandler: FailureBlock?) {
        
        log.info("Request stream for camera with id:\(cameraID) with fps: \(fps)")
        
        let timestamp:NSNumber
        if time == nil {
            timestamp = NSNumber(value: llrint(Date(timeIntervalSinceNow:0).timeIntervalSince1970 * 1000))
        } else {
            timestamp = NSNumber(value: llrint(time!.timeIntervalSince1970 * 1000))
        }
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameRequestStream
        
        let params: [String: String] = (signal == XPSDKVideoConnectionSignalUpload) ? [XPSDKConnectionRequestStreamParamSignalType : "Upload", XPSDKConnectionRequestStreamBookmark : ""] : [XPSDKConnectionRequestStreamParamCameraID: cameraID as String!, XPSDKConnectionRequestStreamParamDestWidth : "\(lround(Double(size.width)))",
            XPSDKConnectionRequestStreamParamDestHeight : "\(lround(Double(size.height)))",
            XPSDKConnectionRequestStreamParamFPS : "\(fps)",
            XPSDKConnectionRequestStreamParamCompressionLevel : "\(compressionLevel)",
            XPSDKConnectionRequestStreamParamUserDownsampling : userInitiatedDownsampling ? "Yes" : "No",
            XPSDKConnectionRequestStreamParamSignalType : signal == XPSDKVideoConnectionSignalLive ? "Live" : "Playback",
            XPSDKConnectionRequestStreamParamMethodType : method == XPSDKVideoConnectionMethodPull ? "Pull" : "Push",
            XPSDKConnectionRequestStreamParamKeyFramesOnly : keyFramesOnly ? "Yes" : "No",
            XPSDKConnectionRequestStreamParamTime : "\(timestamp)",
            XPSDKConnectionRequestStreamParamStreamType : usesTranscoding ? "Transcoded" : "Segmented",
            XPSDKConnectionRequestStreamParamResizeAvailable : resizeSupported ? "Yes" : "No"
        ]
        
        request.parameters = params
        
        request.successHandler = { (response) in
            let vc = self.requestStreamResponseHandler(response: response, cameraId: cameraID, signal: signal, method: method)
            successHandler(response, vc);
            self.videoConnections.add(vc)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()                
    }
    
    public func cancelRequestStream(request : XPSDKRequest) {
        if (self.requests.contains(request)) {
            request.cancelSession()
            requests.remove(request)
            self.updateNetworkIndicator()
        }
        self.startLiveMessageTimer()
    }
    
    private func requestStreamResponseHandler(response: XPSDKResponse, cameraId: XPSDKViewID, signal: XPSDKVideoConnectionSignal, method:XPSDKVideoConnectionMethod) -> XPSDKVideoConnection {
        var videoConnection:XPSDKVideoConnection
        let outputParams = response.parameters
        let videoID = outputParams![XPSDKConnectionRequestStreamParamVideoID] as! XPSDKVideoID!
        if (signal == XPSDKVideoConnectionSignalUpload) {
            let locSupported = (outputParams![XPSDKVideoPushLocation] as! String == XPSDKConnectionRequestStreamParamSupportsValueYes)
            videoConnection = XPSDKUpstreamVideoConnection.init(fromControlConnection: self, videoID: videoID as String!, locationSupportedByServer: locSupported)
        } else {
            let srcSize = CGSize(width: Int(outputParams![XPSDKConnectionRequestStreamParamSrcWidth] as! String)!, height: Int(outputParams![XPSDKConnectionRequestStreamParamSrcHeight] as! String)!)
            let usesTranscoding = !(outputParams![XPSDKConnectionRequestStreamParamStreamType] as! String == "Segmented")
            let conn = XPSDKDownstreamVideoConnection.init(fromControlConnection: self, cameraID: cameraId as String!, videoID: videoID as String!, method: method, signal: signal, sourceSize: srcSize, usesTranscoding: usesTranscoding)!
            conn.supportsLive = outputParams![XPSDKConnectionRequestStreamParamSupportsLive] as! String == XPSDKConnectionRequestStreamParamSupportsValueYes
            conn.supportsPlayback = outputParams![XPSDKConnectionRequestStreamParamSupportsPlayback] as! String == XPSDKConnectionRequestStreamParamSupportsValueYes
            conn.supportsPTZ = outputParams![XPSDKConnectionRequestStreamParamSupportsPTZ] as! String == XPSDKConnectionRequestStreamParamSupportsValueYes
            conn.supportsPTZPresets = outputParams![XPSDKConnectionRequestStreamParamSupportsPTZPresets] as! String == XPSDKConnectionRequestStreamParamSupportsValueYes
            let seqsParam = outputParams?[XPSDKConnectionRequestStreamParamSupportsSequences]
            conn.supportsSequences = (seqsParam == nil) || (seqsParam as! String == XPSDKConnectionRequestStreamParamSupportsValueYes)
            var exportParam = outputParams?[XPSDKConnectionRequestStreamParamSupportsAviExports]
            conn.supportsAviExports = (exportParam == nil) || (exportParam as! String == XPSDKConnectionRequestStreamParamSupportsValueYes)
            exportParam = outputParams?[XPSDKConnectionRequestStreamParamSupportsJpegExports]
            conn.supportsJpegExports = (exportParam == nil) || (exportParam as! String == XPSDKConnectionRequestStreamParamSupportsValueYes)
            let originalSizeParam = outputParams?[XPSDKConnectionRequestStreamParamOriginalSize]
            conn.originalSizeRequested = (originalSizeParam == nil) || (originalSizeParam as! String == XPSDKConnectionRequestStreamParamSupportsValueYes)         
            videoConnection = conn
        }
        return videoConnection
    }
    
    //MARK: Close Stream
    
    public func closeVideoConnection(videoConnection: XPSDKVideoConnection, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        videoConnection.close()
        if (videoConnection.method == XPSDKVideoConnectionMethodPush || self.state != XPSDKConnectionState.working) {
            self.cleanupAfterClosedConnection(videoConnection: videoConnection)
        } else {
            let request = XPSDKRequest(forConnection: self)
            request.commandName = XPSDKConnectionRequestNameCloseStream
            
            let params: [String : String] = [XPSDKConnectionCloseStreamParamVideoID : videoConnection.videoID as String]
            
            request.parameters = params
            
            request.successHandler = { (response) in
                self.cleanupAfterClosedConnection(videoConnection: videoConnection)
                successHandler?(response)
            }
            
            request.failureHandler = { (error) in
                failureHandler?(error)
            }
            
            request.execute()
        }
    }
    
    private func cleanupAfterClosedConnection(videoConnection: XPSDKVideoConnection) {
        self.videoConnections.remove(videoConnection)
    }
    
    public func purgeVideoConnections() {
        self.requests.removeAllObjects()
        for conn in self.videoConnections {
            (conn as! XPSDKVideoConnection).close()
        }
        self.videoConnections.removeAllObjects()
    }
    
    //MARK: Change stream
    
    public func changeStream(forVideoID videoID: String, fps: Int, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        log.info("Change stream for video with id:\(videoID) with fps: \(fps)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionRequestStreamParamFPS: String(fps), XPSDKConnectionChangeStreamParamVideoID : videoID]
        
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
    }
    
    public func changeStream(forVideoID videoID: String, size: CGSize, sourceRect: CGRect, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        log.info("Change stream for video with id:\(videoID) with width: \(size.width) and height \(size.height) and source rectangle :\(sourceRect) ")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID: videoID,
                                        XPSDKConnectionRequestStreamParamDestWidth : "\(size.width)",
            XPSDKConnectionRequestStreamParamDestWidth : "\(size.height)",
            XPSDKConnectionRequestStreamParamSrcLeft : "\(sourceRect.origin.x)",
            XPSDKConnectionRequestStreamParamSrcRight : "\(lround(Double(sourceRect.origin.x + sourceRect.size.width)))",
            XPSDKConnectionRequestStreamParamSrcTop : "\(sourceRect.origin.x)",
            XPSDKConnectionRequestStreamParamSrcBottom : "\(lround(Double(sourceRect.origin.y + sourceRect.size.height)))"
        ]
        
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
    }
    
    public func changeStream(forVideoID videoID: String, playSpeed: Float, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        log.info("Change stream for video with id:\(videoID) with playspeed: \(playSpeed)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamSpeed: String(playSpeed), XPSDKConnectionChangeStreamParamVideoID : videoID]
        
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
    }
    
    
    public func previousSequence(forVideoID videoID: String, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        
        log.info("Previous sequence for video with id:\(videoID)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID : videoID,
                                        XPSDKConnectionChangeStreamParamSeekType : XPSDKConnectionChangeStreamParamSeekTypePrevSeq]
        
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
    }
    
    
    public func nextSequence(forVideoID videoID: String, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        log.info("Next sequence for video with id:\(videoID)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID : videoID,
                                        XPSDKConnectionChangeStreamParamSeekType : XPSDKConnectionChangeStreamParamSeekTypeNextSeq]
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
        
    }
    
    public func previousFrame(forVideoID videoID: String, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        log.info("Previous frame for video with id:\(videoID)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID : videoID,
                                        XPSDKConnectionChangeStreamParamSeekType : XPSDKConnectionChangeStreamParamSeekTypePrevFrame]
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
        
    }
    
    public func nextFrame(forVideoID videoID: String, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        log.info("Next frame for video with id:\(videoID)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID : videoID,
                                        XPSDKConnectionChangeStreamParamSeekType : XPSDKConnectionChangeStreamParamSeekTypeNextFrame]
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
    }
    
    public func seekToTimestamp(forVideoID videoID: String, timestamp:NSNumber, seekType: String, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        
        log.info("Seek to timestamp: \(timestamp) for video with id:\(videoID) for seek type \(seekType)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID : videoID,
                                        XPSDKConnectionChangeStreamParamSeekType : seekType,
                                        XPSDKConnectionChangeStreamParamTime : "\(timestamp)"]
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
        
    }
    
    public func seekToTime(forVideoID videoID: String, time:NSDate, seekType: String, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        log.info("Seek to time: \(time) for video with id:\(videoID) for seek type \(seekType)")
        
        let timestamp = NSNumber(value: llrint(time.timeIntervalSince1970 * 1000))
        
        return seekToTimestamp(forVideoID: videoID, timestamp: timestamp, seekType: XPSDKConnectionChangeStreamParamTime, successHandler: successHandler, failureHandler: failureHandler)
    }
    
    public func moveCamera(forVideoID videoID: String, ptzMotion: XPSDKPTZMotion, successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        
        log.info("Move camera for video with id:\(videoID) with ptz motion: \(ptzMotion)")
        
        var motionName = ""
        switch ptzMotion {
        case .XPSDKPTZMotionUp:
            motionName = "Up"
            break
        case .XPSDKPTZMotionDown:
            motionName = "Down"
            break
        case .XPSDKPTZMotionLeft:
            motionName = "Left"
            break
        case .XPSDKPTZMotionRight:
            motionName = "Right"
            break
        case .XPSDKPTZMotionZoomIn:
            motionName = "ZoomIn"
            break
        case .XPSDKPTZMotionZoomOut:
            motionName = "ZoomOut"
            break
        case .XPSDKPTZMotionHome:
            motionName = "Home"
            break
        }
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID : videoID,
                                        XPSDKConnectionChangeStreamParamPtzMove : motionName
        ]
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
    }
    
    public func moveCamera(forVideoID videoID: String, ptzPreset:XPSDKCameraPTZPreset,  successHandler:SuccessResponse?, failureHandler: FailureBlock?) {
        
        log.info("Move camera for video with id:\(videoID) with ptz preset: \(ptzPreset.displayName)")
        
        let request = XPSDKRequest(forConnection: self)
        
        request.commandName = XPSDKConnectionRequestNameChangeStream
        
        let params: [String: String] = [XPSDKConnectionChangeStreamParamVideoID : videoID,
                                        XPSDKConnectionChangeStreamParamPtzPreset : ptzPreset.displayName]
        request.parameters = params
        
        request.successHandler = { (response) in
            successHandler?(response)
        }
        
        request.failureHandler = { (error) in
            failureHandler?(error)
        }
        
        request.execute()
        
    }
    
}


