//
//  XPSDKVideoConnection.h
//  XProtect
//
//  Created by Aleksandra Spirovska on 11/29/17.
//  Copyright © 2017 Milestone Systems A/S. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CFNetwork/CFNetwork.h>
#import "XPSDKVideoConnectionFrame.h"
#import "XPSDKVideoConnectionSegment.h"
#import "XPSDKVideoConnection.h"

@class XPSDKConnection;
@class XPSDKResponse;
@protocol XPSDKVideoConnectionDelegate;

/*!
 Video connection signal type - live or playback
 */
typedef enum {
    XPSDKVideoConnectionSignalLive,    /// Live video stream from a camera
    XPSDKVideoConnectionSignalPlayback, /// Playback recordings from a recording sserver
    XPSDKVideoConnectionSignalUpload   /// Request upstream connection
} XPSDKVideoConnectionSignal;


/*!
 The video connection class handles the entire delivery of frames from the server to the client. It is not responsible for sending any commands to the server - opening, closing, pausing and etc of a video stream is handled by the XPVideoConnection class.
 
 A video connection basically is a stream of frames from the server to the app. Each frame has a header with some optional fields and optional image (JPEG for the moment). The header may contain information about recording state, motion state, information about changes in the image, changes in the playback state and etc.
 
 A video connection should operate with a delegate. It receives the events and images from the video connection and processes them in any way it wants - display them, change app state and etc.
 
 The implementation of the video connection uses low-level streams & sockets instead of NSURLConnection. The reason is a limitation in the iOS - we cannot keep more than 6 opened connections to the same protocol/host/port. Which means if we start up to 6 cameras and we won't be able to send commands over the meta connection. Extra requests are being queued. Using sockets goes around that limitation, because it appears to be built in the NSURLConnection class.
 
 The video connection class has very rudimentary support for parsing HTTP headers - just enough to detect if there is HTTP error from the server.
 
 The video connection also supports restoring after broken data stream. When a frame is parsed its header is analyzed and if the video ID and other values in the header structure are wrong it enters a special "self repare" mode. When in that mode the incomming data is constantly analyzed to attempt to locate the next frame start. If is is found the class attempts to construct complete frame from that location in the data. If sucessfull resumes normal operation and notifies the delegate with the frame. All earlier that is simply discarded.
 */
@interface XPSDKDownstreamVideoConnection : XPSDKVideoConnection <NSStreamDelegate> {
@private
    
    XPSDKVideoConnectionMethod method;
    
    XPSDKVideoConnectionSignal signal;
    
    NSString * serverAddress;
    NSUInteger serverPort;
    NSInputStream * inputStream; /// Data comming from the server
    NSOutputStream * outputStream; /// Data we are sending to the server
    uint8_t * buffer; /// Raw incomming data buffer
    
    NSMutableData * responseData; /// Incomming data buffer
    
    CGSize sourceSize; /// Camera resolution
    
    XPSDKViewID cameraID; //todo: figure where xpviewid will be exported
    
    BOOL usesTranscoding;

    BOOL httpHeaderParsed; /// Flag - set to YES if the connection has received all HTTP headers and should begin searchign for frames
    
    BOOL receivedResponseSentToDelegate; /// Flag, used by Pull mode connection - set to yes if the delegate has been informed of a received response (videoConnection:receivedResponse: callback)
    
    NSMutableArray * headerLines; /// Keeps all http headers of the response 
    
    NSData * searchForUUID; /// Used in case of broken data stream - keeps the video ID as NSData so we can search for in the incomming byte stream and possibly locate the next frame start
}

@property (nonatomic, assign) CGSize sourceSize; /// Resolution of the camera. It is the delegate's responsibility to update this property on resolution change!
@property (nonatomic, readonly) XPSDKViewID cameraID; /// ID of the camera the connection has been initiated to
@property (nonatomic, readonly) XPSDKVideoConnectionSignal signal;

@property (nonatomic, assign) BOOL supportsLive; /// Set by the main connection when creating the object. Don't change it.
@property (nonatomic, assign) BOOL supportsPlayback; /// Set by the main connection when creating the object. Don't change it.
@property (nonatomic, assign) BOOL supportsPTZ; /// Set by the main connection when creating the object. Don't change it.
@property (nonatomic, assign) BOOL supportsPTZPresets; /// Set by the main connection when creating the object. Don't change it.
@property (nonatomic, assign) BOOL supportsSequences; /// Set by the main connection when creating the object. Don't change it.
@property (nonatomic, assign) BOOL supportsAviExports; /// Set by the main connection when creating the object. Don't change it.
@property (nonatomic, assign) BOOL supportsJpegExports; /// Set by the main connection when creating the object. Don't change it.
@property (nonatomic, assign) BOOL originalSizeRequested; /// Set by the main connection when creating the object. Don't change it.

@property (nonatomic, readonly) NSString * serverAddress; /// Server address
@property (nonatomic, readonly) NSUInteger serverPort; ///Server port

/*!
 Designated initializer. Instances should only ne created by the main connection!
 @param mainConnection the main connection from which the video connection is derived.
 @param camid ID of the camera
 @param vid Video ID
 @param connectionMethod
 @param signalType
 @param size The resolution of the camera
 */
- (id) initFromControlConnection:(XPSDKConnection *) mainConnection
                        cameraID:(XPSDKViewID) camid
                         videoID:(XPSDKVideoID) vid
                          method:(XPSDKVideoConnectionMethod) connectionMethod
                          signal:(XPSDKVideoConnectionSignal) signalType
                      sourceSize:(CGSize) size
                 usesTranscoding:(BOOL)transcoding;


/*!
 Designated initializer. Instances should be only created by the main connection!
 @param url server url
 @param camid ID of the camera
 @param vid Video ID
 @param connectionMethod
 @param signalType
 @param size The resolution of the camera
 */
- (id) initWithServerURL:(NSURL *) serverURL
                cameraID:(XPSDKViewID) camid
                 videoID:(XPSDKVideoID) vid
                  method:(XPSDKVideoConnectionMethod) connectionMethod
                  signal:(XPSDKVideoConnectionSignal) signalType
              sourceSize:(CGSize) size
         usesTranscoding:(BOOL)transcoding;

/*!
 Opens the connection to the video server
 */
- (void) open;

/*!
 Allows changing the desired fps
 */
- (void) setDesiredFps:(NSInteger)fps withSuccessHandler:(void(^)(XPSDKResponse *))successHandler andFailureHandler:(void(^)(NSError *))failureHandler;

@end



/*!
 Video connection delegate protocol. Delegates of XPVideoConnection must implement this protocol to receive notifications form the video connection - new images, recording/motion/playback state changes, etc
 */
@protocol XPSDKVideoConnectionDelegate <NSObject>
@required
/*!
 Notifies the delegate that the connection has received HTTP response upon opening the connection. Push mode video connections only call this method once - on the first request.
 @param headers List of headers received form the server
 */
- (void) videoConnection: (XPSDKDownstreamVideoConnection *) videoConnection
        receivedResponse: (NSArray *) headers;

/*!
 Notifies the delegate that the connection has received new frame. This is called even for frames that don't contain images. The reason is that the header may contain just recording/motion/playback events
 */
- (void) videoConnection: (XPSDKDownstreamVideoConnection *) videoConnection
           receivedFrame: (XPSDKVideoConnectionFrame *) frame;

/*!
 Notifies the delegate that the connection has received new playlist.
 */
- (void) videoConnection: (XPSDKDownstreamVideoConnection *) videoConnection
        receivedPlaylist: (XPSDKVideoConnectionSegment *) segment;

/*!
 Notifies the delegate of a connection interruption. Usually network error
 */
- (void) videoConnection: (XPSDKDownstreamVideoConnection *) videoConnection failedWithError:(NSError *) error;

/*!
 Notifies the delegate the video connection has been closed.
 */
- (void) videoConnectionFinished: (XPSDKDownstreamVideoConnection *) videoConnection;

/*!
 Use this when you only have received headers, but not something with them
 */
- (void) videoConnection: (XPSDKDownstreamVideoConnection *) videoConnection
 receivedHeadersForFrame: (XPSDKVideoConnectionFrame *)frame;

@end

