//
//  MicroLifeDataModel.h
//  MicroLifeDeviceSDK
// 1. Analysis tool
// 2. Analysis of each device instruction (rewritten by each device)
//  Created by willy.wu on 2020/11/18.
//

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

/**
 DeviceType:

 - DeviceTypeUnknown: Unknown device type
 - DeviceType3GBP: 3G Blood Pressure Monitor
 - DeviceType4GBP: 4G Blood Pressure Monitor
 - DeviceTypeWeight: Weight Scale
 - DeviceTypeTemperature: Thermometer
 - DeviceTypeOxygen: Oxygen Saturation Monitor
 - DeviceTypeWatchBPHome: Watch BP Home
 - DeviceTypeWatchBPO3: Watch BP O3
 - DeviceTypeWatchBPOffice: Watch BP Office
 - DeviceTypeECG: Electrocardiogram Device
 - DeviceTypeWatchBPM1: Watch BP M1
 - DeviceTypeBGM700: Blood Glucose Monitor 700 Series
 - DeviceTypeBGM800: Blood Glucose Monitor 800 Series
 - DeviceTypePFM: Peak Flow Meter
 - DeviceTypePPG: Photoplethysmography Device
 */
typedef NS_ENUM(NSInteger, MicroLifeDeviceType) {
    DeviceTypeUnknown,
    DeviceType3GBP,
    DeviceType4GBP,
    DeviceTypeWeight,
    DeviceTypeTemperature,
    DeviceTypeOxygen,
    DeviceTypeWatchBPHome,
    DeviceTypeWatchBPO3,
    DeviceTypeWatchBPOffice,
    DeviceTypeECG,
    DeviceTypeWatchBPM1,
    DeviceTypeBGM700,
    DeviceTypeBGM800,
    DeviceTypePFM,
    DeviceTypePPG,
    DeviceType5GBP,
    DeviceTypeWatchBPOfficeVascular
};

/**
 STATUS:

 - MicroLifeMeasurementWait: device wait countdown for next measurement
 - MicroLifeMeasurementStart: device is start BP measurement
 - MicroLifeMeasurementStop: manual press I/O to stop measurement
 */
typedef NS_ENUM(NSInteger, STATUS) {
    MicroLifeMeasurementWait    = 0x01,
    MicroLifeMeasurementStart   = 0x02,
    MicroLifeMeasurementStop    = 0x04,
};

/**
Dformat:Data format which APP requested.

- MicroLifeDNoCBPRaw: No CBP raw data
- MicroLifeDLowCBPRaw: low resolution CBP data (sampling rate =16Hz)
- MicroLifeDFullCBPRaw: full CBP raw data (sampling rate=256Hz)
*/
typedef NS_ENUM(NSInteger, Dformat) {
    MicroLifeDNoCBPRaw      = 0x00,
    MicroLifeDLowCBPRaw     = 0x01,
    MicroLifeDFullCBPRaw    = 0x03
};

/**
 Highest inflation pressure of Auto mode: Auto mode, 140, 160, 180, 200, 220, 240, 280

 The device inflates the cuff using fuzzy logic to proper cuff pressure; the Highest Inflation Pressure is considered as a safeguard pressure

 - MicroLifeHIinfPressureAuto: Auto mode
 - MicroLifeHIinfPressure140: 140
 - MicroLifeHIinfPressure160: 160
 - MicroLifeHIinfPressure180: 180
 - MicroLifeHIinfPressure200: 200
 - MicroLifeHIinfPressure220: 220
 - MicroLifeHIinfPressure240: 240
 - MicroLifeHIinfPressure280: 280
*/
typedef NS_ENUM(NSInteger, HIinfPressure) {
    MicroLifeHIinfPressureAuto  = 0,
    MicroLifeHIinfPressure140   = 140,
    MicroLifeHIinfPressure160   = 160,
    MicroLifeHIinfPressure180   = 180,
    MicroLifeHIinfPressure200   = 200,
    MicroLifeHIinfPressure220   = 220,
    MicroLifeHIinfPressure240   = 240,
    MicroLifeHIinfPressure280   = 280
};

typedef NS_ENUM(NSInteger, Interval) {
    MicroLifeInterval5  = 5,
    MicroLifeInterval10  = 10,
    MicroLifeInterval15  = 15,
    MicroLifeInterval20  = 20,
    MicroLifeInterval30  = 30,
    MicroLifeInterval60  = 60,
    MicroLifeIntervalOff  = 255
};

typedef NS_ENUM(NSInteger, MeasurementTime) {
    MicroLifeMeasurementTimeOff  = 0,
    MicroLifeMeasurementTimel5  = 5,
    MicroLifeMeasurementTime10  = 10,
    MicroLifeMeasurementTime15  = 15,
    MicroLifeMeasurementTime20  = 20,
    MicroLifeMeasurementTime30  = 30,
    MicroLifeMeasurementTime60  = 60
};

typedef NS_ENUM(NSInteger , TransmitRate) {
    TransmitRate256Hz = 1,
    TransmitRate512Hz = 2
};

/**
 Condition Mode
 Raw data:    description    description
 0            RA             right arm
 1            LA             left arm
 2            RALA           right arm & left arm
 3            RALL           right arm & left leg
 4            RARL           right arm & right leg
 5            LARL           left arm & right leg
 6            LALL           left arm & left leg
 254          N/A            No data
 */
typedef NS_ENUM(NSInteger , ConditionMode) {
    ConditionModeRA = 0,
    ConditionModeLA = 1,
    ConditionModeRALA = 2,
    ConditionModeRALL = 3,
    ConditionModeRARL = 4,
    ConditionModeLARL = 5,
    ConditionModeLALL = 6,
    ConditionModeNA = 254
};

/**
 User gender

 - MicroLifeUserGenderMale: Male
 - MicroLifeUserGenderFemale: Female
 - MicroLifeUserGenderHideyoshi: Diverse
 */
typedef NS_ENUM(NSInteger, MicroLifeUserGender) {
    MicroLifeUserGenderMale,
    MicroLifeUserGenderFemale,
    MicroLifeUserGenderHideyoshi
};

/**
 * User's role type
 *
 * - MicroLifeUserRoleTypeGuest: Guest user (temporary measurement, no data saving)
 * - MicroLifeUserRoleTypeNormal: Normal registered user (full features with data history)
 * - MicroLifeUserRoleTypeMultiUser: Multiple user match (requires user selection)
 */
typedef NS_ENUM(NSInteger, MicroLifeUserRoleType) {
    MicroLifeUserRoleTypeGuest = 0x00,
    MicroLifeUserRoleTypeNormal = 0xFF,
    MicroLifeUserRoleTypeMultiUser = 0xAA
};

/**
 Weight unit (kg/lb)

 - MicroLifeEBUnitTypeKG: kg
 - MicroLifeEBUnitTypeLB: lb
 */
typedef NS_ENUM(NSInteger, MicroLifeEBUnitType) {
    MicroLifeEBUnitTypeKG,
    MicroLifeEBUnitTypeLB,
};


typedef NS_ENUM(NSInteger, MAMRest) {
    MAMRestOff  = 0,
    MAMRest1min  = 60,
    MAMRest3min  = 180,
    MAMRest5min  = 300
};

typedef NS_ENUM(NSInteger, MAMInterval) {
    MicroLifeMAMInterval15  = 15,
    MicroLifeMAMInterval30  = 30,
    MicroLifeMAMInterval60  = 60,
    MicroLifeMAMInterval120  = 120
};

typedef NS_ENUM(NSInteger, SsampleTime) {
    MicroLifeSsampleTimeNA = 0,
    MicroLifeSsampleTime30 = 1,
    MicroLifeSsampleTime60 = 2,
    MicroLifeSsampleTime540 = 9,
    MicroLifeSsampleTime1830 = 31
};

typedef NS_ENUM(NSInteger, SampleRate) {
    MicroLifeSampleRate128 = 0,
    MicroLifeSampleRate256 = 1,
    MicroLifeSampleRate512 = 2
};

/// ECG Status:
/// Note: Refer to 2.1.3 ECG measurement flowchart, device will auto update device status (0x05~0x08) during ECG measurement. But if stop ECG measurement, device will stop auto to update status.
/// Device status    Raw data    Description
/// manual enter Bluetooth    0x01    User press i/o to enter Bluetooth mode.
/// Detect new blood pressure data    0x02    After blood pressure measurement (not trigger ECG) , auto enter Bluetooth mode.
/// Detect new blood pressure data, wait ECG trigger    0x03    After blood pressure measurement with arrhythmia, auto enter Bluetooth mode and wait ECG command (25h).
/// Only ECG mode, , wait ECG trigger    0x04    Starts only ECG measurement, wait ECG start command (25h).
/// Wait lead-on stable    0x05    Receive ECG start command 25h, wait user lead-on. Update this command (20h) every 500ms until timeout (30 seconds).
/// Start ECG measurement    0x06    Detect lead on stable, ready to start ECG measurement. Than auto transmit ECG raw data(package type=69h or 70h)
/// Stop ECG measurement    0x07    Stop ECG measurement caused by lead-off, remote stop, user stop or transmit overflow.
/// Succeed and store memory    0x08    Completed measurement and store memory
/// Factory mode    0x09
typedef NS_ENUM(NSInteger, ECGStatus) {
    MicroLifeECGStatusManualEnterBluetooth = 0x01,
    MicroLifeECGStatusDetectNewBloodPressureData = 0x02,
    MicroLifeECGStatusDetectNewBloodPressureDataWaitECGTrigger = 0x03,
    MicroLifeECGStatusOnlyECGModeWaitECGTrigger = 0x04,
    MicroLifeECGStatusWaitLeadOnStable = 0x05,
    MicroLifeECGStatusStartECGMeasurement = 0x06,
    MicroLifeECGStatusStopECGMeasurement = 0x07,
    MicroLifeECGStatusSucceedAndStoreMemory = 0x08,
    MicroLifeECGStatusFactoryMode = 0x09
};

/// ECG code:
/// 0: N/A
/// 1: succeed measurement ECG
/// 45: wait ECG trigger (or not completed by unknown reason)
/// 41: failed by lead off
/// 42: failed by transmit overflow
/// 43: failed by low battery
/// 46: failed by manual stop
/// 47: failed by remote stop.
/// FF: Unknown
typedef NS_ENUM(NSInteger, ECGcode) {
    MicroLifeECGcodeNA = 0,
    MicroLifeECGcodeSucceedMeasurementECG = 1,
    MicroLifeECGcodeWaitECGTrigger = 45,
    MicroLifeECGcodeLeadOff = 41,
    MicroLifeECGcodeTransmitOverflow = 42,
    MicroLifeECGcodeLowBattery = 43,
    MicroLifeECGcodeManualStop = 46,
    MicroLifeECGcodeRemoteStop = 47,
    MicroLifeECGcodeUnknown = 255
};

/// Specimen type:
/// 0: blood
/// 1: QC solution
typedef NS_ENUM(NSInteger, SpecimenType) {
    MicroLifeSpecimenTypeBlood = 0,
    MicroLifeSpecimenTypeQCSolution = 1
};

/// meal status:
/// 0: N/A
/// 1: Before Meal
/// 2: After Meal
typedef NS_ENUM(NSInteger, MealStatus) {
    MicroLifeMealStatusNA = 0,
    MicroLifeMealStatusBeforeMeal = 1,
    MicroLifeMealStatusAfterMeal = 2
};

/// MAP Type:
/// 0: Oscillometric MAP
/// 1: Formula MAP (MAP=1/3 SYS+ 2/3 DIA)
typedef NS_ENUM(NSInteger, MAPType) {
    MicroLifeMAPTypeOscillometric = 0,
    MicroLifeMAPTypeFormula = 1
};

/// User Ethnicity:
/// 1: Asian
/// 2: Caucasian
/// 3: African
/// 4: Hispanic
typedef NS_ENUM(NSInteger, MicroLifeUserEthnicity) {
    MicroLifeUserEthnicityAsian = 1,
    MicroLifeUserEthnicityCaucasian = 2,
    MicroLifeUserEthnicityAfrican = 3,
    MicroLifeUserEthnicityHispanic = 4
};

NS_ASSUME_NONNULL_BEGIN

@interface MicroLifeDataModel : NSObject

@property (nonatomic, assign) MicroLifeDeviceType deviceType;

@property (nonatomic, assign) NSInteger CMD;

@property (nonatomic, assign) NSInteger indexYear;

@property (nonatomic, strong) NSString *dataValue;

@property (nonatomic, strong) NSNumber *success;

@property (nonatomic, strong) NSNumber *null;

@property (nonatomic, strong) NSDate *measureDate;

@property (nonatomic, strong) NSString *UUID;

@property (nonatomic, strong) NSString *Mac;

@property (nonatomic, strong) NSNumber *protocolID;

#pragma mark - Initialization

/// Create instance with value
/// @param value Initial data (NSString hex or NSData)
+ (instancetype)stringWithValue:(id)value;

/// Set data value
/// @param value Data to set (NSString hex or NSData)
- (void)data:(id)value;

#pragma mark - Class Methods (Conversion)

/// Convert Hexadecimal string to Decimal number
/// @param hexadecimal Hex string (e.g., @"0A")
+ (NSNumber *)hexadecimal2Decimal:(NSString *)hexadecimal;

/// Convert Decimal number to Binary string
/// @param decimal Decimal integer
/// @param length Target length of the binary string (padding with 0)
+ (NSString *)decimal2Binary:(NSInteger)decimal length:(NSInteger)length;

/// Convert Binary string to Decimal number
/// @param binary Binary string (e.g., @"1010")
+ (NSNumber *)binary2Decimal:(NSString *)binary;

/// Convert Hex string to String (ASCII/UTF8)
/// @param hexadecimal Hex string
/// @param encoding String encoding
+ (NSString *)hexadecimal2String:(NSString *)hexadecimal cStringUsingEncoding:(NSStringEncoding)encoding;

/// Convert String to Hex string
/// @param valueStr Original string
/// @param encoding String encoding
+ (NSString *)string2Hexadecimal:(NSString *)valueStr cStringUsingEncoding:(NSStringEncoding)encoding;

/// Combine High and Low bytes into an integer
/// @param high High byte value
/// @param low Low byte value
+ (NSInteger)mixHigh:(NSInteger)high Low:(NSInteger)low;

/// Invert byte order (Little Endian <-> Big Endian representation)
/// @param value Integer value
+ (NSString *)inversionValue:(NSInteger)value;

/// Parse Date string
/// [Note] If dateString is nil or empty, returns current date.
/// @param dateString Date string (Format: yyyy-MM-dd HH:mm:ss)
+ (NSDate *)parseDate:(NSString *)dateString;

/// Format MAC Address (e.g., "00:11:22:33:44:55")
/// @param mac MAC data
+ (NSString *)parseMacAddress:(NSData *)mac;

/// Check MAC Address validity
/// 0: Mac None
/// 1: Mac format is correct (Length is multiple of 6)
/// 2: Mac format error
/// @param mac MAC data
+ (NSInteger)checkMac:(NSData *)mac;

#pragma mark - Instance Methods (Parsing Tool)

/// Copy string from current data without consuming it
/// @param offset Number of characters to copy
- (NSString *)copy:(NSInteger)offset;

/// Cut (consume) string from current data
/// @param offset Number of characters to cut
- (NSString *)cut:(NSInteger)offset;

/// Parse Hexadecimal from current data to Decimal
/// @param offset Number of Hex characters to consume
- (NSNumber *)parseDecimal:(NSInteger)offset;

/// Parse Binary from current data to Decimal
/// [Process] Cuts Hex -> Converts to Binary -> Converts to Decimal
/// @param offset Number of Hex characters to consume
- (NSNumber *)parseDecimalby2:(NSInteger)offset;

/// Parse Binary String
///
/// Extracts a Hex string of the specified length (offset) from the current `dataValue` and converts it into a binary string.
/// The output is **Big-Endian (MSB First)**: the leftmost character is the Most Significant Bit (Bit 7), and the rightmost is the Least Significant Bit (Bit 0).
///
/// Example: Hex "89" -> Binary "10001001"
///
/// @param offset The number of Hex characters to consume (e.g., 2 for 1 Byte).
/// @param length The length of the resulting binary string (typically 8).
/// @return A binary string representation.
/// @note Use `parseDecimalby2:` on the result to parse bits from left to right (Bit 7 -> Bit 0).
- (NSString *)parseBinary:(NSInteger)offset length:(NSInteger)length;

/// Parse Hexadecimal from current data to String (ASCII)
/// @param offset Number of Hex characters to consume
- (NSString *)parseString:(NSInteger)offset;

/// Parse String from current data to Hexadecimal string
/// @param offset Number of characters to consume
- (NSString *)parseHexadecimal:(NSInteger)offset;

/// Parse Year
/// [Note] Adds 2000 and indexYear to the parsed value.
/// @param offset Number of Hex characters to consume
- (NSNumber *)parseYear:(NSInteger)offset;

#pragma mark - Command analysis of each device (rewrite by device)
/// <#Description#>
/// @param deviceType <#deviceType description#>
/// @param CMD <#CMD description#>
/// @param value <#value description#>
/// @param UUID <#UUID description#>
/// @param mac <#mac description#>
/// @param protocolID <#protocolID description#>
- (void)analysis:(MicroLifeDeviceType)deviceType CMD:(NSInteger)CMD Data:(id)value UUID:(NSString *)UUID Mac:(NSData *)mac ProtocolID:(NSNumber *)protocolID;

/// <#Description#>
/// @param replyACK <#replyACK description#>
/// @param replyNullACK <#replyNullACK description#>
- (void)replyACK:(BOOL)replyACK NullACK:(BOOL)replyNullACK;

/// <#Description#>
/// @param indexYear <#yearIndex description#>
- (void)indexYear:(NSUInteger)indexYear;

#pragma mark - Dictionary object conversion
/// Dictionary to model.
/// @param value Dictionary (NSDictionary) or json format character.
+ (instancetype)parseObjectWithKeyValues:(id)value;
+ (NSArray* _Nonnull)parseObjectArrayWithKeyValuesArray:(NSArray* const _Nonnull)array;

/// to Dictionary.
- (NSMutableDictionary *)parseDictionary;

//Get the type of a model object in an array
- (NSDictionary *)gainClassTypes;
@end

NS_ASSUME_NONNULL_END
