//
//  ZYJMQTTProperties.m
//  ZYJMQTTClient
//
//  Created by Christoph Krey on 04.04.17.
//  Copyright © 2017 Christoph Krey. All rights reserved.
//

#import "ZYJMQTTProperties.h"

@implementation ZYJMQTTProperties

- (instancetype)init {
    return [self initFromData:[[NSData alloc] init]];
}
- (instancetype)initFromData:(NSData *)data {
    self = [super init];

    int propertyLength = [ZYJMQTTProperties getVariableLength:data];
    int offset = [ZYJMQTTProperties variableIntLength:propertyLength];
    NSData *remainingData = [data subdataWithRange:NSMakeRange(offset, data.length - offset)];
    offset = 0;
    if (remainingData.length >= propertyLength) {
        while (propertyLength - offset > 0) {
            const UInt8 *bytes = remainingData.bytes;
            UInt8 propertyType = bytes[offset];
            switch (propertyType) {
                case ZYJMQTTPayloadFormatIndicator:
                    if (propertyLength - offset > 1) {
                        self.payloadFormatIndicator = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;
                case ZYJMQTTPublicationExpiryInterval:
                    if (propertyLength - offset > 4) {
                        self.publicationExpiryInterval = @([ZYJMQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 5;
                    }
                    break;
                case ZYJMQTTContentType:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.contentType = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;

                case ZYJMQTTResponseTopic:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.responseTopic = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;

                case ZYJMQTTCorrelationData:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.correlationData = [ZYJMQTTProperties getBinaryData:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;

                case ZYJMQTTSubscriptionIdentifier:
                    if (propertyLength - offset > 1) {
                        int subscriptionIdentifier = [ZYJMQTTProperties getVariableLength:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        int l = [ZYJMQTTProperties variableIntLength:subscriptionIdentifier];
                        self.subscriptionIdentifier = @(subscriptionIdentifier);
                        offset += 1 + l;

                    }
                    break;

                case ZYJMQTTSessionExpiryInterval:
                    if (propertyLength - offset > 4) {
                        self.sessionExpiryInterval = @([ZYJMQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 5;
                    }
                    break;
                case ZYJMQTTAssignedClientIdentifier:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.assignedClientIdentifier = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;


                case ZYJMQTTServerKeepAlive:
                    if (propertyLength - offset > 2) {
                        self.serverKeepAlive = @([ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 3;
                    }
                    break;

                case ZYJMQTTAuthMethod:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.authMethod = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;


                case ZYJMQTTAuthData:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.authData = [ZYJMQTTProperties getBinaryData:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;
                    

                case ZYJMQTTRequestProblemInformation:
                    if (propertyLength - offset > 1) {
                        self.requestProblemInformation = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;

                case ZYJMQTTWillDelayInterval:
                    if (propertyLength - offset > 4) {
                        self.willDelayInterval = @([ZYJMQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 5;
                    }
                    break;

                case ZYJMQTTRequestResponseInformation:
                    if (propertyLength - offset > 1) {
                        self.requestResponseInformation = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;

                case ZYJMQTTResponseInformation:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.responseInformation = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;


                case ZYJMQTTServerReference:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.serverReference = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;


                case ZYJMQTTReasonString:
                    if (propertyLength - offset > 2) {
                        int l = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        self.reasonString = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];
                        offset += 1 + 2 + l;
                    }
                    break;


                case ZYJMQTTReceiveMaximum:
                    if (propertyLength - offset > 2) {
                        self.receiveMaximum = @([ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 3;
                    }
                    break;

                case ZYJMQTTTopicAliasMaximum:
                    if (propertyLength - offset > 2) {
                        self.topicAliasMaximum = @([ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 3;
                    }
                    break;

                case ZYJMQTTTopicAlias:
                    if (propertyLength - offset > 2) {
                        self.topicAlias = @([ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 3;
                    }
                    break;

                case ZYJMQTTMaximumQoS:
                    if (propertyLength - offset > 1) {
                        self.maximumQoS = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;

                case ZYJMQTTRetainAvailable:
                    if (propertyLength - offset > 1) {
                        self.retainAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;

                case ZYJMQTTUserProperty:
                    if (propertyLength - offset > 4) {
                        int keyL = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        NSString *key = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]];

                        int valueL = [ZYJMQTTProperties getTwoByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1 + 2 + keyL, remainingData.length - (offset + 1))]];

                        NSString *value = [ZYJMQTTProperties getUtf8String:[remainingData subdataWithRange:NSMakeRange(offset + 1 + 2 + keyL, remainingData.length - (offset + 1))]];

                        if (!self.userProperty) {
                            self.userProperty = [[NSMutableDictionary alloc] init];
                        }
                        self.userProperty[key] = value;
                        offset += 1 + 2 + keyL + 2 + valueL;
                    }
                    break;

                case ZYJMQTTMaximumPacketSize:
                    if (propertyLength - offset > 4) {
                        self.maximumPacketSize = @([ZYJMQTTProperties getFourByteInt:[remainingData subdataWithRange:NSMakeRange(offset + 1, remainingData.length - (offset + 1))]]);
                        offset += 5;
                    }
                    break;

                case ZYJMQTTWildcardSubscriptionAvailable:
                    if (propertyLength - offset > 1) {
                        self.wildcardSubscriptionAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;

                case ZYJMQTTSubscriptionIdentifiersAvailable:
                    if (propertyLength - offset > 1) {
                        self.subscriptionIdentifiersAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;

                case ZYJMQTTSharedSubscriptionAvailable:
                    if (propertyLength - offset > 1) {
                        self.sharedSubscriptionAvailable = [NSNumber numberWithInt:bytes[offset + 1]];
                        offset += 2;
                    }
                    break;

                default:
                    return self;
            }
        }
    }
    return self;
}

+ (int)getVariableLength:(NSData *)data {
    int length = 0;
    int offset = 0;
    int multiplier = 1;
    UInt8 digit;

    do {
        if (data.length < offset) {
            return -1;
        }
        [data getBytes:&digit range:NSMakeRange(offset, 1)];
        offset++;
        length += (digit & 0x7f) * multiplier;
        multiplier *= 128;
        if (multiplier > 128 * 128 * 128) {
            return -2;
        }
    } while ((digit & 0x80) != 0);
    return length;
}

+ (int)getTwoByteInt:(NSData *)data {
    int i = 0;
    if (data.length >= 2) {
        const UInt8 *bytes = data.bytes;
        i = bytes[0] * 256 +
        bytes[1];
    }
    return i;
}

+ (int)getFourByteInt:(NSData *)data {
    int i = 0;
    if (data.length >= 4) {
        const UInt8 *bytes = data.bytes;
        i = bytes[0] * 256 * 256 * 256 +
        bytes[1] * 256 * 256 +
        bytes[2] * 256 +
        bytes[3];
    }
    return i;
}

+ (NSString *)getUtf8String:(NSData *)data {
    NSString *s;
    int l = [ZYJMQTTProperties getTwoByteInt:data];
    if (data.length >= l + 2) {
        s = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, l)] encoding:NSUTF8StringEncoding];
    }
    return s;
}

+ (NSData *)getBinaryData:(NSData *)data {
    NSData *d;
    int l = [ZYJMQTTProperties getTwoByteInt:data];
    if (data.length >= l + 2) {
        d = [data subdataWithRange:NSMakeRange(2, l)];
    }
    return d;
}

+ (int)variableIntLength:(int)length {
    int l = 0;
    if (length <= 127) {
        l = 1;
    } else if (length <= 16383) {
        l = 2;
    } else if (length <= 2097151) {
        l = 3;
    } else if (length <= 268435455) {
        l = 4;
    }
    return l;
}
@end


