//
//  ReactNativeBiometrics.m
//
//  Created by Brandon Hines on 4/3/18.
//

#import "ReactNativeBiometrics.h"
#import <LocalAuthentication/LocalAuthentication.h>
#import <Security/Security.h>
#import <React/RCTConvert.h>
#import <Foundation/Foundation.h>
#import <SCCSR.h>
#import <Security/Security.h>
@implementation ReactNativeBiometrics

RCT_EXPORT_MODULE(ReactNativeBiometrics);

RCT_EXPORT_METHOD(isSensorAvailable: (NSDictionary *)params
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
  LAContext *context = [[LAContext alloc] init];
  NSError *authError = nil;

  // Luôn dùng LAPolicyDeviceOwnerAuthentication để bắt lỗi passcode chưa đặt
  BOOL canEvaluate = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError];

  if (canEvaluate) {
    NSString *biometryType = [self getBiometryType:context];
    NSDictionary *result = @{
      @"available": @(YES),
      @"biometryType": biometryType
    };
    resolve(result);
  } else {
    NSInteger errorCode = authError.code;
    NSString *errorMessage;

    switch (errorCode) {
      case LAErrorPasscodeNotSet:
        errorCode = -16;
        errorMessage = @"Bạn chưa đặt mật mã cho thiết bị.";
        break;
      case LAErrorBiometryNotAvailable:
        errorCode = -4;
        errorMessage = @"Thiết bị không hỗ trợ xác thực sinh trắc học.";
        break;
      case LAErrorBiometryNotEnrolled:
        errorCode = -12;
        errorMessage = @"Bạn chưa đăng ký Face ID hoặc Touch ID.";
        break;
      case LAErrorBiometryLockout:
        errorCode = -11;
        errorMessage = @"Face ID / Touch ID đã bị khóa do nhập sai quá nhiều lần. Cần nhập mật mã.";
        break;
      case LAErrorUserCancel:
        errorMessage = @"Bạn đã hủy xác thực.";
        break;
      case LAErrorUserFallback:
        errorMessage = @"Bạn đã chọn phương thức đăng nhập khác.";
        break;
      case LAErrorSystemCancel:
      case LAErrorAppCancel:
        errorCode = -2;
        errorMessage = @"Xác thực đã bị hệ thống hoặc ứng dụng hủy.";
        break;
      default:
        errorCode = -9;
        errorMessage = authError.localizedDescription ?: @"Không thể sử dụng xác thực.";
        break;
    }

    NSDictionary *result = @{
      @"available": @(NO),
      @"message": errorMessage,
      @"code": @(errorCode)
    };
    resolve(result);
  }
}



RCT_EXPORT_METHOD(createKeys: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    CFErrorRef error = NULL;
    // BOOL allowDeviceCredentials = [RCTConvert BOOL:params[@"allowDeviceCredentials"]];
    NSString *keytag = [RCTConvert NSString:params[@"keytag"]];
    NSNumber *keytype = [RCTConvert NSNumber:params[@"keytype"]];
      int type = [keytype intValue];
    NSNumber *keysize = [RCTConvert NSNumber:params[@"keysize"]];
      NSNumber *size = @2048;
      if(type != 0){
          reject(@"storage_error", @"Chỉ hỗ trợ genkey rsa (keytype = 0)", nil);
          return;
      }
      if([keysize intValue] > 0){
          size = keysize;
      }
      
    // SecAccessControlCreateFlags secCreateFlag = kSecAccessControlBiometryAny;

    // if (allowDeviceCredentials == TRUE) {
    //   secCreateFlag = kSecAccessControlUserPresence;
    // }

    SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                                                                    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                                                                    kSecAccessControlUserPresence, &error);
    if (sacObject == NULL || error != NULL) {
      NSString *errorString = [NSString stringWithFormat:@"SecItemAdd can't create sacObject: %@", error];
      reject(@"storage_error", errorString, nil);
      return;
    }

      NSData *biometricKeyTag = [self getBiometricKeyTag:keytag];
    NSDictionary *keyAttributes = @{
                                    (id)kSecClass: (id)kSecClassKey,
                                    (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
                                    (id)kSecAttrKeySizeInBits: size,
                                    (id)kSecPrivateKeyAttrs: @{
                                        (id)kSecAttrIsPermanent: @YES,
                                        // (id)kSecUseAuthenticationUI: (id)kSecUseAuthenticationUIAllow,
                                        (id)kSecAttrApplicationTag: biometricKeyTag,
                                        (id)kSecAttrAccessControl: (__bridge_transfer id)sacObject
                                        }
                                    };

      [self deleteBiometricKey:keytag];
    NSError *gen_error = nil;
    id privateKey = CFBridgingRelease(SecKeyCreateRandomKey((__bridge CFDictionaryRef)keyAttributes, (void *)&gen_error));

    if(privateKey != nil) {
      id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
      CFDataRef publicKeyDataRef = SecKeyCopyExternalRepresentation((SecKeyRef)publicKey, nil);
      NSData *publicKeyData = (__bridge NSData *)publicKeyDataRef;
      NSData *publicKeyDataWithHeader = [self addHeaderPublickey:publicKeyData];
      NSString *publicKeyString = [publicKeyDataWithHeader base64EncodedStringWithOptions:0];

      NSDictionary *result = @{
        @"publicKey": publicKeyString,
      };
      resolve(result);
    } else {
      NSString *message = [NSString stringWithFormat:@"Key generation error: %@", gen_error];
      reject(@"storage_error", message, nil);
    }
  });
}

RCT_EXPORT_METHOD(getPublicKey:(NSString *)keytag resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // NSDictionary *result = @{
    //     @"publicKey": @"ios",
    //   };
    //   resolve(result);
    NSData *biometricKeyTag = [self getBiometricKeyTag:keytag];
    NSDictionary *query = @{
        (id)kSecClass: (id)kSecClassKey,
        (id)kSecAttrApplicationTag: biometricKeyTag,
        (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
        (id)kSecReturnRef: @YES,
        (id)kSecUseOperationPrompt: @"Get publickey"
        
                            };
    SecKeyRef privateKey;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);
      if (status == errSecSuccess) {
          id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
          CFDataRef publicKeyDataRef = SecKeyCopyExternalRepresentation((SecKeyRef)publicKey, nil);
          NSData *publicKeyData = (__bridge NSData *)publicKeyDataRef;
          NSData *publicKeyDataWithHeader = [self addHeaderPublickey:publicKeyData];
          NSString *publicKeyString = [publicKeyDataWithHeader base64EncodedStringWithOptions:0];

          NSDictionary *result = @{
            @"publicKey": publicKeyString,
          };
          resolve(result);
      }
      else{
          NSString *message = [NSString stringWithFormat:@"Get public error: %@", @"No get publickey"];
          reject(@"storage_error", message, nil);
      }
  });
}
RCT_REMAP_METHOD(getUniqueId,
                 getUniqueIdWithResolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSString *key = @"com.bnnsoftvn.uniqueid";
  NSString *uniqueId = [self getFromKeychain:key];

  if (!uniqueId) {
    uniqueId = [[NSUUID UUID] UUIDString];
    [self saveToKeychain:key value:uniqueId];
  }

  resolve(uniqueId);
}

- (void)saveToKeychain:(NSString *)key value:(NSString *)value {
  NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];

  NSDictionary *query = @{
    (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
    (__bridge id)kSecAttrAccount: key,
    (__bridge id)kSecValueData: data
  };

  SecItemAdd((__bridge CFDictionaryRef)query, NULL);
}

- (NSString *)getFromKeychain:(NSString *)key {
  NSDictionary *query = @{
    (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
    (__bridge id)kSecAttrAccount: key,
    (__bridge id)kSecReturnData: @YES,
    (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
  };

  CFTypeRef result = NULL;
  OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);

  if (status == errSecSuccess) {
    NSData *data = (__bridge_transfer NSData *)result;
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  }

  return nil;
}
RCT_EXPORT_METHOD(createCsr: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSString *promptMessage = [RCTConvert NSString:params[@"promptMessage"]];
    NSString *keytag = [RCTConvert NSString:params[@"keytag"]];
    NSString *cn = [RCTConvert NSString:params[@"commonName"]];
    NSString *ou = [RCTConvert NSString:params[@"organizationalUnit"]];
    NSString *o = [RCTConvert NSString:params[@"organization"]];
    NSString *l = [RCTConvert NSString:params[@"locality"]];
    NSString *st = [RCTConvert NSString:params[@"state"]];
    NSString *c = [RCTConvert NSString:params[@"country"]];

    if (cn == nil || [cn isEqualToString:@""]) {
      NSDictionary *result = @{
        @"success": @(NO),
        @"message": @"CommonName không được để trống!"
      };
      resolve(result);
      return;
    }

    NSData *biometricKeyTag = [self getBiometricKeyTag:keytag];
    NSDictionary *query = @{
      (id)kSecClass: (id)kSecClassKey,
      (id)kSecAttrApplicationTag: biometricKeyTag,
      (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
      (id)kSecReturnRef: @YES,
      (id)kSecUseOperationPrompt: promptMessage,
      // (id)kSecUseAuthenticationUI: (id)kSecUseAuthenticationUIAllow
    };

    SecKeyRef privateKey;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);

    if (status == errSecSuccess) {
      id publicKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)privateKey));
      CFDataRef publicKeyDataRef = SecKeyCopyExternalRepresentation((SecKeyRef)publicKey, nil);
      NSData *publicKeyData = (__bridge_transfer NSData *)publicKeyDataRef;

      SCCSR *sccsr = [[SCCSR alloc] init];
      sccsr.commonName = cn;
      sccsr.organizationName = o;
      sccsr.organizationalUnitName = ou;
      sccsr.countryName = c;
      sccsr.stateName = st;
      sccsr.localityName = l;

      NSData *certificateRequest = [sccsr build:publicKeyData privateKey:privateKey];
      NSString *csrBase64 = [certificateRequest base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];

      if (csrBase64 != nil) {
        NSDictionary *result = @{
          @"success": @(YES),
          @"csr": csrBase64
        };
        resolve(result);
      } else {
        NSDictionary *result = @{
          @"success": @(NO),
          @"message": @"Có lỗi xảy ra, sinh CSR thất bại!"
        };
        resolve(result);
      }
    } else {
      NSString *message = [self keychainErrorMessage:status];
      NSDictionary *result = @{
        @"success": @(NO),
        @"message": message,
        @"code": @(status)
      };
      resolve(result);
    }
  });
}

// Thêm phương thức helper vào cùng file .m (có thể ở cuối file)
- (NSString *)keychainErrorMessage:(OSStatus)status {
  switch (status) {
    case errSecItemNotFound:
      return @"Không tìm thấy khóa riêng tư tương ứng. Có thể bạn chưa tạo khóa hoặc đã xóa.";
    case errSecAuthFailed:
      return @"Xác thực sinh trắc học thất bại hoặc bị huỷ.";
    case errSecUserCanceled:
      return @"Bạn đã huỷ xác thực.";
    case errSecInteractionNotAllowed:
      return @"Không thể hiển thị lời nhắc xác thực. Hãy đảm bảo ứng dụng đang ở chế độ foreground.";
    case errSecDecode:
      return @"Không thể giải mã dữ liệu khóa.";
    case errSecParam:
      return @"Tham số không hợp lệ khi truy vấn khóa.";
    default:
      return [NSString stringWithFormat:@"Lỗi không xác định (mã lỗi: %d)", (int)status];
  }
}

RCT_EXPORT_METHOD(deleteKeys:(NSString *)keytag resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      BOOL biometricKeyExists = [self doesBiometricKeyExist:keytag];

    if (biometricKeyExists) {
        OSStatus status = [self deleteBiometricKey:keytag];

      if (status == noErr) {
        NSDictionary *result = @{
          @"keysDeleted": @(YES),
        };
        resolve(result);
      } else {
        NSString *message = [NSString stringWithFormat:@"Key not found: %@",[self keychainErrorToString:status]];
        reject(@"deletion_error", message, nil);
      }
    } else {
        NSDictionary *result = @{
          @"keysDeleted": @(NO),
        };
        resolve(result);
    }
  });
}

RCT_EXPORT_METHOD(createSignature: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSString *promptMessage = [RCTConvert NSString:params[@"promptMessage"]];
    NSString *payload = [RCTConvert NSString:params[@"payload"]];
    NSString *keytag = [RCTConvert NSString:params[@"keytag"]];
    NSNumber *type = [RCTConvert NSNumber:params[@"type"]];

    NSData *biometricKeyTag = [self getBiometricKeyTag:keytag];
    NSDictionary *query = @{
      (id)kSecClass: (id)kSecClassKey,
      (id)kSecAttrApplicationTag: biometricKeyTag,
      (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
      (id)kSecReturnRef: @YES,
      (id)kSecUseOperationPrompt: promptMessage,
      (id)kSecUseAuthenticationUI: (id)kSecUseAuthenticationUIAllow
    };

    SecKeyRef privateKey;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);

    if (status == errSecSuccess) {
      NSError *error = nil;
      NSData *dataToSign = [payload dataUsingEncoding:NSUTF8StringEncoding];
      if ([type intValue] == 1) {
        dataToSign = [[NSData alloc] initWithBase64EncodedString:payload options:0];
      }

      NSData *signature = CFBridgingRelease(SecKeyCreateSignature(
        privateKey,
        kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256,
        (CFDataRef)dataToSign,
        (void *)&error
      ));

      if (signature != nil) {
        NSString *signatureString = [signature base64EncodedStringWithOptions:0];
        NSDictionary *result = @{
          @"success": @(YES),
          @"signature": signatureString
        };
        resolve(result);
      } else {
        NSDictionary *result = @{
          @"success": @(NO),
          @"message": [NSString stringWithFormat:@"Không thể ký dữ liệu: %@", error.localizedDescription ?: @"Lỗi không xác định"],
          @"code": @(error.code)
        };
        resolve(result);
      }
    } else {
      // Chuyển lỗi keychain thành thông báo tiếng Việt
      NSString *message = [self keychainErrorMessage:status];
      NSDictionary *result = @{
        @"success": @(NO),
        @"message": message,
        @"code": @(status)
      };
      resolve(result);
    }
  });
}


RCT_EXPORT_METHOD(simplePrompt: (NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSString *promptMessage = [RCTConvert NSString:params[@"promptMessage"]];

    LAContext *context = [[LAContext alloc] init];
    context.localizedFallbackTitle = @""; // Không hiển thị fallback

    [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
            localizedReason:promptMessage
                      reply:^(BOOL success, NSError *biometricError) {
      if (success) {
        NSDictionary *result = @{
          @"success": @(YES)
        };
        resolve(result);
      } else {
        // Chuyển mã lỗi thành thông điệp tiếng Việt
        NSString *errorMessage = @"Không thể xác thực.";
        NSInteger errorCode = biometricError.code;

        switch (errorCode) {
        case -6: // LAErrorBiometryNotAvailable
          errorCode = -4;
          errorMessage = @"Thiết bị không hỗ trợ xác thực sinh trắc học.";
          break;
        case -7: // LAErrorBiometryNotEnrolled
          errorCode = -12;
          errorMessage = @"Bạn chưa đăng ký Face ID hoặc Touch ID.";
          break;
        case -8: // LAErrorBiometryLockout
          errorCode = -11;
          errorMessage = @"Face ID / Touch ID đã bị khóa do nhập sai quá nhiều lần. Cần nhập mật mã.";
          break;
        case -5: // LAErrorPasscodeNotSet
          errorMessage = @"Bạn chưa đặt mật mã cho thiết bị.";
          break;
        case -2: // LAErrorUserCancel
          errorMessage = @"Bạn đã hủy xác thực.";
          break;
        case -3: // LAErrorUserFallback
          errorMessage = @"Bạn đã chọn phương thức đăng nhập khác.";
          break;
        case -4: // LAErrorSystemCancel
          errorCode = -2;
          errorMessage = @"Hệ thống đã hủy xác thực (ví dụ: có cuộc gọi đến).";
          break;
        case -9: // LAErrorAppCancel
          errorCode = -2;
          errorMessage = @"Ứng dụng đã hủy xác thực.";
          break;
        default:
          errorCode = -9;
          errorMessage = biometricError.localizedDescription ?: @"Không thể sử dụng xác thực sinh trắc học.";
          break;
      }

        NSDictionary *errorResult = @{
          @"success": @(NO),
          @"error": errorMessage,
          @"code": @(errorCode)
        };
        resolve(errorResult); // Trả về lỗi qua resolve thay vì reject
      }
    }];
  });
}


RCT_EXPORT_METHOD(biometricKeysExist:(NSString *)keytag resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      BOOL biometricKeyExists = [self doesBiometricKeyExist:keytag];

    if (biometricKeyExists) {
      NSDictionary *result = @{
        @"keysExist": @(YES)
      };
      resolve(result);
    } else {
      NSDictionary *result = @{
        @"keysExist": @(NO)
      };
      resolve(result);
    }
  });
}

- (NSData *) getBiometricKeyTag:(NSString *)keytag {
  NSString *biometricKeyAlias = @"com.rnbiometrics.biometricKey";
    if(keytag != nil && ![keytag  isEqual: @""]){
        biometricKeyAlias = keytag;
    }
  NSData *biometricKeyTag = [biometricKeyAlias dataUsingEncoding:NSUTF8StringEncoding];
  return biometricKeyTag;
}

- (BOOL) doesBiometricKeyExist:(NSString *)keytag {
    NSData *biometricKeyTag = [self getBiometricKeyTag:keytag];
  NSDictionary *searchQuery = @{
                                (id)kSecClass: (id)kSecClassKey,
                                (id)kSecAttrApplicationTag: biometricKeyTag,
                                (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
                                (id)kSecUseAuthenticationUI: (id)kSecUseAuthenticationUIFail
                                };

  OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchQuery, nil);
  return status == errSecSuccess || status == errSecInteractionNotAllowed;
}

-(OSStatus) deleteBiometricKey:(NSString *)keytag {
    NSData *biometricKeyTag = [self getBiometricKeyTag:keytag];
  NSDictionary *deleteQuery = @{
                                (id)kSecClass: (id)kSecClassKey,
                                (id)kSecAttrApplicationTag: biometricKeyTag,
                                (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA
                                };

  OSStatus status = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
  return status;
}

- (NSString *)getBiometryType:(LAContext *)context
{
  if (@available(iOS 11, *)) {
    return (context.biometryType == LABiometryTypeFaceID) ? @"FaceID" : @"TouchID";
  }

  return @"TouchID";
}

- (NSString *)keychainErrorToString:(OSStatus)error {
  NSString *message = [NSString stringWithFormat:@"%ld", (long)error];

  switch (error) {
    case errSecSuccess:
      message = @"success";
      break;

    case errSecDuplicateItem:
      message = @"error item already exists";
      break;

    case errSecItemNotFound :
      message = @"error item not found";
      break;

    case errSecAuthFailed:
      message = @"error item authentication failed";
      break;

    default:
      break;
  }

  return message;
}


- (NSData *)addHeaderPublickey:(NSData *)publicKeyData {

    unsigned char builder[15];
    NSMutableData * encKey = [[NSMutableData alloc] init];
    unsigned long bitstringEncLength;

    static const unsigned char _encodedRSAEncryptionOID[15] = {

        /* Sequence of length 0xd made up of OID followed by NULL */
        0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
        0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00

    };
    // When we get to the bitstring - how will we encode it?
    if  ([publicKeyData length ] + 1  < 128 )
        bitstringEncLength = 1 ;
    else
        bitstringEncLength = (([publicKeyData length ] +1 ) / 256 ) + 2 ;
    //
    //        // Overall we have a sequence of a certain length
    builder[0] = 0x30;    // ASN.1 encoding representing a SEQUENCE
    //        // Build up overall size made up of -
    //        // size of OID + size of bitstring encoding + size of actual key
    size_t i = sizeof(_encodedRSAEncryptionOID) + 2 + bitstringEncLength + [publicKeyData length];
    size_t j = encodeLength(&builder[1], i);
    [encKey appendBytes:builder length:j +1];

    // First part of the sequence is the OID
    [encKey appendBytes:_encodedRSAEncryptionOID
                 length:sizeof(_encodedRSAEncryptionOID)];

    // Now add the bitstring
    builder[0] = 0x03;
    j = encodeLength(&builder[1], [publicKeyData length] + 1);
    builder[j+1] = 0x00;
    [encKey appendBytes:builder length:j + 2];

    // Now the actual key
    [encKey appendData:publicKeyData];

    return encKey;
}

size_t encodeLength(unsigned char * buf, size_t length) {

    // encode length in ASN.1 DER format
    if (length < 128) {
        buf[0] = length;
        return 1;
    }

    size_t i = (length / 256) + 1;
    buf[0] = i + 0x80;
    for (size_t j = 0 ; j < i; ++j) {
        buf[i - j] = length & 0xFF;
        length = length >> 8;
    }

    return i + 1;
}

@end
