package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.util.Log;
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord;
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord;
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GenericXMLRecord;
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.MeterRecord;
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.PageHeader;
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord;
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialDriver;
import com.eveningoutpost.dexdrip.Services.DexCollectionService;
import com.eveningoutpost.dexdrip.Services.DexShareCollectionService;
import com.eveningoutpost.dexdrip.ShareTest;
import org.w3c.dom.Element;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import rx.Observable;
import rx.functions.Action1;
// This code and this particular library are from the NightScout android uploader
// Check them out here: https://github.com/nightscout/android-uploader
// Some of this code may have been modified for use in this project
public class ReadDataShare {
byte[] accumulatedResponse;
private ShareTest mShareTest;
private DexShareCollectionService mCollectionService;
public ReadDataShare(ShareTest aShareTest){
mShareTest = aShareTest;
}
public ReadDataShare(DexShareCollectionService collectionService){
mCollectionService = collectionService;
}
public void getRecentEGVs(final Action1<EGVRecord[]> recordListener) {
final int recordType = Constants.RECORD_TYPES.EGV_DATA.ordinal();
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
@Override
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
};
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
@Override
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
};
readDataBasePageRange(recordType, databasePageRangeCaller);
}
public void getRecentMeterRecords(final Action1<MeterRecord[]> recordListener) {
final int recordType = Constants.RECORD_TYPES.METER_DATA.ordinal();
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
@Override
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
};
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
@Override
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
};
readDataBasePageRange(recordType, databasePageRangeCaller);
}
public void getRecentCalRecords(final Action1<CalRecord[]> recordListener) {
final int recordType = Constants.RECORD_TYPES.CAL_SET.ordinal();
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
@Override
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
};
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
@Override
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
};
readDataBasePageRange(recordType, databasePageRangeCaller);
}
public void getRecentSensorRecords(final Action1<SensorRecord[]> recordListener) {
final int recordType = Constants.RECORD_TYPES.SENSOR_DATA.ordinal();
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
@Override
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
};
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
@Override
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
};
readDataBasePageRange(recordType, databasePageRangeCaller);
}
public void getTimeSinceEGVRecord(final EGVRecord egvRecord, final Action1<Long> timeSinceEgvRecord) {
Action1<Long> tempSystemTimeListener = new Action1<Long>() {
@Override
public void call(Long s) { Observable.just(s - egvRecord.getSystemTimeSeconds()).subscribe(timeSinceEgvRecord); }
};
readSystemTime(tempSystemTimeListener);
}
public void ping(final Action1<Boolean> pingListener) {
Action1<byte[]> pingReader = new Action1<byte[]>() {
@Override
public void call(byte[] s) { Observable.just(read(0, s).getCommand() == Constants.ACK).subscribe(pingListener); }
};
writeCommand(Constants.PING, pingReader);
}
public void readBatteryLevel(final Action1<Integer> batteryLevelListener) {
Action1<byte[]> batteryLevelReader = new Action1<byte[]>() {
@Override //TODO: find out if this should be wrapped in read(s).getData();
public void call(byte[] s) { Observable.just(ByteBuffer.wrap(s).order(ByteOrder.LITTLE_ENDIAN).getInt()).subscribe(batteryLevelListener); }
};
writeCommand(Constants.READ_BATTERY_LEVEL, batteryLevelReader);
}
public void readSerialNumber(final Action1<String> serialNumberListener) {
final Action1<byte[]> manufacturingDataListener = new Action1<byte[]>() {
@Override
public void call(byte[] s) {
Element el = ParsePage(s, Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal());
Observable.just(el.getAttribute("SerialNumber")).subscribe(serialNumberListener);
}
};
readDataBasePage(Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal(), 0, manufacturingDataListener);
}
public void readDisplayTime(final Action1<Date> displayTimeListener) {
Action1<Long> tempSystemTimeListener = new Action1<Long>() {
@Override
public void call(Long s) {
final long systemTime = s;
Action1<Long> tempSystemTimeListener = new Action1<Long>() {
@Override
public void call(Long s) {
Date dateDisplayTime = Utils.receiverTimeToDate(systemTime + s);
Observable.just(dateDisplayTime).subscribe(displayTimeListener); }
};
readDisplayTimeOffset(tempSystemTimeListener);
}
};
readSystemTime(tempSystemTimeListener);
}
public void readSystemTime(final Action1<Long> systemTimeListener) {
Action1<byte[]> systemTimeReader = new Action1<byte[]>() {
@Override
public void call(byte[] s) {
Observable.just(Utils.receiverTimeToDate(ByteBuffer.wrap(read(0,s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt()).getTime()).subscribe(systemTimeListener);
}
};
writeCommand(Constants.READ_SYSTEM_TIME, systemTimeReader);
}
public void readDisplayTimeOffset(final Action1<Long> displayTimeOffsetListener) {
Action1<byte[]> displayTimeOffsetReader = new Action1<byte[]>() {
@Override
public void call(byte[] s) { Observable.just((long) ByteBuffer.wrap(read(0,s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt()).subscribe(displayTimeOffsetListener); }
};
writeCommand(Constants.READ_DISPLAY_TIME_OFFSET, displayTimeOffsetReader);
}
private void readDataBasePageRange(int recordType, final Action1<Integer> databasePageRangeCaller) {
ArrayList<Byte> payload = new ArrayList<Byte>();
payload.add((byte) recordType);
final Action1<byte[]> databasePageRangeListener = new Action1<byte[]>() {
@Override
public void call(byte[] s) {
Observable.just(ByteBuffer.wrap(new ReadPacket(s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt(4)).subscribe(databasePageRangeCaller);
}
};
writeCommand(Constants.READ_DATABASE_PAGE_RANGE, payload, databasePageRangeListener);
}
private <T> T readDataBasePage(final int recordType, int page, final Action1<byte[]> fullPageListener) {
byte numOfPages = 1;
if (page < 0){ throw new IllegalArgumentException("Invalid page requested:" + page); }
ArrayList<Byte> payload = new ArrayList<Byte>();
payload.add((byte) recordType);
byte[] pageInt = ByteBuffer.allocate(4).putInt(page).array();
payload.add(pageInt[3]);
payload.add(pageInt[2]);
payload.add(pageInt[1]);
payload.add(pageInt[0]);
payload.add(numOfPages);
accumulatedResponse = null;
final Action1<byte[]> databasePageReader = new Action1<byte[]>() {
@Override
public void call(byte[] s) {
Log.d("ShareTest", "Database Page Reader received SIZE: " + s.length);
byte[] temp = s;
if (accumulatedResponse == null) {
accumulatedResponse = s;
} else {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(accumulatedResponse);
outputStream.write(temp);
accumulatedResponse = outputStream.toByteArray();
Log.d("ShareTest", "Combined Response length: " + accumulatedResponse.length);
} catch (Exception e) { e.printStackTrace(); }
}
if (temp.length < 20) { Observable.just(accumulatedResponse).subscribe(fullPageListener).unsubscribe(); }
}
};
writeCommand(Constants.READ_DATABASE_PAGES, payload, databasePageReader);
return null;
}
private void writeCommand(int command, ArrayList<Byte> payload, Action1<byte[]> responseListener) {
List<byte[]> packets = new PacketBuilder(command, payload).composeList();
if(mShareTest != null) { mShareTest.writeCommand(packets, 0, responseListener); }
else if (mCollectionService != null) { mCollectionService.writeCommand(packets, 0, responseListener); }
}
private void writeCommand(int command, Action1<byte[]> responseListener) {
List<byte[]> packets = new PacketBuilder(command).composeList();
if(mShareTest != null) { mShareTest.writeCommand(packets, 0, responseListener); }
else if (mCollectionService != null) { mCollectionService.writeCommand(packets, 0, responseListener); }
}
private ReadPacket read(int numOfBytes, byte[] readPacket) {
return new ReadPacket(Arrays.copyOfRange(readPacket, 0, readPacket.length));
}
private <T> T ParsePage(byte[] data, int recordType) { return ParsePage(data, recordType, null); }
private <T> T ParsePage(byte[] data, int recordType, Action1<T> parsedPageReceiver) {
int HEADER_LEN = 28;
PageHeader pageHeader=new PageHeader(data);
int NUM_REC_OFFSET = 4;
int numRec = data[NUM_REC_OFFSET];
int rec_len;
switch (Constants.RECORD_TYPES.values()[recordType]) {
case MANUFACTURING_DATA:
GenericXMLRecord xmlRecord = new GenericXMLRecord(Arrays.copyOfRange(data, HEADER_LEN, data.length - 1));
if(parsedPageReceiver != null) {
Observable.just((T) xmlRecord).subscribe(parsedPageReceiver);
} else {
return (T) xmlRecord;
}
break;
case SENSOR_DATA:
rec_len = 20;
SensorRecord[] sensorRecords = new SensorRecord[numRec];
for (int i = 0; i < numRec; i++) {
int startIdx = HEADER_LEN + rec_len * i;
sensorRecords[i] = new SensorRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
}
if(parsedPageReceiver != null) {
Observable.just((T) sensorRecords).subscribe(parsedPageReceiver);
} else {
return (T) sensorRecords;
}
break;
case EGV_DATA:
rec_len = 13;
EGVRecord[] egvRecords = new EGVRecord[numRec];
for (int i = 0; i < numRec; i++) {
int startIdx = HEADER_LEN + rec_len * i;
egvRecords[i] = new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
}
if(parsedPageReceiver != null) {
Observable.just((T) egvRecords).subscribe(parsedPageReceiver);
} else {
return (T) egvRecords;
}
break;
case METER_DATA:
rec_len = 16;
MeterRecord[] meterRecords = new MeterRecord[numRec];
for (int i = 0; i < numRec; i++) {
int startIdx = HEADER_LEN + rec_len * i;
meterRecords[i] = new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
}
if(parsedPageReceiver != null) {
Observable.just((T) meterRecords).subscribe(parsedPageReceiver);
} else {
return (T) meterRecords;
}
break;
case CAL_SET:
rec_len = 249;
if (pageHeader.getRevision()<=2) { rec_len = 148; }
CalRecord[] calRecords = new CalRecord[numRec];
for (int i = 0; i < numRec; i++) {
int startIdx = HEADER_LEN + rec_len * i;
calRecords[i] = new CalRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
}
if(parsedPageReceiver != null) {
Observable.just((T) calRecords).subscribe(parsedPageReceiver);
} else {
return (T) calRecords;
}
break;
default:
break;
}
Observable.just((T) null).subscribe(parsedPageReceiver);
return (T) null;
}
}