//
//  SocketServiceHandler.m
//  OneCable
//
//  Created by KaRam Kim on 2017. 2. 17..
//  Copyright © 2017년 ntels. All rights reserved.
//

#import "SocketServiceHandler.h"
#import "Definitions.h"
#import "JDJSONModel.h"
#import "JDObject.h"


#define MAX_RETRY_CONNECT_COUNT 1
#define MAX_REQUEST_ID 255
#define NONE_DELEGATE_REQUEST_ID -99

@interface SocketServiceHandler() <SRWebSocketDelegate>
{
    SRWebSocket *_socket;
    NSMutableArray<SocketRequestModel *> *_messages;
//    NSMutableArray *_delegates;
    
    int _retryCount;
    BOOL _isShowLoading;
    int _loadingRequestId;
}
@property NSMutableDictionary* controllers;
@property NSMutableDictionary* responses;
@property int   controllerId;

@end

@implementation SocketServiceHandler

+ (id)sharedManager
{
    static SocketServiceHandler *sharedSocketServiceHandler = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedSocketServiceHandler = [[self alloc] init];
    });
    return sharedSocketServiceHandler;
}

- (id) init
{
    self = [super init];
    
    if (self) {
        
        _isConnected = NO;

        
        _messages = [[NSMutableArray alloc] init];
//        _delegates = [[NSMutableArray alloc] init];
        
        _controllers = [[NSMutableDictionary alloc] init];
        _responses = [[NSMutableDictionary alloc] init];
        
        self.controllerId   = 0;
        
//        [self socketOpen];
    }
    
    return self;
}

//- (void) initDelegate:(id<SocketServiceDelegate>) delegate
//{
//    [self setDelegate:delegate];
//}


- (SRReadyState) getSocketStatus
{
    return _socket.readyState;
}

- (void) socketOpen
{
    NSLog(@"socketOpen");
    
    _strSocketURL = [NSString stringWithFormat:@"%@%@", kSocketServer, API_ROOT_PATH];
    NSLog(@"socketURL : %@", _strSocketURL);
    
    if( _socket != nil )
    {
        [_socket open];
    }
    else
    {
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:_strSocketURL]];
        if([[JDFacade facade].loginUser authorization]!=nil)
        {
            [request setValue:[[JDFacade facade].loginUser authorization] forHTTPHeaderField:API_AUTHORIZATION_KEY];
        }
        NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
        
#ifdef DEBUG_MODE
        language = @"ko";
#endif
        [request setValue:language forHTTPHeaderField:API_LANGUAGE_KEY];
        
        NSLog(@"All Header Info : %@", request.allHTTPHeaderFields);
        _socket = [[SRWebSocket alloc] initWithURLRequest:request];
        
        _socket.delegate = self;
        [_socket open];
    }
}

- (void) open
{
    if( !_isConnected )
    {
        if( _socket.readyState == SR_OPEN )
        {
            _isConnected = YES;
        }
        else if ( _socket.readyState == SR_CLOSED || _socket.readyState == SR_CLOSING )
        {
            [self close];
             _socket = nil;
            [self socketOpen];
        }
    }
}
- (void) close
{
    if( _isConnected )
    {
        [_socket close];
        _socket = nil;
    }
    else
    {
        if (_socket != nil)
        {
            _socket = nil;
        }
    }
    _isConnected = NO;
}


-(void)sendRequest {
    _isShowLoading = YES;
    [[JDFacade facade] loadIndicator:YES allowUserInteraction:NO];
}

-(void)fisnishRequest {
    _isShowLoading = NO;
    [[JDFacade facade] loadIndicator:NO allowUserInteraction:YES];
}

- (void) sendData:(SocketRequestModel *)data
{
    if(_socket.readyState == SR_OPEN)
    {
        NSLog(@"Socket Ready Send Data");
        NSLog(@"Send Data : %@", data);
        //NSLog(@"Send Param : %@", data.getSendMessage);
        [_socket send:data.getSendMessage];
    }
    else if ( _socket.readyState == SR_CLOSED || _socket.readyState == SR_CLOSING )
    {
        NSLog(@"Socket State is NOT Ready, Retry Process");
        [_messages addObject:data];
        [self reConnect];
    }
    else
    {
        NSLog(@"Socket NOT Ready, Retry Process");
        [_messages addObject:data];
        if(!_isConnected)
        {
            [self socketOpen];
        }
    }

}

- (void) sendDataWihOutDelegate:(SocketRequestModel *)data
{
    [self sendDataWithDelegate:data modelClass:nil delegate:nil];
}

- (void) sendDataWithDelegate:(SocketRequestModel *)data delegate:(id)delegate
{
    [self sendDataWithDelegate:data modelClass:nil delegate:delegate];
}

- (void) sendDataWithDelegate:(SocketRequestModel *)data modelClass:(Class)modelClass delegate:(id)delegate
{
    
    [self sendDataWithDelegate:data modelClass:modelClass delegate:delegate isShowLoading:NO];
}


- (void) sendDataWithDelegate:(SocketRequestModel *)data modelClass:(Class)modelClass delegate:(id)delegate isShowLoading:(BOOL)isShowLoading
{
    short controllerId = NONE_DELEGATE_REQUEST_ID;
    
    if (delegate) {
        controllerId = self.controllerId++;
        if (self.controllerId >= INT16_MAX) {
            self.controllerId = 0;
        }
    }
    
    //NSLog(@"Request Info : %@", data);
    data.requestId = [NSString stringWithFormat:@"%d", controllerId];
    //NSLog(@"Request ID : %@", data.requestId);
    
    if (delegate) {
        [_controllers setObject:delegate forKey:[NSNumber numberWithInt:controllerId]];
    }
    
    if (modelClass) {
        [_responses setObject:modelClass forKey:[NSNumber numberWithInt:controllerId]];
    }
    
    if (controllerId!=NONE_DELEGATE_REQUEST_ID && isShowLoading) {
        _loadingRequestId = controllerId;
        [self sendRequest];
    }
    
    [self sendData:data];
}

- (void)setDelegateController:(id)delegate {
    
    short controllerId = NONE_DELEGATE_REQUEST_ID;
    
    if (delegate) {
        
        controllerId = self.controllerId++;
        if (self.controllerId >= INT16_MAX) {
            self.controllerId = 0;
        }
    }
    
    if (delegate) {
        [_controllers setObject:delegate forKey:[NSNumber numberWithInt:controllerId]];
    }
}

- (void)removeDelegate:(id)delegate {
//    [_delegates removeObject:delegate];
}

-(void) reConnect
{
    if(_retryCount < MAX_RETRY_CONNECT_COUNT)
    {
        _retryCount++;
        [self close];
        _socket = nil;
        [self socketOpen];
    }
    else
    {
        [self close];
    }
}

#pragma mark - SRWebSocketDelegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket
{
    NSLog(@"SOCKET didOpen");
    _retryCount = 0;
    // Open이 되지 않은 상태에서 전송시도한 메세지들을 발송한다.
    for (SocketRequestModel *data in _messages)
    {
        [self sendData:data];
        [_messages removeObject:data];
    }
    
//    for (id controller in _delegates) {
//        if (controller && [controller respondsToSelector:@selector(socketConnectComplete)]) {
//            [controller socketConnectComplete];
//        }
//    }
    
    
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
{
    NSLog(@"SOCKET didReceiveMessage : %@", message);

    NSString *errString = @"";
    
    if( message == nil || [message isEqualToString:@""] )
        errString = @"message empty";
    else
    {
        id JSONModel = nil;
        NSError *error = nil;
        NSString *JSONString = [NSString stringWithUTF8Data:[message dataUsingEncoding:NSUTF8StringEncoding]];
//        NSDictionary *jsonObj = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding]
//                                                     options:NSJSONReadingAllowFragments
//                                                       error:&error];
//        
        //NSLog(@"Json String : %@", JSONString);
        
        if (error != nil) {
            NSLog(@"NSJSONSerialization JSONObject error: %@", [error localizedDescription]);
            return;
        }

        SocketModel *response = [[SocketModel alloc] initWithString:JSONString error:&error];
        

        id controller = nil;
        Class modelClass = nil;
        
        if (response.requestId != nil && ![response.requestId isEmptyString]) {
            NSNumber* controllerId = [NSNumber numberWithInteger:[response.requestId integerValue]];
            
            if ([controllerId integerValue] == _loadingRequestId) {
                _loadingRequestId = -999;
                [self fisnishRequest];
            }
            
            controller = [self.controllers objectForKey:controllerId];
            modelClass = [self.responses objectForKey:controllerId];
        }
  
        id rawJSON = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding]
                                                     options:NSJSONReadingAllowFragments
                                                       error:&error];
        if (modelClass) {
            if ([rawJSON isKindOfClass:[NSArray class]]) {
                NSDictionary *JSONDic = @{@"list": rawJSON};
                JSONModel = [[modelClass alloc] initWithDictionary:JSONDic error:&error];
            }
            else {
                JSONModel = [[modelClass alloc] initWithString:JSONString error:&error];
            }
                
        } else {
            JSONModel = rawJSON;
        }
        
        NSLog(@"JSONModel : %@", JSONModel);
        
        //0622 공통 메세징 처리, 기존 메세징 처리 주석 LoginViewController,ThingsDetailViewController
        if (!response.isSuccess) [self handlingSocketErrors:response];

        
        if (!controller) {
            
            if (self.controllers.count > 0) {
                NSArray* array = self.controllers.allKeys;
                
                for (NSNumber *key in array) {
                    
                    
                    id controller  = [self.controllers objectForKey:key];
                    
                    if ([controller respondsToSelector:@selector(socketDidReceiveMessage:result:)]) {
                        [controller socketDidReceiveMessage:JSONModel result:response];
                    }
                }
            }
            
            [[NSNotificationCenter defaultCenter] postNotificationName:kSocketBoradCast object:JSONModel];
        }
        else {
            
            if ([controller respondsToSelector:@selector(socketDidReceiveMessage:result:)]) {
                [controller socketDidReceiveMessage:JSONModel result:response];
            }
            
            [self.controllers removeObjectForKey:[NSNumber numberWithInteger:[response.requestId integerValue]]];
            
//            if (self.controllers.count > 1) {
//                NSArray* array = self.controllers.allKeys;
//                NSArray* sortedArray = [array sortedArrayUsingSelector:@selector(compare:)];
//                
//                [self.controllers removeObjectForKey:[sortedArray objectAtIndex:0]];
//            }
        }
        
        
        NSLog(@"[socket service didReceiveMessage]");
        // TODO : 전달받은 데이터로 자동으로 처리할 내용이 있으면 이곳에서 처리할것
        // 디바이스 정보인 경우 로컬 정보를 업데이트
        if (response != nil && EQUALS(response.messageType, MSG_TYPE_DEVICE_CONTENT)) {
            
            [self updateDeviceContentWithJSONModel:JSONModel];
        }
        else if(response != nil && EQUALS(response.messageType, MSG_TYPE_DEVICE_CONN)) {
            
            [self updateDeviceConnectionJSONModel:JSONModel];
        }
        
    }
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error
{
    NSLog(@"WebSocket error: %@", error);
    
    _isConnected = NO;
    [self reConnect];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload
{
    NSLog(@"WebSocket Receive Pong: %@", pongPayload);
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean
{
    NSLog(@"WebSocket closed with code: %ld reason:%@ wasClean:%d",
          (long)code, reason, wasClean);
    
    _isConnected = NO;
    [self reConnect];
}


#pragma mark - socket 통신 후 공통처리 
//device.content 정보 loginUser 에 반영
- (void)updateDeviceContentWithJSONModel:(id)jsonModel  {
    
    ContentDeviceModel *result = [[ContentDeviceModel alloc] initWithDictionary:jsonModel error:nil];
    
    //    NSLog(@"updateDeviceContentWithJSONModel\nresult : %@", result);
    
    NSMutableArray *deviceList = [[JDFacade facade].loginUser.deviceList mutableCopy];
    
    for (NSInteger i=0 ; i<deviceList.count ; i++) {
        
        DeviceModel *deviceModel = [deviceList objectAtIndex:i];
        
        if (EQUALS(deviceModel.deviceId, result.deviceId)) {
            
            DeviceModel *newDeviceModel = [deviceModel copy];
            
            BOOL is = result.contentValue != nil &&
            EQUALS([result.contentValue objectForKey:@"online"], @"ON");
            
            //            NSLog(@"online : %@", [result.contentValue objectForKey:@"online"]);
            //            NSLog(@"online : %d", result.contentValue != nil &&
            //                  EQUALS([result.contentValue objectForKey:@"online"], @"ON"));
            
            //newDeviceModel.isDeviceOnlined = is;
            newDeviceModel.deviceOnline = is ? @"ON" : @"OFF";
            [deviceList replaceObjectAtIndex:i withObject:newDeviceModel];
            
            [[JDFacade facade].loginUser setDeviceList:deviceList];
        }
    }
    
    //NSLog(@"[socket service result]\n%@",[JDFacade facade].loginUser.deviceList);
    
}


//device.conn 정보 loginUser 에 반영
- (void)updateDeviceConnectionJSONModel:(id)jsonModel  {
    
    DeviceConnectionModel *result = [[DeviceConnectionModel alloc] initWithDictionary:jsonModel error:nil];
    
    NSMutableArray *deviceList = [[JDFacade facade].loginUser.deviceList mutableCopy];
    
    for (NSInteger i=0 ; i<deviceList.count ; i++) {
        
        DeviceModel *deviceModel = [deviceList objectAtIndex:i];
        
        if (EQUALS(deviceModel.deviceId, result.deviceId)) {
            
            DeviceModel *newDeviceModel = [deviceModel copy];
            
            newDeviceModel.deviceConn = result.deviceConn;
            newDeviceModel.deviceConnLastDatetime = result.deviceConnLastDatetime;
            
            [deviceList replaceObjectAtIndex:i withObject:newDeviceModel];
            [[JDFacade facade].loginUser setDeviceList:deviceList];
        }
    }
}

//socket통신 에러 71 login first 로그인으로 이동시킴
- (void)handlingSocketErrors:(SocketModel*)response {
    
    BOOL isUpdateResult = [response.messageType isEqualToString:MSG_TYPE_NODE_PROFILE_UPDATE_RES];
    BOOL isLogin = [response.messageType isEqualToString:MSG_TYPE_LOGIN];
    BOOL isAutoLogin = [response.messageType isEqualToString:MSG_TYPE_AUTO_LOGIN];
    BOOL isDuplicateID = [response.messageType isEqualToString:MSG_TYPE_DUPLICATE_ID];
    
    
    //화면에서 따로 처리해야 하는 경우
    if (isUpdateResult || isLogin || isAutoLogin)
        return;
    
    
    
    BOOL online = YES;
    DeviceModel *deviceModel = [[JDFacade facade].loginUser getHomeHub];
    if (deviceModel != nil && !deviceModel.isDeviceOnlined) online = NO;

    //중복로그인
    if (isDuplicateID) {
        
        [[JDFacade facade] alert:@"다른 기기에서 동일한 계정으로 접속하여 로그아웃되었습니다." completionHander:^{
            
            [[JDFacade facade] logout];
        }];
    }
    //게이트웨이 에러
    else if ((EQUALS(response.resultCode, @"81") ||
         EQUALS(response.resultCode, @"82") ||
         EQUALS(response.resultCode, @"83")) && !online) {
        
        [[JDFacade facade] alert:@"게이트웨이를 확인해주세요." completionHander:^{
            
            UIViewController *vc = [CommonUtil instantiateViewControllerWithIdentifier:@"MainViewController" storyboardName:@"Main"];
            
            [JDFacade facade].mainViewController = (MainViewController *)vc;
            [[JDFacade facade].appDelegate.window setRootViewController:vc];

        }];
    }
    //공통 에러 메세지
    else if (response.resultMsg != nil){
        
        [[JDFacade facade] alert:response.resultMsg completionHander:^{
            
            //로그인으로 이동
            if (EQUALS(response.resultCode, @"71")) {
                [[JDFacade facade] logout];
            }

            
        }];
    }
    

}

@end
