//
//  WSSesseonController.m
//  WifiPrint
//
//  Created by woosimsystems on 2015. 5. 19..
//  Copyright (c) 2015년 woosimsystems. All rights reserved.
//

#import "WSSessionController.h"

NSString *WSDataReceivedNotification = @"WSDataReceivedNotification";
NSString *socketDidConnectNotification = @"socketDidConnectNotification";
NSString *socketDidDisconnectNotification = @"socketDidDisconnectNotification";


@implementation WSSessionController

@synthesize hostIP = _hostIP;
@synthesize port = _port;

NSInputStream *inputStream = nil;
NSOutputStream *outputStream = nil;

enum STATE {
    READY = 1,
    CONNECTING,
    CONNECTED,
    CLOSING,
    CLOSED = READY
};

int com_state = READY;


+ (WSSessionController *)sharedController
{
    static WSSessionController *sessionController = nil;
    
    if (sessionController == nil) {
        sessionController = [[WSSessionController alloc] init];
    }
    
    return sessionController;
}

- (void)dealloc
{
    [self closeSession];
    
}

// initialize WiFi network with host IP and port number
- (void)setupControllerForNetwork:(NSString *)IPString withPort:(NSString *)portString
{
    
    _hostIP = [IPString copy];
    _port = [portString integerValue];

}

// open a session with the ip and port, and set up the input and output stream on the default run loop
- (BOOL)openSession
{
    com_state = READY;
    CFReadStreamRef   readStream = NULL;
    CFWriteStreamRef  writeStream = NULL;
    
    if (_hostIP == nil ) {
        NSLog(@"Invalid Host IP");
        return NO;
    }
    if ((_port < 0) || (_port > 65535)) {
        NSLog(@"Invalid port number");
        return NO;
    }
    
    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)(_hostIP), (unsigned int)_port, &readStream, &writeStream);
    
    if ((readStream == NULL) || (writeStream == NULL)) {
        NSLog(@"CFStream creation failure");
        return NO;
    }

    inputStream =  (__bridge NSInputStream *)readStream;
    outputStream = (__bridge NSOutputStream *)writeStream;
    
    CFRelease(readStream);
    CFRelease(writeStream);
    
    [inputStream setDelegate:self];
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream open];
    
    [outputStream setDelegate:self];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream open];
    
   
    if(([inputStream streamStatus] == NSStreamStatusError) && ([outputStream streamStatus] == NSStreamStatusError))
    {
        return NO;
    }
    
    return YES;
}

-(void)connectingCheckCallback
{
    
}

// close the session with the accessory.
- (void)closeSession
{
    com_state = CLOSING;
    if (inputStream != nil) {
        [inputStream close];
        [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [inputStream setDelegate:nil];
        inputStream = nil;
    }
    
    if (outputStream != nil) {
        [outputStream close];
        [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [outputStream setDelegate:nil];
        outputStream = nil;
    }
    
    if ([inputStream streamStatus] == NSStreamStatusNotOpen && [outputStream streamStatus] == NSStreamStatusNotOpen)
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:socketDidDisconnectNotification object:self];
    }
    
    _outData = nil;
    _inData = nil;
    com_state = CLOSED;
}

// write data to instance variable
- (void)writeData:(NSData *)data
{
    if (_outData == nil) {
        _outData = [[NSMutableData alloc] init];
    }
    
    [_outData appendData:data];
    [self _writeDataToStream];
}

// read data from instance variable
- (NSData *)readData:(NSUInteger)bytesToRead
{
    NSData *data = nil;
    
    if ([_inData length] >= bytesToRead) {
        // copy the receiver’s bytes that fall within the limits specified by a given range
        NSRange range = NSMakeRange(0, bytesToRead);
        data = [_inData subdataWithRange:range];
        
        // delete bytes in the receiver in the range
        [_inData replaceBytesInRange:range withBytes:NULL length:0];
    }
    
    return data;
}

// get number of bytes read into local buffer
- (NSUInteger)getAvailableReadBytes
{
    return [_inData length];
}


#pragma mark - NSStream delegate event

// asynchronous NSStream handleEvent method
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventNone:
            break;
            
        case NSStreamEventOpenCompleted:
            
            if (com_state != CONNECTED){
                
                [[NSNotificationCenter defaultCenter] postNotificationName:socketDidConnectNotification object:self];
            }
            com_state = CONNECTED;
            break;
            
        case NSStreamEventHasBytesAvailable:
            [self _readDataFromStream];
            break;
            
        case NSStreamEventHasSpaceAvailable:
            [self _writeDataToStream];
            break;
            
        case NSStreamEventErrorOccurred:
            [[NSNotificationCenter defaultCenter] postNotificationName:socketDidDisconnectNotification object:self];
            break;
            
        case NSStreamEventEndEncountered:
            break;
            
        default:
            break;
    }
}


#pragma mark Internal

// internal write method - write data to the accessory while there is space available and data to write
- (void)_writeDataToStream
{
    while (([outputStream hasSpaceAvailable]) && ([_outData length] > 0))
    {
        // Writes the contents of a provided data buffer to the receiver
        NSInteger bytesWritten = [outputStream write:[_outData bytes] maxLength:[_outData length]];
        
        if (bytesWritten == -1)
        {
            NSLog(@"write error");
            break;
        }
        else if (bytesWritten > 0)
        {            
            // delete bytes in the receiver in the range (0, bytesWritten)
            [_outData replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
        }
    }
}

// internal read method - read data while there is data and space available in the input buffer
- (void)_readDataFromStream
{
#define INPUT_BUFFER_SIZE 256
    
    uint8_t buffer[INPUT_BUFFER_SIZE];
    while ([inputStream hasBytesAvailable])
    {
        NSInteger bytesRead = [inputStream read:buffer maxLength:INPUT_BUFFER_SIZE];
        NSLog(@"read %ld bytes from input stream", (long)bytesRead);
        
        if (_inData == nil) {
            _inData = [[NSMutableData alloc] init];
        }
        
        [_inData appendBytes:(void *)buffer length:bytesRead];
    }
    
    [[NSNotificationCenter defaultCenter] postNotificationName:WSDataReceivedNotification object:self];
}


@end
