/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Alexandre Normand
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.glukit.dexcom.sync.responses;
import com.google.common.base.Throwables;
import com.google.common.primitives.UnsignedInts;
import org.glukit.dexcom.sync.DataInputFactory;
import org.glukit.dexcom.sync.DecodingUtils;
import org.glukit.dexcom.sync.model.DatabasePage;
import org.glukit.dexcom.sync.model.DatabasePageHeader;
import org.glukit.dexcom.sync.model.RecordType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.IOException;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static org.glukit.dexcom.sync.DecodingUtils.toHexString;
/**
* Response for {@link org.glukit.dexcom.sync.model.ReceiverCommand#ReadDatabasePages}.
*
* @author alexandre.normand
*/
public class DatabasePagesResponse extends GenericResponse {
private static Logger LOGGER = LoggerFactory.getLogger(DatabasePagesResponse.class);
private static final int PAGE_HEADER_SIZE = 28;
private static final int PAGE_DATA_SIZE = 500;
private List<DatabasePage> pages;
public DatabasePagesResponse(DataInputFactory dataInputFactory) {
super(dataInputFactory);
}
@Override
public void fromBytes(byte[] responseAsBytes) {
this.pages = newArrayList();
ByteArrayInputStream inputStream = new ByteArrayInputStream(responseAsBytes);
DataInput dataInput = this.dataInputFactory.create(inputStream);
try {
int available = inputStream.available();
while (available > 0) {
LOGGER.debug(format("Available bytes remaining [%d]", available));
if (available < PAGE_HEADER_SIZE + PAGE_DATA_SIZE) {
String message =
format("Some bytes are still available but not enough for a page, something is buggy. " +
"Remaining count: [%d]", available);
throw new IllegalStateException(message);
}
byte[] headerBytes = new byte[PAGE_HEADER_SIZE];
dataInput.readFully(headerBytes, 0, PAGE_HEADER_SIZE);
LOGGER.debug(format("Parsing header from bytes [%s]", toHexString(headerBytes)));
DatabasePageHeader pageHeader = readPageHeader(headerBytes);
byte[] pageData = new byte[PAGE_DATA_SIZE];
dataInput.readFully(pageData, 0, PAGE_DATA_SIZE);
available = inputStream.available();
LOGGER.debug(format("Parsing page data from bytes [%s]", toHexString(pageData)));
DatabasePage page = new DatabasePage(pageHeader, pageData);
pages.add(page);
}
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
private DatabasePageHeader readPageHeader(byte[] headerBytes) {
try {
DataInput dataInput = this.dataInputFactory.create(new ByteArrayInputStream(headerBytes));
long firstRecordIndex = UnsignedInts.toLong(dataInput.readInt());
long numberOfRecords = UnsignedInts.toLong(dataInput.readInt());
RecordType recordType = RecordType.fromId(dataInput.readByte());
byte revision = dataInput.readByte();
long pageNumber = UnsignedInts.toLong(dataInput.readInt());
long reserved2 = UnsignedInts.toLong(dataInput.readInt());
long reserved3 = UnsignedInts.toLong(dataInput.readInt());
long reserved4 = UnsignedInts.toLong(dataInput.readInt());
int crc = dataInput.readUnsignedShort();
int expectedCrc = DecodingUtils.getCrc16(headerBytes, 0, PAGE_HEADER_SIZE - 2);
if (crc != expectedCrc) {
throw new IllegalStateException(format("Invalid crc, expected [%s], received [%s]",
Integer.toHexString(expectedCrc), Integer.toHexString(crc)));
}
return new DatabasePageHeader(firstRecordIndex, numberOfRecords, recordType, revision,
pageNumber, reserved2, reserved3, reserved4, crc);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
protected List<DatabasePage> getPages() {
return pages;
}
}