#import "Algorithm20.h"

@implementation Algorithm20

- (instancetype)init {
    self = [super init];
    if (self) {
        self.X = [[ParamX alloc] init];
        self.iValueManager = [[IValueManager alloc] init];
        self.rfManager = [[ReferenceManager alloc] init];
        self.calIValue = [NSMutableArray array];
    }
    return self;
}

- (IValue *)getInitParam {
    return self.iValueManager.preIValue;
}

- (void)setInitParam:(IValue *)iValue {
    [self.iValueManager setPreIValue:iValue];
}

- (void)addRef:(NSNumber *)value time:(NSDate *)time {
    [self.rfManager addReference:value.doubleValue time:time];
}

- (NSNumber *)calcBloodGlucose:(double)current datetime:(NSDate *)datetime {
    NSNumber *bgValue = @(0.0);
    IValue *cur_data = [[IValue alloc] initWithCurrentTime:datetime current:@(current)];
    IValue *i4_data = nil;
    [self.rfManager findReferenceByDate:datetime];

    if ([self.rfManager isCalK]) {
        i4_data = [self calKValueAlgorithm:cur_data];
    } else {
        i4_data = [self I4Algorith:cur_data];
    }

    if ([self.rfManager isCalK]) {
        bgValue = @([self.rfManager getRefValue].value);
    } else if ([self.rfManager isWaitCalK]) {
        bgValue = @([self.rfManager getRefValue].value);
        i4_data.rfTime = [self.rfManager getRefValue].time;
        i4_data.rf = @([self.rfManager getRefValue].value);
    } else if (i4_data.k != nil) {
        bgValue = @([i4_data.I4 doubleValue] / [i4_data.k doubleValue]);
    }

    bgValue = [self roundDouble:bgValue.doubleValue precision:1];
    if ((current >= 600.0) || (current <= 30.0)) {
        bgValue = @(0.0);
    }

    [i4_data setBg:bgValue];
    [self.iValueManager setPreIValue:i4_data];

    NSString *rft = i4_data.rfTime == nil ? @"" : [self dateformate:i4_data.rfTime];

    NSLog(@"%@\t%@\t%@", i4_data.current, [self dateformate:i4_data.currentTime], bgValue);
    return bgValue;
}

- (NSNumber *)calcBloodGlucose:(double)current index:(NSInteger)index {
    NSNumber *bgValue = @(0.0);
    IValue *cur_data = [[IValue alloc] initWithCurrentTime:nil current:@(current)];
    IValue *i4_data = nil;
    [self.rfManager findReferenceByIndex:index];

    if ([self.rfManager isCalK]) {
        i4_data = [self calKValueAlgorithm:cur_data];
    } else {
        i4_data = [self I4Algorith:cur_data];
    }

    if ([self.rfManager isCalK]) {
        bgValue = @([self.rfManager getRefValue].value);
    } else if ([self.rfManager isWaitCalK]) {
        bgValue = @([self.rfManager getRefValue].value);
        i4_data.rfTime = [self.rfManager getRefValue].time;
        i4_data.rf = @([self.rfManager getRefValue].value);
    } else if (i4_data.k != nil) {
        bgValue = @([i4_data.I4 doubleValue] / [i4_data.k doubleValue]);
    }

    bgValue = [self roundDouble:bgValue.doubleValue precision:1];
    if ((current >= 600.0) || (current <= 30.0)) {
        bgValue = @(0.0);
    }

    [i4_data setBg:bgValue];
    [self.iValueManager setPreIValue:i4_data];
    [self.calIValue addObject:i4_data];

    return bgValue;
}

- (NSMutableArray<NSNumber *> *)calculateBloodGlucose:(NSInteger)index refValue:(NSNumber *)refValue values:(NSMutableArray<NSNumber *> *)values {
    [self.rfManager addCurrentReference:refValue.doubleValue index:index];

    NSMutableArray<NSNumber *> *dataList = [NSMutableArray array];
    for (NSInteger i = 0; i < values.count; i++) {
        NSNumber *electricValue = values[i];
        NSNumber *bgValue = [self calcBloodGlucose:electricValue.doubleValue index:(index + i)];
        [dataList addObject:bgValue];
    }

    return dataList;
}

- (IValue *)calTest:(double)current datetime:(NSDate *)datetime {
    NSNumber *bgValue = @(0.0);
    IValue *cur_data = [[IValue alloc] initWithCurrentTime:datetime current:@(current)];
    IValue *i4_data = nil;
    [self.rfManager findReferenceByDate:datetime];

    if ([self.rfManager isCalK]) {
        i4_data = [self calKValueAlgorithm:cur_data];
    } else {
        i4_data = [self I4Algorith:cur_data];
    }

    if ([self.rfManager isCalK]) {
        bgValue = @([self.rfManager getRefValue].value);
    } else if ([self.rfManager isWaitCalK]) {
        bgValue = @([self.rfManager getRefValue].value);
        [i4_data setRfTime:[self.rfManager getRefValue].time];
        [i4_data setRf:@([self.rfManager getRefValue].value)];
    } else if (i4_data.k != nil) {
        bgValue = @([i4_data.I4 doubleValue] / [i4_data.k doubleValue]);
    }

    bgValue = [self roundDouble:bgValue.doubleValue precision:1];
    if ((current >= 600.0) || (current <= 30.0)) {
        bgValue = @(0.0);
    }

    [i4_data setBg:bgValue];
    [self.iValueManager setPreIValue:i4_data];

    NSString *rft = i4_data.rfTime == nil ? @"" : [self dateformate:i4_data.rfTime];

//    NSLog(@"%@\t%@\t%@", [i4_data getI0], [i4_data getI1], [i4_data getI2], [i4_data getI3], [i4_data getI4], [i4_data getK], [i4_data getRf], [i4_data getBg], [i4_data getCurrent], [self dateformate:[i4_data getCurrentTime]], [i4_data getRf], rft);

    return i4_data;
}

- (IValue *)calKValueAlgorithm:(IValue *)cur_data {
    NSNumber *kValue = @(0.0);
    cur_data = [self calI4ForK:cur_data];
    kValue = cur_data.k;
    I4Algorithm *i4Temp = nil;

    if (kValue.doubleValue >= 10.0 && kValue.doubleValue <= 20.0) {
        [cur_data setK:kValue];
        return cur_data;
    }

    if (kValue.doubleValue < 10.0) {
        [cur_data setI4:@([cur_data.I3 doubleValue] - [I4Algorithm I4_20].value)];
        kValue = @([cur_data.I4 doubleValue] / [self.rfManager getRefValue].value);
        i4Temp = [I4Algorithm I4_20];
    } else if (kValue.doubleValue > 20.0) {
        [cur_data setI4:@([cur_data.I3 doubleValue] - [I4Algorithm I4_60].value)];
        kValue = @([cur_data.I4 doubleValue] / [self.rfManager getRefValue].value);
        i4Temp = [I4Algorithm I4_60];
    }

    if (kValue.doubleValue >= 7.5 && kValue.doubleValue <= 25.0) {
        [cur_data setK:kValue];
        [cur_data setBgCurrent:i4Temp.value];
        return cur_data;
    }

    if (kValue.doubleValue < 7.5) {
        cur_data.I4 = @([cur_data.I3 doubleValue] - [I4Algorithm I4_0].value);
        kValue = @([cur_data.I4 doubleValue] / [self.rfManager getRefValue].value);
        i4Temp = [I4Algorithm I4_0];
    } else if (kValue.doubleValue > 25.0) {
        cur_data.I4 = @([cur_data.I3 doubleValue] - [I4Algorithm I4_80].value);
        kValue = @([cur_data.I4 doubleValue] / [self.rfManager getRefValue].value);
        i4Temp = [I4Algorithm I4_80];
    }

    if (kValue.doubleValue > 2.5 && kValue.doubleValue < 40.0) {
        [cur_data setK:kValue];
        [cur_data setBgCurrent:i4Temp.value];
    }

    return cur_data;
}

- (int)calRoc {
    if (self.calIValue == nil) {
        return 0;
    }

    NSInteger size = self.calIValue.count;
    if (size == 0 || size <= 2) {
        return 0;
    }

    double b0 = [self.calIValue[size - 3].bg doubleValue];
    double b1 = [self.calIValue[size - 2].bg doubleValue];
    double b2 = [self.calIValue[size - 1].bg doubleValue];

    if (b0 == 0.0 || b1 == 0.0 || b2 == 0.0) {
        return 0;
    }

    double roc = (0.3 * ((b1 - b0) / 3.0)) + (0.7 * ((b2 - b1) / 3.0));

    if (roc >= 0.1) {
        return 1;
    } else if (roc > 0.05 && roc < 0.1) {
        return 2;
    } else if (roc >= -0.05 && roc <= 0.05) {
        return 3;
    } else if (roc > -0.1 && roc < -0.05) {
        return 4;
    } else if (roc <= -0.1) {
        return 5;
    }

    return 0;
}

- (IValue *)calI4ForK:(IValue *)cur_data {
    IValue *I4_redta = [self I4Algorith:cur_data];
    NSNumber *kValue = @([I4_redta.I4 doubleValue] / [self.rfManager getRefValue].value);
    [I4_redta setK:kValue];
    return I4_redta;
}

- (IValue *)I4Algorith:(IValue *)cur_data {
    IValue *prev_data = self.iValueManager.preIValue;

    [cur_data setI0:cur_data.current];

    [cur_data setI1:@([cur_data.I0 doubleValue] * (1.0 + self.X.X1 * (32.0 - self.X.X2)))];

    if (prev_data != nil && prev_data.I3 != nil) {
        cur_data.I3 = @(self.X.X4 * [cur_data.I1 doubleValue] + (1.0 - self.X.X4) * [prev_data.I3 doubleValue]);
        cur_data.bgCurrent = prev_data.bgCurrent;
        cur_data.k = prev_data.k;

        if (cur_data.rf != nil && cur_data.rf.doubleValue != 0.0) {
            cur_data.rf = prev_data.rf;
            cur_data.rfTime = prev_data.rfTime;
        }
    } else {
        cur_data.bgCurrent = [I4Algorithm I4_40].value;
        cur_data.I3 = cur_data.I1;
    }

    cur_data.I4 = @([cur_data.I3 doubleValue] - cur_data.bgCurrent);

    return cur_data;
}

- (NSNumber *)roundDouble:(double)val precision:(int)precision {
    NSNumber *ret = nil;
    @try {
        double factor = pow(10.0, precision);
        ret = @(floor(val * factor + 0.5) / factor);
    }
    @catch (NSException *exception) {
        NSLog(@"%@", exception.reason);
    }
    return ret;
}

- (NSString *)dateformate:(NSDate *)date {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    return [formatter stringFromDate:date];
}

@end
