//
//  CommunicationProtocol.m
//  DUREX Vendor Control
//
//  Created by Imanol Barba on 5/23/14.
//  Copyright (c) 2014 Emmoco. All rights reserved.
//

#import "CommunicationProtocol.h"

@interface CommunicationProtocol ()

@property Boolean messageAvailableMobile;
@property Boolean messageAvailableDevice;
@property NSMutableString *message;
@property NSUInteger currentIndex;
@property NSUInteger remainingBytes;
@property NSInteger numPackets;

@end

@implementation CommunicationProtocol

+ (id)sharedProtocol
{
    static CommunicationProtocol *shared = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shared = [[self alloc] init];
        [shared setMessageAvailableDevice:FALSE];
        [shared setMessageAvailableMobile:FALSE];
    });
    return shared;
}

-(void) readMessage
{
    [self setMessage:[NSMutableString stringWithFormat:@""]];
    self.numPackets = -1;
    [self waitForMessage: 0];
}

- (void) readNextFragment
{
    __block NSUInteger numBytes;
    [[EMConnectionManager sharedManager] readResource:@"numBytes" onSuccess:^(id readValue)
    {
        numBytes = [readValue unsignedCharValue];
        NSLog(@"[CommunicationProtocol.m]: numBytes read: %d",numBytes);
        [[EMConnectionManager sharedManager] readResource:@"data" onSuccess:^(id readValue)
        {
            NSString *readData = [readValue substringToIndex:numBytes];
            if([readValue length] < numBytes)
            {
                NSLog(@"[CommunicationProtocol.m]: WARNING: Device issued wrong numBytes, possible truncated message.");
            }
            [self.message appendString:readData];
            NSLog(@"[CommunicationProtocol.m]: data read: %@",readData);
            [[EMConnectionManager sharedManager] writeValue:@"0" toResource:@"messageAvailableDevice" onSuccess:^
            {
                NSLog(@"[CommunicationProtocol.m]: messageAvailableDevice set to FALSE");
                NSLog(@"[CommunicationProtocol.m]: packet read");
                self.numPackets--;
                if(self.numPackets)
                {
                    [self waitForMessage: 0];
                }
                else
                {
                    [[self delegate] processMessage:self didFinishEnteringItem:self.message];
                }
            }onFail:^(NSError *error)
            {
                NSLog(@"[CommunicationProtocol.m]: On setMessageAvailableDevice to FALSE: %@",error);
                [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while reading answer from device",nil),[error localizedDescription]]];
            }];
        }onFail:^(NSError *error)
        {
            NSLog(@"[CommunicationProtocol.m]: On readData: %@",error);
            [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while reading answer from device",nil),[error localizedDescription]]];
        }];
    }onFail:^(NSError *error)
    {
        NSLog(@"[CommunicationProtocol.m]: On readNumBytes: %@",error);
        [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while reading answer from device",nil),[error localizedDescription]]];
    }];
}

- (void) readNumPackets
{
    [[EMConnectionManager sharedManager] readResource:@"numPackets" onSuccess:^(id readValue)
    {
        self.numPackets = [readValue unsignedCharValue];
        NSLog(@"[CommunicationProtocol.m]: numPackets read: %d",self.numPackets);
        [self readNextFragment];
    }onFail:^(NSError *error)
    {
        NSLog(@"[CommunicationProtocol.m]: On readNumPackets: %@",error);
        [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while reading answer from device",nil),[error localizedDescription]]];
    }];
}

- (void) waitForMessage: (uint8_t) retries
{
    if(retries == MAX_RETRIES)
    {
        NSLog(@"[CommunicationProtocol.m]: Device not ready.");
        NSLog(@"[CommunicationProtocol.m]: Timeout while expecting message");
    }
    else
    {
        NSLog(@"[CommunicationProtocol.m]: Reading messageAvailable from device");
        [[EMConnectionManager sharedManager] readResource:@"messageAvailableDevice" onSuccess:^(id readValue)
        {
            [self setMessageAvailableDevice:[readValue intValue]];
            if(![self messageAvailableDevice])
            {
                NSLog(@"[CommunicationProtocol.m]: Device not ready. Retrying...");
                [NSThread sleepForTimeInterval:SLEEP_TIME];
                [self waitForMessage:retries + 1];
            }
            else
            {
                if(self.numPackets == -1)
                {
                    [self readNumPackets];
                }
                else
                {
                    [self readNextFragment];
                }
            }
        }onFail:^(NSError *error)
        {
            NSLog(@"[CommunicationProtocol.m]: On waitForMessage: %@",error);
            [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while reading answer from device",nil),[error localizedDescription]]];
        }];
    }
}

-(void) writeMessage: (NSString*) message
{
    NSLog(@"[CommunicationProtocol.m]: Sending message: %@",message);
    [self setMessage:[NSMutableString stringWithString:message]];
    [self setRemainingBytes:[message length]];
    [self setCurrentIndex:0];
    [[EMConnectionManager sharedManager] writeValue:[NSNumber numberWithUnsignedChar:(unsigned char)([message length]/MAX_STRING_LENGTH)+1] toResource:@"numPackets" onSuccess:^
     {
         NSLog(@"[CommunicationProtocol.m]: numPackets set to %u",([message length]/MAX_STRING_LENGTH) + 1);
         [self sendNextFragment];
     }onFail:^(NSError *error)
     {
         NSLog(@"[CommunicationProtocol.m]: On setNumPackets: %@",error);
            [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while sending command to device",nil),[error localizedDescription]]];
     }];
}

- (void) sendNextFragment
{
    if(self.remainingBytes)
    {
        NSLog(@"[CommunicationProtocol.m]: Sending next fragment");
        NSUInteger numBytes;
        if(self.remainingBytes > MAX_STRING_LENGTH)
        {
            numBytes = MAX_STRING_LENGTH;
            self.remainingBytes -= MAX_STRING_LENGTH;
        }
        else
        {
            numBytes = self.remainingBytes;
            self.remainingBytes = 0;
        }
        [[EMConnectionManager sharedManager] writeValue:[NSNumber numberWithUnsignedChar:(unsigned char)numBytes] toResource:@"numBytes" onSuccess:^
         {
             NSLog(@"[CommunicationProtocol.m]: numBytes set to %d", numBytes);
             NSString *data = [self.message substringWithRange:NSMakeRange(self.currentIndex, numBytes)];
             self.currentIndex += numBytes;
             [[EMConnectionManager sharedManager] writeValue:data toResource:@"data" onSuccess:^
              {
                  NSLog(@"[CommunicationProtocol.m]: data set to: %@",data);
                  [[EMConnectionManager sharedManager] writeValue:@"1" toResource:@"messageAvailableMobile" onSuccess:^
                   {
                       NSLog(@"[CommunicationProtocol.m]: messageAvailableMobile set to TRUE");
                       NSLog(@"[CommunicationProtocol.m]: Packet written");
                       [self readACK: 0];
                   }onFail:^(NSError *error)
                   {
                       NSLog(@"[CommunicationProtocol.m]: On setMessageAvailable to TRUE: %@",error);
                       [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while sending command to device",nil),[error localizedDescription]]];
                   }];
              }onFail:^(NSError *error)
              {
                  NSLog(@"[CommunicationProtocol.m]: On setData: %@",error);
                  [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while sending command to device",nil),[error localizedDescription]]];
              }];
         }onFail:^(NSError *error)
         {
             NSLog(@"[CommunicationProtocol.m]: On setNumBytes: %@",error);
             [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while sending command to device",nil),[error localizedDescription]]];
         }];
    }
    else
    {
        NSLog(@"[CommunicationProtocol.m]: Finished sending message");
        [self readMessage];
    }
}

- (void) readACK: (uint8_t) retries
{
    if(retries == MAX_RETRIES)
    {
        NSLog(@"[CommunicationProtocol.m]: Device not ready.");
        NSLog(@"[CommunicationProtocol.m]: Timeout while expecting ACK");
    }
    else
    {
        NSLog(@"[CommunicationProtocol.m]: Reading ACK from device");
        [[EMConnectionManager sharedManager] readResource:@"messageAvailableMobile" onSuccess:^(id readValue)
        {
            [self setMessageAvailableMobile:[readValue intValue]];
            if([self messageAvailableMobile])
            {
                NSLog(@"[CommunicationProtocol.m]: Device not ready. Retrying...");
                [NSThread sleepForTimeInterval:SLEEP_TIME];
                [self readACK:retries + 1];
            }
            else
            {
                [self sendNextFragment];
            }
        }onFail:^(NSError *error)
        {
            NSLog(@"[CommunicationProtocol.m]: On readACK: %@",error);
            [[self delegate] reportProtocolError:self didFinishEnteringItem:[NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"Error occurred while sending command to device",nil),[error localizedDescription]]];
        }];
    }
}

-(void) establishConnection
{
    NSLog(@"[CommunicationProtocol.m]: Establishing connection...");
    [self writeMessage:@"Hello"];
    NSLog(@"[CommunicationProtocol.m]: Hello sent");
}

-(void) disconnect
{
    NSLog(@"[CommunicationProtocol.m]: Terminating connection...");
    [self writeMessage:@"Bye"];
    NSLog(@"[CommunicationProtocol.m]: Bye sent");
}

-(void) updateTime: (NSDateComponents*) date
{
    NSMutableString *command = [NSMutableString stringWithFormat: @"A5"];
    NSInteger year = [date year];
    year = year - (year/100)*100;
    [command appendString:[NSString stringWithFormat:@"%02ld%02ld%02ld%02ld%02ld%02ld",(long)year,(long)[date month],(long)[date day],(long)[date hour],(long)[date minute],(long)[date second]]];
    [self writeMessage:command];
}

-(void) updatePrice: (uint8_t) channel : (uint8_t) product : (uint8_t) eur : (uint8_t) cents
{
    NSMutableString *command = [NSMutableString stringWithFormat: @"A6%01d%01d%02d%02d",channel,product,eur,cents];
    [self writeMessage:command];
}

-(void) updateProductName: (uint8_t) channel : (uint8_t) product : (NSString*) name
{
    if([name length] > MAX_PRODUCT_NAME_LENGTH)
    {
        name = [name substringToIndex:MAX_PRODUCT_NAME_LENGTH-1];
    }
    NSMutableString *command = [NSMutableString stringWithFormat: @"A7%01d%01d%@",channel,product,name];
    [self writeMessage:command];
}

-(void) readSensorData
{
    NSString *command = @"A4";
    [self writeMessage:command];
}

-(void) readSalesLog : (NSDateComponents*) start : (NSDateComponents*) end
{
    NSMutableString *startDate = [NSMutableString stringWithString:@""];
    NSMutableString *endDate = [NSMutableString stringWithString:@""];
    NSMutableString *command = [NSMutableString stringWithString:@"A2"];
    if(start == nil)
    {
        [startDate setString:@""];
    }
    else
    {
        NSInteger year = [start year];
        year = year - (year/100)*100;
        [startDate appendString:[NSString stringWithFormat:@"%02ld%02ld%02ld%02ld%02ld",(long)year,(long)[start month],(long)[start day],(long)[start hour],(long)[start minute]]];
    }
    if(end == nil)
    {
        [endDate setString:@""];
    }
    else
    {
        NSInteger year = [end year];
        year = year - (year/100)*100;
        [endDate appendString:[NSString stringWithFormat:@"%02ld%02ld%02ld%02ld%02ld",(long)year,(long)[end month],(long)[end day],(long)[end hour],(long)[end minute]]];
    }
    [command appendString:startDate];
    [command appendString:@"-"];
    [command appendString:endDate];
    [self writeMessage:command];
}

-(void) readIncidentLog:(NSDateComponents *)start :(NSDateComponents *)end
{
    NSMutableString *startDate = [NSMutableString stringWithString:@""];
    NSMutableString *endDate = [NSMutableString stringWithString:@""];
    NSMutableString *command = [NSMutableString stringWithString:@"A3"];
    if(start == nil)
    {
        [startDate setString:@""];
    }
    else
    {
        NSInteger year = [start year];
        year = year - (year/100)*100;
        [startDate appendString:[NSString stringWithFormat:@"%02ld%02ld%02ld%02ld%02ld",(long)year,(long)[start month],(long)[start day],(long)[start hour],(long)[start minute]]];
    }
    if(end == nil)
    {
        [endDate setString:@""];
    }
    else
    {
        NSInteger year = [end year];
        year = year - (year/100)*100;
        [endDate appendString:[NSString stringWithFormat:@"%02ld%02ld%02ld%02ld%02ld",(long)year,(long)[end month],(long)[end day],(long)[end hour],(long)[end minute]]];
    }
    [command appendString:startDate];
    [command appendString:@"-"];
    [command appendString:endDate];
    [self writeMessage:command];
}

@end
