# ADTF File Python Module

The `adtf_file` module provides Python bindings for
the [ADTF File Library](https://gitlab.com/digitalwerk/solutions/adtf_content/adtf_base/adtf_file_library), enabling
users to read, inspect, process, and
export ADTF data files (e.g., `.adtfdat`) directly within Python.

## Features

* **Sequential Reading:** Iterate through file items (Samples, StreamTypes, Triggers) efficiently.
* **Random Access:** Seek specific timestamps or indices using the `SeekableReader`.
* **Data Decoding:** Built-in DDL (Data Definition Language) decoding to convert binary sample buffers into accessible
  Python objects.
* **Demultiplexing & Export:** Process streams and export data (e.g., to CSV) using ADTF processors.
* **Plugin Support:** Load external ADTF file plugins to handle custom formats or processors.

## Basic Usage

### Opening a Reader

To read a file sequentially, use `create_reader`. This provides access to file metadata, stream definitions, and the
items within the file.

```python
import adtf_file
import sys

filename = "recording.adtfdat"
reader = adtf_file.create_reader(filename)

print(f"File Version: {reader.file_version}")
print(f"Description: {reader.description}")

# Print available streams
for stream in reader.streams:
    print(f"Stream: {stream.name} (ID: {stream.stream_id})")

```

### Iterating Over Items

The reader yields `FileItem` objects. You must check the `type` of the item to determine if it is a Sample, a StreamType
update, or a Trigger.

```python
while True:
    # get_next_item returns None when EOF is reached
    item = reader.get_next_item()
    if not item:
        break

    print(f"Time: {item.time_stamp}, StreamID: {item.stream_id}")

    if item.type == adtf_file.ItemType.Sample:
        sample = item.sample
        print(f"  Sample size: {sample.buffer.nbytes} bytes")

    elif item.type == adtf_file.ItemType.StreamType:
        print(f"  Stream Type Definition Update")

    elif item.type == adtf_file.ItemType.Trigger:
        print(f"  Trigger Event")

```

### Decoding Sample Data

Binary sample data can be decoded into Python objects using the `SampleDecoder`. The decoder requires the `stream_type`
associated with the sample stream.

```python
# Map to store decoders for each stream ID
decoders = {}

# Initialize decoders from the file header streams
for stream in reader.streams:
    decoders[stream.stream_id] = adtf_file.SampleDecoder(stream.initial_type)

reader = adtf_file.create_reader(filename)
while True:
    item = reader.get_next_item()
    if not item: break

    # Update decoder if the stream type changes mid-file
    if item.type == adtf_file.ItemType.StreamType:
        decoders[item.stream_id] = adtf_file.SampleDecoder(item.stream_type)

    # Decode samples
    if item.type == adtf_file.ItemType.Sample:
        decoder = decoders.get(item.stream_id)
        if decoder:
            # decoded_data is a SimpleNamespace with attributes matching the DDL
            decoded_data = decoder.decode(item.sample)
            print(decoded_data)

```

## Advanced Usage

### Random Access (Seekable Reader)

If the underlying file format supports it (e.g., indexed files), you can use `create_seekablereader` to jump to specific
locations.

```python
reader = adtf_file.create_seekablereader("recording.adtfdat")

# Find index for a specific timestamp (in microseconds)
target_time = 1000000
index = reader.get_item_index_for_timeStamp(target_time)

# Jump to that item
reader.seek_to(index)
item = reader.get_next_item()

```

### Exporting / Processing (Demultiplexer)

The `Demultiplexer` class allows you to run ADTF processors (like exporters) on the file content.

```python
def progress_callback(progress_float):
    print(f"Progress: {progress_float * 100:.2f}%")
    return True  # Return False to cancel


# Load necessary plugins (e.g., for CSV export)
adtf_file.load_plugin("csv_exporter.adtffileplugin")

# Create demultiplexer
demux = adtf_file.create_demultiplexer("recording.adtfdat")

# Add a processor
# Args: Stream Name, Substream Name (optional), Processor ID, Dest Filename, Config Dict
demux.add_processor(
    stream_name="vehicle_speed",
    substream_name="",
    processor_id="csv",
    destination_url="output.csv",
    configuration={"separator": ";"}
)

# Run the export
demux.process(progress_callback)

```

## API Reference

### Module Functions

* `create_reader(filename, config={})`: Returns a `Reader` object.
* `create_seekablereader(filename, config={})`: Returns a `SeekableReader` object.
* `create_demultiplexer(filename, reader_id="", reader_config={})`: Returns a `Demultiplexer`.
* `load_plugin(filename)`: Loads an ADTF file plugin (shared object/dll).

### Classes

#### `Reader`

* **Properties:** `file_version`, `item_count`, `description`, `extensions`, `streams`.
* **Methods:** `get_next_item()`.

#### `FileItem`

* **Properties:**
* `time_stamp`: Integer timestamp.
* `stream_id`: ID of the stream this item belongs to.
* `type`: Enum (`Sample`, `StreamType`, `Trigger`).
* `sample`: Returns `WriteSample` (if type is Sample).
* `stream_type`: Returns `PropertyStreamType` (if type is StreamType).

#### `WriteSample`

* **Properties:**
* `buffer`: Memory buffer (compatible with `memoryview` or `bytearray`).
* `time_stamp`: Sample timestamp.
* `substream_id`: ID for substreams (if applicable).
* `flags`: Sample flags.

#### `SampleDecoder`

* **Constructor:** `SampleDecoder(stream_type)`
* **Methods:** `decode(sample)` -> Returns Python object (primitives or `types.SimpleNamespace`).

---

**Note:** This module relies on the ADTF File Library ABI. Ensure your Python environment matches the architecture and
version of the ADTF libraries provided.
