/*
 * Inscoper NBO Example: Detector Wavelength Ranges
 * ================================================
 * This example demonstrates how to describe the spectral response of a detector
 * with repeated WavelengthRange elements, and how physical quantities carry their
 * unit.
 *
 * Prerequisites:
 * -------------
 * 1. Installation: inscoper-nbo-<version>.jar must be on the classpath.
 */

import com.inscoper.nbo.CameraIllumination_Type;
import com.inscoper.nbo.CMOS;
import com.inscoper.nbo.Detector_Type_InlineWavelengthRange;
import com.inscoper.nbo.DetectorID_Type;
import com.inscoper.nbo.DetectorNoiseModel_Type;
import com.inscoper.nbo.DigitizerType_Type;
import com.inscoper.nbo.PositiveFloat_Type;
import com.inscoper.nbo.UnitsLength_Type;

import java.nio.file.Path;
import java.nio.file.Paths;

public class DetectorWavelengthRanges {

    // --8<-- [start:main_logic]
    record Band(float cutOn, float cutOff, String profile) {
    }

    public static void main(String[] args) {
        // Step 1: Describe the detector
        CMOS camera = new CMOS();

        DetectorID_Type detectorId = new DetectorID_Type();
        detectorId.set("Detector:1");
        camera.setID(detectorId);

        camera.setManufacturer("Hamamatsu");
        camera.setModel("ORCA-Fusion BT");
        camera.setCatalogNumber("C15440-20UP");

        DigitizerType_Type maxBitDepth = new DigitizerType_Type();
        maxBitDepth.set("16bit");
        camera.setMaxBitDepth(maxBitDepth);

        DetectorNoiseModel_Type noiseModel = new DetectorNoiseModel_Type();
        noiseModel.set("Gaussian");
        camera.setDetectorNoiseModel(noiseModel);

        camera.setReadOutNoise(0.7f);
        camera.setElectronicConversionFactor(0.24f);
        camera.setDarkCurrentRate(0.06f);
        camera.setQuantumEfficiency(0.8f);

        // Camera adds its own required attributes on top of those of Detector
        camera.setPixelWidth(6.5f);
        camera.setPixelHeight(6.5f);
        camera.setPixelWellCapacity(15000L);
        camera.setMaximumFrameRate(89.1f);
        camera.setMaximumReadoutRate(100.0f);

        CameraIllumination_Type illumination = new CameraIllumination_Type();
        illumination.set("Back");
        camera.setIllumination(illumination);

        // Step 2: Add one WavelengthRange per band the detector is characterized for
        // WavelengthRange is declared inside Detector_Type, so the generated class is
        // Detector_Type_InlineWavelengthRange. The same class is reused by every
        // concrete detector, CMOS included.
        Band[] bands = {
            new Band(400.0f, 550.0f, "qe_400_550.csv"),
            new Band(550.0f, 700.0f, "qe_550_700.csv"),
        };

        for (Band band : bands) {
            Detector_Type_InlineWavelengthRange wavelengthRange =
                    new Detector_Type_InlineWavelengthRange();

            PositiveFloat_Type cutOn = new PositiveFloat_Type();
            cutOn.set(band.cutOn());
            wavelengthRange.setCutOn(cutOn);

            PositiveFloat_Type cutOff = new PositiveFloat_Type();
            cutOff.set(band.cutOff());
            wavelengthRange.setCutOff(cutOff);

            // Wavelengths are lengths: the unit is an attribute of its own
            UnitsLength_Type unit = new UnitsLength_Type();
            unit.set("nm");
            wavelengthRange.setCutOnUnit(unit);
            wavelengthRange.setCutOffUnit(unit);

            // PeakWavelength and WavelengthProfile are required on a wavelength range
            wavelengthRange.setPeakWavelength((band.cutOn() + band.cutOff()) / 2.0f);
            wavelengthRange.setWavelengthProfile(band.profile());

            camera.addToWavelengthRange_List(wavelengthRange);
        }

        // Step 3: Read the ranges back
        System.out.println("Ranges: " + camera.getWavelengthRange_List().size());
        for (Detector_Type_InlineWavelengthRange wavelengthRange :
                camera.getWavelengthRange_List()) {
            System.out.println("  " + wavelengthRange.getCutOn().get() + " - "
                    + wavelengthRange.getCutOff().get() + " "
                    + wavelengthRange.getCutOnUnit().get());
        }

        // Step 4: Export
        Path outputPath = Paths.get(System.getProperty("java.io.tmpdir"), "detector.xml");
        camera.toXmlFile(outputPath.toString());
        System.out.println("Detector description written to " + outputPath);
    }
    // --8<-- [end:main_logic]
}
