"""
Inscoper NBO Example Script: Describe an Acquired Image
=====================================================
This script demonstrates how to describe an acquired dataset in the NBO model:
image dimensions, pixel calibration, channels, and per-plane timing.

Prerequisites:
-------------
1. Installation: The `inscoper_nbo` wheel must be installed in the active Python
   environment.
"""

import os
import tempfile

import inscoper_nbo

# --8<-- [start:main_logic]
# Values that would come from the acquisition software
SIZE_X, SIZE_Y, SIZE_Z, SIZE_C, SIZE_T = 2304, 2304, 5, 2, 3
PIXEL_SIZE_XY = 0.108  # micrometers
Z_STEP = 0.5           # micrometers

# Step 1: Describe the pixel block
pixels = inscoper_nbo.Pixels()

pixels_id = inscoper_nbo.PixelsID_Type()
pixels_id.set("Pixels:1")
pixels.setID(pixels_id)

dimension_order = inscoper_nbo.PixelsDimensionOrder_Type()
dimension_order.set("XYZTC")
pixels.setDimensionOrder(dimension_order)

for setter, value in [
    (pixels.setSizeX, SIZE_X),
    (pixels.setSizeY, SIZE_Y),
    (pixels.setSizeZ, SIZE_Z),
    (pixels.setSizeC, SIZE_C),
    (pixels.setSizeT, SIZE_T),
]:
    size = inscoper_nbo.PositiveInt_Type()
    size.set(value)
    setter(size)

pixel_type = inscoper_nbo.PixelType_Type()
pixel_type.set("uint16")
pixels.setPixelType(pixel_type)

# Step 2: Add the physical calibration
for setter, value in [
    (pixels.setPhysicalSizeX, PIXEL_SIZE_XY),
    (pixels.setPhysicalSizeY, PIXEL_SIZE_XY),
    (pixels.setPhysicalSizeZ, Z_STEP),
]:
    physical_size = inscoper_nbo.PositiveFloat_Type()
    physical_size.set(value)
    setter(physical_size)

# Step 3: Declare that the document carries metadata only
# Pixels holds a choice between the binary alternatives (BinData, TiffData,
# NGFFData) and MetadataOnly, used when the pixel data lives elsewhere.
pixels_choice = inscoper_nbo.Pixels_Choice()
metadata_only_list = inscoper_nbo.MetadataOnlyVector()
metadata_only_list.append(inscoper_nbo.MetadataOnly())
pixels_choice.addMetadataOnly(metadata_only_list)
pixels.setpixels_choice(pixels_choice)

# Step 4: Describe the channels
for index, (channel_name, imaging_method, contrast) in enumerate(
    [
        ("DAPI", "Wide-field_Fluorescence", "Fluorescence"),
        ("Brightfield", "Transmitted_Bright-field", "Brightfield"),
    ]
):
    channel = inscoper_nbo.Channel()

    channel_id = inscoper_nbo.ChannelID_Type()
    channel_id.set(f"Channel:{index}")
    channel.setID(channel_id)

    name = inscoper_nbo.Denomination_Type()
    name.set(channel_name)
    channel.setName(name)

    illumination_type = inscoper_nbo.ImagingMethodList_Type()
    illumination_type.set(imaging_method)
    channel.setIlluminationType(illumination_type)

    contrast_method = inscoper_nbo.ChannelContrastMethod_Type()
    contrast_method.set(contrast)
    channel.setContrastMethod(contrast_method)

    # The light path records the optics the channel was acquired through
    light_path = inscoper_nbo.LightPath()
    light_path_id = inscoper_nbo.LSID_Type()
    light_path_id.set(f"LightPath:{index}")
    light_path.setID(light_path_id)
    channel.setLightPath(light_path)

    pixels.addToChannel_List(channel)

# Step 5: Record per-plane timing
timestamp = 0.0
for the_t in range(SIZE_T):
    for the_c in range(SIZE_C):
        for the_z in range(SIZE_Z):
            plane = inscoper_nbo.Plane()

            plane_id = inscoper_nbo.PlaneID_Type()
            plane_id.set(f"Plane:{the_t}:{the_c}:{the_z}")
            plane.setID(plane_id)

            for setter, value in [
                (plane.setTheZ, the_z),
                (plane.setTheC, the_c),
                (plane.setTheT, the_t),
            ]:
                index_value = inscoper_nbo.NonNegativeInt_Type()
                index_value.set(value)
                setter(index_value)

            plane.setTimestamp(timestamp)
            timestamp += 0.05

            pixels.addToPlane_List(plane)

# Step 6: Attach the pixel block to an image and export
image = inscoper_nbo.Image()

image_id = inscoper_nbo.ImageID_Type()
image_id.set("Image:1")
image.setID(image_id)

image_name = inscoper_nbo.Denomination_Type()
image_name.set("Well A1 - Field 1")
image.setName(image_name)

image.setPixels(pixels)

stage_label = inscoper_nbo.StageLabel()
stage_label.setName("Well A1 - Field 1")
stage_label.setX(12500.0)
stage_label.setY(8300.0)
stage_label.setZ(4210.5)
image.addToStageLabel_List(stage_label)

output_path = os.path.join(tempfile.gettempdir(), "image_metadata.xml")
image.toXmlFile(output_path)
print(f"Image description written to {output_path}")
print(f"Planes: {len(image.getPixels().getPlane_List())}")
print(f"Channels: {[c.getName().get() for c in image.getPixels().getChannel_List()]}")
# --8<-- [end:main_logic]
