//
//  SocketController.m
//  wifiprint
//
//  Created by woosimsystems on 2021/05/21.
//  Copyright © 2021 Woosim Systems. All rights reserved.
//

#import "SocketController.h"

@interface SocketController()

@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, strong) NSMutableData *outData;
@property (nonatomic, strong) NSMutableData *inData;

@end

NSString *WSSocketDidConnectNotification = @"WSSocketConnect";
NSString *WSSocketDidDisconnectNotification = @"WSSocketDisconnect";
NSString *WSSocketDataReceivedNotification = @"WSSocketDataReceived";

@implementation SocketController

- (BOOL)connect:(NSString *)host withPort:(NSInteger)port
{
    _host = [host copy];
    _port = port;
    
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)(_host), (unsigned int)_port, &readStream, &writeStream);
    
    if ((readStream == NULL) || (writeStream == NULL)) {
        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];

    return YES;
}

- (void)disconnect
{
    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;
    }
    _outData = nil;
    _inData = nil;
    [[NSNotificationCenter defaultCenter] postNotificationName:WSSocketDidDisconnectNotification object:nil];
}

- (void)writeData:(NSData *)data
{
    if (_outputStream == nil) {
        return;
    }
    if (_outData == nil) {
        _outData = [[NSMutableData alloc] init];
    }
    [_outData appendData:data];
    [self writeStream];
}

- (void)writeStream
{
    while (([_outputStream hasSpaceAvailable]) && ([_outData length] > 0))
    {
        NSInteger bytesWritten = [_outputStream write:[_outData bytes] maxLength:[_outData length]];
        if (bytesWritten == -1) {
            NSLog(@"write error");
            break;
        } else if (bytesWritten > 0) {
            [_outData replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
        }
    }
}

- (NSData *)readData:(NSUInteger)bytesToRead
{
    NSData *data = nil;
    if ([_inData length] >= bytesToRead) {
        NSRange range = NSMakeRange(0, bytesToRead);
        data = [_inData subdataWithRange:range];
        [_inData replaceBytesInRange:range withBytes:NULL length:0];
    }
    return data;
}

- (NSUInteger)readBytesAvailable
{
    return [_inData length];
}

#pragma mark - NSStream delegate

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            if (stream == _outputStream) {
                [[NSNotificationCenter defaultCenter] postNotificationName:WSSocketDidConnectNotification object:nil];
            }
            break;
        case NSStreamEventHasBytesAvailable:
            if (stream == _inputStream) {
                [self readStream];
            }
            break;
        case NSStreamEventHasSpaceAvailable:
            if (stream == _outputStream) {
                [self writeStream];
            }
            break;
        case NSStreamEventErrorOccurred:
            [self disconnect];
            break;
        case NSStreamEventEndEncountered:
            break;
        default:
            break;
    }
}

- (void)readStream
{
    NSUInteger bufferSize = 512;
    uint8_t buffer[bufferSize];
    while ([_inputStream hasBytesAvailable]) {
        NSInteger bytesRead = [_inputStream read:buffer maxLength:bufferSize];
//        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:WSSocketDataReceivedNotification object:nil];
}

@end
