/*---------------------------------------------------------------------------*/
/* JsonIo.m                                                                  */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#import <Foundation/Foundation.h>

#import "platform/platform.h"
#import "platform/IosNamespace.h"
#import "JsonIo.h"

#define JSON_TYPE_OBJECT        0
#define JSON_TYPE_ARRAY         1
#define JSON_TYPE_VALUE         2

#define JSON_FLAG_QUOTED        1
#define JSON_FLAG_INCOMPLETE    2

#define JSON_MAX_ITEM           3072

@interface JsonContent : NSObject

@property (nonatomic) int desc;
@property (nonatomic) NSString *name;
@property (nonatomic) NSString *info;

@property (copy) int (^get_type)(void);
@property (copy) int (^get_count)(void);
@property (copy) int (^get_flags)(void);
@property (copy) int (^get_base)(void);
@property (copy) void (^set_type)(int type);
@property (copy) void (^set_count)(int count);
@property (copy) void (^set_flags)(int flags);
@property (copy) void (^set_base)(int base);
@property (copy) void (^set_desc)(int type, int count, int flags, int base);

+ (id) jsonContent;

- (id) init;

@end
@implementation JsonContent

+ (id) jsonContent
{
    return [[self alloc] init];
}

- (id) init
{
    self = [super init];
    if (self) {
        JsonContent *__weak weakSelf = self;
        _get_type = ^ int ()  { return(weakSelf.desc & 0x00000003); };
        _get_count = ^ int () { return((weakSelf.desc >> 2) & 0x0000ffff); };
        _get_flags = ^ int () { return((weakSelf.desc >> 18) & 0x00000003); };
        _get_base = ^ int ()  { return((weakSelf.desc >> 20) & 0x00000fff); };
        _set_type = ^ void (int type)   { weakSelf.desc = (weakSelf.desc & ~0x00000003) | type; };
        _set_count = ^ void (int count) { weakSelf.desc = (weakSelf.desc & ~0x0003fffc) | (count << 2); };
        _set_flags = ^ void (int flags) { weakSelf.desc = (weakSelf.desc & ~0x000c0000) | (flags << 18); };
        _set_base = ^ void (int base)   { weakSelf.desc = (weakSelf.desc & ~0xfff00000) | (base << 20); };
        _set_desc = ^ void (int type, int count, int flags, int base) {
            weakSelf.desc = type | (count << 2) | (flags << 18) | (base << 20);
		};

    }
    return self;
}

@end
@interface JsonContentArray : NSObject

@property (nonatomic) NSMutableArray *array_;

@property (copy) JsonContent *(^get)(int index);
@property (copy) void (^set)(int index, JsonContent *str);
@property (copy) int (^size)(void);
@property (copy) void (^add)(JsonContent *str);
@property (copy) void (^clear)(void);

+ (id) array;

- (id) init;
- (JsonContent *) objectAtIndexedSubscript:(int)index;
- (void) setObject:(JsonContent *)content atIndexedSubscript:(int)index;

@end
@implementation JsonContentArray

+ (id) array
{
    return [[self alloc] init];
}

- (id) init
{
    self = [super init];
    if (self) {
        JsonContentArray *__weak weakSelf = self;
        _get = ^ JsonContent * (int index) { JsonContent *content = (JsonContent *)(weakSelf.array_[index]); return ((((NSNull *) content) == [NSNull null]) ? nil : content); };
        _set = ^ void (int index, JsonContent *content) { [weakSelf.array_ replaceObjectAtIndex:index withObject:(content == nil) ? [NSNull null] : content]; };
        _size = ^ int () { return (int)(weakSelf.array_.count); };
        _add = ^ void (JsonContent *content) { [weakSelf.array_ addObject:(content == nil) ? [NSNull null] : content]; };
        _clear = ^ void () { return [weakSelf.array_ removeAllObjects]; };

        _array_ = [NSMutableArray array];
    }
    return self;
}

- (JsonContent *) objectAtIndexedSubscript:(int)index
{
    JsonContent *content = self.array_[index];
    return ((((NSNull *) content) == [NSNull null]) ? nil : content);
}

- (void) setObject:(JsonContent *)content atIndexedSubscript:(int)index
{
    self.array_[index] = (content == nil) ? [NSNull null] : content;
}

@end
@interface JsonIo()

@property (nonatomic) int index;
@property (nonatomic) JsonContentArray *content;

- (void) reset_;
- (int) add_:(int)type flags:(int)flags base:(int)base name:(NSString *)name info:(NSString *)info;
- (int) add_object_:(int)base name:(NSString *)name;
- (int) add_array_:(int)base name:(NSString *)name;
- (void) add_string_:(int)base name:(NSString *)name value:(NSString *)value;
- (void) add_json_:(int)base name:(NSString *)name value:(NSString *)value;
- (void) add_int_:(int)base name:(NSString *)name c:(int)c;
- (void) add_unsigned_:(int)base name:(NSString *)name c:(int)c;
- (void) add_bool_:(int)base name:(NSString *)name value:(bool)value;
- (int) encode_to_string_:(int)current str:(NSMutableString *)str;
- (void) encode_:(NSMutableString *)str;
- (word) get_first_:(byte)type base:(word)base;
- (word) get_next_:(byte)type base:(word)base last:(word)last;
- (word) get_id_:(byte)type base:(word)base flags:(byte)flags name:(NSString *)name;
- (NSString *) get_value_:(word)base flags:(byte)flags name:(NSString *)name;
- (word) get_object_:(word)base name:(NSString *)name;
- (word) get_array_:(word)base name:(NSString *)name;
- (NSString *) get_string_:(word)base name:(NSString *)name;
- (bool) decode_:(NSString *)p;

+ (id) jsonIo;

- (id) init;

@end
@implementation JsonIo

+ (id) jsonIo
{
    return [[self alloc] init];
}

- (id) init
{
    self = [super init];
    if (self) {
        JsonIo *__weak weakSelf = self;
        _reset = ^ void () { [weakSelf reset_]; };
        _add = ^ int (int type, int flags, int base, NSString *name, NSString *info) { return [weakSelf add_:type flags:flags base:base name:name info:info]; };
        _add_object = ^ int (int base, NSString *name) { return [weakSelf add_object_:base name:name]; };
        _add_array = ^ int (int base, NSString *name) { return [weakSelf add_array_:base name:name]; };
        _add_string = ^ void (int base, NSString *name, NSString *value) { [weakSelf add_string_:base name:name value:value]; };
        _add_json = ^ void (int base, NSString *name, NSString *value) { [weakSelf add_json_:base name:name value:value]; };
        _add_int = ^ void (int base, NSString *name, int c) { [weakSelf add_int_:base name:name c:c]; };
        _add_unsigned = ^ void (int base, NSString *name, int c) { [weakSelf add_unsigned_:base name:name c:c]; };
        _add_bool = ^ void (int base, NSString *name, bool value) { [weakSelf add_bool_:base name:name value:value]; };
        _encode_to_string = ^ int (int current, NSMutableString *str) { return([weakSelf encode_to_string_:current str:str]); };
        _encode = ^ void (NSMutableString *str) { [weakSelf encode_:str]; };
        _get_first = ^ word (byte type, word base) { return([weakSelf get_first_:type base:base]); };
        _get_next = ^ word (byte type, word base, word last) { return([weakSelf get_next_:type base:base last:last]); };
        _get_id = ^ word (byte type, word base, byte flags, NSString *name) { return([weakSelf get_id_:type base:base flags:flags name:name]); };
        _get_value = ^ NSString * (word base, byte flags, NSString *name) { return([weakSelf get_value_:base flags:flags name:name]); };
        _get_object = ^ word (word base, NSString *name) { return([weakSelf get_object_:base name:name]); };
        _get_array = ^word (word base, NSString *name) { return([weakSelf get_array_:base name:name]); };
        _get_string = ^NSString * (word base, NSString *name) { return([weakSelf get_string_:base name:name]); };
        _decode = ^bool (NSString *p) { return([ weakSelf decode_:p]); };

        self.index = 0;
        self.content = [JsonContentArray array];
    }
    return self;
}

- (void) reset_
{
    self.index = 0;
    self.content = [JsonContentArray array];
}

- (int) add_:(int)type flags:(int)flags base:(int)base name:(NSString *)name info:(NSString *)info
{
    int current;
    if(base >= self.index) {
        current = 0;
    }
    else {
        current = base + self.content[base].get_count() + 1;
    }
    if(current != self.index) return 0;

    self.content[current] = [JsonContent jsonContent];
    self.content[current].set_desc(type, ((type == JSON_TYPE_OBJECT) || (type == JSON_TYPE_ARRAY)) ? 0 : (int)(info.length), flags, base);
    self.content[current].name = name;
    self.content[current].info = info;

    while(base < self.index) {
        self.content[base].set_count(self.content[base].get_count() + 1);
        base = self.content[base].get_base();
    }

    (self.index)++;
    return current;
}

- (int) add_object_:(int)base name:(NSString *)name
{
    return self.add(JSON_TYPE_OBJECT, 0, base, name, nil);
}

- (int) add_array_:(int)base name:(NSString *)name
{
    return self.add(JSON_TYPE_ARRAY, 0, base, name, nil);
}

- (void) add_string_:(int)base name:(NSString *)name value:(NSString *)value
{
    if(value != nil) {
        self.add(JSON_TYPE_VALUE, JSON_FLAG_QUOTED, base, name, value);
    }
}

- (void) add_json_:(int)base name:(NSString *)name value:(NSString *)value
{
    if(value != nil) {
        self.add(JSON_TYPE_VALUE, 0, base, name, value);
    }
}

- (void) add_int_:(int)base name:(NSString *)name c:(int)c
{
    self.add(JSON_TYPE_VALUE, 0, base, name, [NSString stringWithFormat:@"%d", c]);
}

- (void) add_unsigned_:(int)base name:(NSString *)name c:(int)c
{
    self.add(JSON_TYPE_VALUE, 0, base, name, [NSString stringWithFormat:@"%u", c]);
}

- (void) add_bool_:(int)base name:(NSString *)name value:(bool)value
{
    self.add(JSON_TYPE_VALUE, 0, base, name, value ? @"true" : @"false");
}

- (int) encode_to_string_:(int)current str:(NSMutableString *)str
{
    NSRange r;
    int c, i;
    int count = 1;
    
    r.length = 1;
    switch(self.content[current].get_type()) {
    case JSON_TYPE_ARRAY:
        [str appendString:@"["];
        for(i = 0; i < self.content[current].get_count(); ) {
            if(i != 0) [str appendString:@","];
            c = self.encode_to_string(current+i+1, str);
            i += c;
            count += c;
        }
        [str appendString:@"]"];
        break;
    case JSON_TYPE_OBJECT:
        [str appendString:@"{"];
        for(i = 0; i < self.content[current].get_count(); ) {
            if ((self.content[current + i + 1].get_type() != JSON_TYPE_VALUE) || (self.content[current + i + 1].info != nil)) {
                if(i != 0) [str appendString:@","];
                [str appendString:@"\""];
                [str appendString:self.content[current+i+1].name];
                [str appendString:@"\":"];
                c = self.encode_to_string(current+i+1, str);
                i += c;
                count += c;
            }
            else {
                i++;
            }
        }
        [str appendString:@"}"];
        break;
    case JSON_TYPE_VALUE:
        if((self.content[current].get_flags() & JSON_FLAG_QUOTED) != 0) {
            [str appendString:@"\""];
            for(i = 0; i < self.content[current].get_count(); i++) {
                switch([self.content[current].info characterAtIndex:i]) {
                case '\x00': [str appendString:@"\\u0000"]; break;
                case '\x01': [str appendString:@"\\u0001"]; break;
                case '\x02': [str appendString:@"\\u0002"]; break;
                case '\x03': [str appendString:@"\\u0003"]; break;
                case '\x04': [str appendString:@"\\u0004"]; break;
                case '\x05': [str appendString:@"\\u0005"]; break;
                case '\x06': [str appendString:@"\\u0006"]; break;
                case '\x07': [str appendString:@"\\u0007"]; break;
                case '\b': [str appendString:@"\\b"]; break;
                case '\t': [str appendString:@"\\t"]; break;
                case '\n': [str appendString:@"\\n"]; break;
                case '\x0b': [str appendString:@"\\u000b"]; break;
                case '\f': [str appendString:@"\\f"]; break;
                case '\r': [str appendString:@"\\r"]; break;
                case '\x0e': [str appendString:@"\\u000e"]; break;
                case '\x0f': [str appendString:@"\\u000f"]; break;
                case '\x10': [str appendString:@"\\u0010"]; break;
                case '\x11': [str appendString:@"\\u0011"]; break;
                case '\x12': [str appendString:@"\\u0012"]; break;
                case '\x13': [str appendString:@"\\u0013"]; break;
                case '\x14': [str appendString:@"\\u0014"]; break;
                case '\x15': [str appendString:@"\\u0015"]; break;
                case '\x16': [str appendString:@"\\u0016"]; break;
                case '\x17': [str appendString:@"\\u0017"]; break;
                case '\x18': [str appendString:@"\\u0018"]; break;
                case '\x19': [str appendString:@"\\u0019"]; break;
                case '\x1a': [str appendString:@"\\u001a"]; break;
                case '\x1b': [str appendString:@"\\u001b"]; break;
                case '\x1c': [str appendString:@"\\u001c"]; break;
                case '\x1d': [str appendString:@"\\u001d"]; break;
                case '\x1e': [str appendString:@"\\u001e"]; break;
                case '\x1f': [str appendString:@"\\u001f"]; break;
                case '"': [str appendString:@"\\\""]; break;
                case '\\': [str appendString:@"\\\\"]; break;
                default: r.location = i; [str appendString:[self.content[current].info substringWithRange:r]]; break;
                }
            }
            [str appendString:@"\""];
        }
        else {
            [str appendString:self.content[current].info];
        }
        break;
    default:
        break;
    }
    return count;
}

- (void) encode_:(NSMutableString *)str
{
    if (self.index != 0) {
        self.encode_to_string(0, str);
    }
}

- (word) nextindex:(word)i
{
    if(self.content[i].get_type()==JSON_TYPE_OBJECT || self.content[i].get_type()==JSON_TYPE_ARRAY) return i+self.content[i].get_count()+1;
    return i+1;
}

- (word) get_first_:(byte)type base:(word)base
{
    word end;
    if(base==0xffff) {
        if(self.index == 0) return 0xffff;
        end = self.index-1;
        base = 0;
    }
    else {
        end = base + self.content[base].get_count();
        base++;
    }

    while(base<=end && type!=JSON_TYPE_ANY && self.content[base].get_type()!=type) {
        base = [self nextindex:base];
    }
    if(base>end) return 0xffff;
    return base;
}

- (word) get_next_:(byte)type base:(word)base last:(word)last
{
    word end;
    if(base==0xffff) {
        end = self.index-1;
        base = 0;
    }
    else {
        end = base + self.content[base].get_count();
    }
    last = [self nextindex:last];

    while(last<=end && type!=JSON_TYPE_ANY && self.content[last].get_type()!=type) {
        last = [self nextindex:last];
    }
    if(last>end) return 0xffff;
    return last;
}

- (word) get_id_:(byte)type base:(word)base flags:(byte)flags name:(NSString *)name
{
    word id = self.get_first(type, base);
    if (name == nil) {
        while(id!=0xffff) {
            if(self.content[id].get_flags()==flags && self.content[id].name==nil) return id;
            id = self.get_next(type, base, id);
        }
    }
    else {
        while(id!=0xffff) {
            if(self.content[id].get_flags()==flags && self.content[id].name!=nil && [self.content[id].name isEqualToString:name]) return id;
            id = self.get_next(type, base, id);
        }
    }
    return 0xffff;
}

- (NSString *) get_value_:(word)base flags:(byte)flags name:(NSString *)name
{
    word id = self.get_id(JSON_TYPE_VALUE, base, flags, name);
    return ((id == 0xffff) ? nil : self.content[id].info);
}

- (word) get_object_:(word)base name:(NSString *)name
{
    return self.get_id(JSON_TYPE_OBJECT, base, 0, name);
}

- (word) get_array_:(word)base name:(NSString *)name
{
    return self.get_id(JSON_TYPE_ARRAY, base, 0, name);
}

- (NSString *) get_string_:(word)base name:(NSString *)name
{
    return self.get_value(base, JSON_FLAG_QUOTED, name);
}

- (bool) whitespace:(char)c
{
    return c==' ' || c=='\r' || c=='\n' || c=='\t';
}

- (bool) unquoted:(char)c
{
    return c=='-' || (c>='0'&&c<='9') || (c>='a'&&c<='z') || (c>='A'&&c<='Z');
}

- (unsigned) skip_whitespace:(NSString *)p i:(unsigned)i
{
    while ((i < p.length) && [self whitespace:[p characterAtIndex:i]]) {
        i++;
    }
    return i;
}

- (unsigned) unescape:(NSString *)p i:(unsigned)i str:(NSMutableString *)str
{
    NSRange r, r4;

    r.length = 1;
    r4.length = 4;
    while ((i < p.length) && ([p characterAtIndex:i] != '"')) {
        char ch = [p characterAtIndex:i];
        if (ch == '\\') {
            i++;
            if (i == p.length) {
                return (unsigned)(p.length);
            }
            switch ([p characterAtIndex:i]) {
            case '"': [str appendString:@"\""]; break;
            case '\\': [str appendString:@"\\"]; break;
            case '/': [str appendString:@"/"]; break;
            case 'b': [str appendString:@"\b"]; break;
            case 'f': [str appendString:@"\f"]; break;
            case 'n': [str appendString:@"\n"]; break;
            case 'r': [str appendString:@"\r"]; break;
            case 't': [str appendString:@"\t"]; break;
            case 'u':
                if (i+5 > p.length) {
                    return (unsigned)(p.length);
                }
                r4.location = i+1;
                unichar c = strtol([p substringWithRange:r4].UTF8String, NULL, 16);
                [str appendString:[NSString stringWithCharacters:&c length:1]];
                i += 4;
                break;
            }
        }
        else {
            r.location = i;
            [str appendString:[p substringWithRange:r]];
        }
        i++;
    }
    return i;
}

- (unsigned) read:(NSString *)p i:(unsigned)i base:(word)base name:(NSString *)name
{
    NSRange r;

    i = [self skip_whitespace:p i:i];
    if(i == p.length) {
        if (base==0xffff) {
            return i;
        }
    }
    else if([p characterAtIndex:i]=='{') {
        i++;
        int root = self.index;
        self.content[self.index] = [JsonContent jsonContent];
        self.content[self.index].set_desc(JSON_TYPE_OBJECT, 0, 0, base);
        self.content[self.index].name = name;
        self.content[self.index].info = nil;
        self.index++;
        do {
            i = [self skip_whitespace:p i:i];
            if([p characterAtIndex:i]=='}') {
                self.content[root].set_count(self.index-root-1);
                return i+1;
            }
            if(self.index != root+1) {
                if ([p characterAtIndex:i]!=',') {
                    return (unsigned)(p.length + 1);
                }
                i = [self skip_whitespace:p i:i+1];
            }
            if ([p characterAtIndex:i]!='"') {
                return (unsigned)(p.length + 1);
            }
            NSMutableString *n = [NSMutableString stringWithCapacity:16];
            i = [self unescape:p i:i+1 str:n];
            if (i == p.length) {
                return (unsigned)(p.length + 1);
            }
            i = [self skip_whitespace:p i:i+1];
            if ([p characterAtIndex:i]!=':') {
                return (unsigned)(p.length + 1);
            }
            i = [self read:p i:i+1 base:root name:n];
        }
        while(i < p.length);
    }
    else if([p characterAtIndex:i]=='[') {
        i++;
        int root = self.index;
        self.content[self.index] = [JsonContent jsonContent];
        self.content[self.index].set_desc(JSON_TYPE_ARRAY, 0, 0, base);
        self.content[self.index].name = name;
        self.content[self.index].info = nil;
        self.index++;
        do {
            i = [self skip_whitespace:p i:i];
            if([p characterAtIndex:i]==']') {
                self.content[root].set_count(self.index-root-1);
                return i+1;
            }
            if(self.index != root+1) {
                if ([p characterAtIndex:i]!=',') {
                    return (unsigned)(p.length + 1);
                }
                i++;
            }
            i = [self read:p i:i base:root name:nil];
        }
        while(i < p.length);
    }
    else if([p characterAtIndex:i]=='"') {
        NSMutableString *v = [NSMutableString stringWithCapacity:16];
        i = [self unescape:p i:i+1 str:v];
        if (i < p.length) {
            self.content[self.index] = [JsonContent jsonContent];
            self.content[self.index].set_desc(JSON_TYPE_VALUE, (int)(v.length), JSON_FLAG_QUOTED, base);
            self.content[self.index].name = name;
            self.content[self.index].info = v;
            self.index++;
            return i+1;
        }
    }
    else if([self unquoted:[p characterAtIndex:i]]) {
        unsigned j = i;
        do {
            i++;
        }
        while ((i < p.length) && [self unquoted:[p characterAtIndex:i]]);
        if ((i < p.length) || (self.index == 0)) {
            self.content[self.index] = [JsonContent jsonContent];
            self.content[self.index].set_desc(JSON_TYPE_VALUE, i - j, 0, base);
            self.content[self.index].name = name;
            r.location = j;
            r.length = i - j;
            self.content[self.index].info = [p substringWithRange:r];
            self.index++;
            return i;
        }
    }
    return (unsigned)(p.length + 1);
}

- (bool) decode_:(NSString *)p
{
    self.index = 0;
    unsigned i = [self read:p i:0 base:0xffff name:nil];
    i = [self skip_whitespace:p i:i];
    return (i == p.length);
}

/*
- (void) dump
{
    word i;

    for(i=0; i<self.index && i<256; i++) {
        NSLog(@"%u %u %u %x %u %@ %@", i, self.content[i].get_base(), self.content[i].get_count(),
            self.content[i].get_flags(), self.content[i].get_type(),
            ((self.content[i].name != nil) ? self.content[i].name : @"<nil>"),
            ((self.content[i].info != nil) ? self.content[i].info : @"<nil>"));
    }
}
*/

@end
