#!/usr/bin/env python

from construct import *

BLOCK_TYPE = Enum(Int8ul,
    RESERVED            = 0x00,
    ARBITRARY_TIMESTAMP = 0x01,
    CONSTANT_FREQUENCY  = 0x02,
)

SAMPLE_TYPE = Enum(Int16ul,
    RESERVED              = 0x0000,
    TEMPERATURE           = 0x0001,
    ACCELERATION          = 0x0002,
    ANGULAR_RATE          = 0x0003,
    VOLTAGE               = 0x0004,
    ECG                   = 0x0005,
    HUMIDITY              = 0x0006,
    PRESSURE              = 0x0007,
    MAGNETIC_FLUX_DENSITY = 0x0008,
    BINARY_DATA           = 0x0009,
    ILLUMINANCE           = 0x000a,

    CUSTOM                = 0xF000,
)

resd_header = Struct(
    "magic" / Const(b"RESD"),
    "version" / Int8ul,
    "reserved" / Padding(3)
)

blob = Struct(
    "size" / Rebuild(Int32ul, len_(this.data)),
    "data" / Int8ul[this.size],
)

data_block_metadata_item = Struct(
    "key" / NullTerminated(GreedyRange(Int8ub)),
    "type" / Int8ul,
    "value" / Switch(this.type,
    {
        0x01: Int8sl,
        0x02: Int8ul,
        0x03: Int16sl,
        0x04: Int16ul,
        0x05: Int32sl,
        0x06: Int32ul,
        0x07: Int64sl,
        0x08: Int64ul,
        0x09: Float32l,
        0x0A: Float64l,
        0x0B: NullTerminated(GreedyRange(Int8ul)),
        0x0C: blob,
    }),
)

data_block_metadata = Struct(
    "size" / Int64ul,
    "items" / FixedSized(this.size, GreedyRange(data_block_metadata_item)),
)

data_block_sample = lambda sample_type: Switch(sample_type, {
    "TEMPERATURE": Int32sl,
    "ACCELERATION": Struct(
        "x" / Int32sl,
        "y" / Int32sl,
        "z" / Int32sl,
    ),
    "ANGULAR_RATE": Struct(
        "x" / Int32sl,
        "y" / Int32sl,
        "z" / Int32sl,
    ),
    "VOLTAGE": Int32ul,
    "ECG": Int32sl,
    "HUMIDITY": Int32ul,
    "PRESSURE": Int64ul,
    "MAGNETIC_FLUX_DENSITY": Struct(
        "x" / Int32sl,
        "y" / Int32sl,
        "z" / Int32sl,
    ),
    "BINARY_DATA": blob,
    "ILLUMINANCE": Int32ul,
})

data_block_sample_arbitrary = lambda sample_type: Struct(
    "timestamp" / Int64ul,
    "sample" / data_block_sample(sample_type)
)

data_block_sample_arbitrary_subheader = Struct(
    "start_time" / Int64ul,
)

data_block_sample_frequency = lambda sample_type: Struct(
    "sample" / data_block_sample(sample_type)
)

data_block_sample_frequency_subheader = Struct(
    "start_time" / Int64ul,
    "period" / Int64ul,
)

data_block_sample_single = lambda type_, sample_type: Switch(type_, {
    "ARBITRARY_TIMESTAMP": data_block_sample_arbitrary(sample_type),
    "CONSTANT_FREQUENCY": data_block_sample_frequency(sample_type),
})

data_block_subheader = Switch(this.header.block_type, {
    "ARBITRARY_TIMESTAMP": data_block_sample_arbitrary_subheader,
    "CONSTANT_FREQUENCY": data_block_sample_frequency_subheader
})

data_block_header = Struct(
    "block_type" / BLOCK_TYPE,
    "sample_type" / SAMPLE_TYPE,
    "channel_id" / Int16ul,
    "data_size" / Int64ul,
)

data_block = Struct(
    "header" / data_block_header,
    "subheader" / data_block_subheader,
    "metadata" / data_block_metadata,
    "samples" / GreedyRange(data_block_sample_single(this.header.block_type, this._.header.sample_type))
)

resd = Struct(
    "header" / resd_header,
    "blocks" / GreedyRange(data_block)
)
