# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

import datetime as dt
import os
import shutil

import numpy as np
import pytest
from numpy.testing import assert_allclose, assert_array_equal

from mne import pick_types
from mne._fiff.constants import FIFF
from mne.datasets.testing import data_path, requires_testing_data
from mne.io import read_raw_nirx
from mne.io.tests.test_raw import _test_raw_reader
from mne.preprocessing import annotate_nan
from mne.preprocessing.nirs import (
    _reorder_nirx,
    short_channels,
    source_detector_distances,
)
from mne.transforms import _get_trans, apply_trans

testing_path = data_path(download=False)
fname_nirx_15_0 = testing_path / "NIRx" / "nirscout" / "nirx_15_0_recording"
fname_nirx_15_2 = testing_path / "NIRx" / "nirscout" / "nirx_15_2_recording"
fname_nirx_15_2_short = (
    testing_path / "NIRx" / "nirscout" / "nirx_15_2_recording_w_short"
)
fname_nirx_15_3_short = testing_path / "NIRx" / "nirscout" / "nirx_15_3_recording"


# This file has no saturated sections
nirsport1_wo_sat = (
    testing_path / "NIRx" / "nirsport_v1" / "nirx_15_3_recording_wo_saturation"
)
# This file has saturation / but not on the optode pairing in montage
nirsport1_w_sat = (
    testing_path
    / "NIRx"
    / "nirsport_v1"
    / "nirx_15_3_recording_w_saturation_not_on_montage_channels"
)
# This file has saturation in channels of interest
nirsport1_w_fullsat = (
    testing_path
    / "NIRx"
    / "nirsport_v1"
    / "nirx_15_3_recording_w_saturation_on_montage_channels"
)

# NIRSport2 device using Aurora software
nirsport2 = testing_path / "NIRx" / "nirsport_v2" / "aurora_recording _w_short_and_acc"
nirsport2_2021_9 = testing_path / "NIRx" / "nirsport_v2" / "aurora_2021_9"
nirsport2_2021_9_6 = testing_path / "NIRx" / "nirsport_v2" / "aurora_2021_9_6"


def test_nirsport_v2_matches_snirf(nirx_snirf):
    """Test NIRSport2 raw files return same data as snirf."""
    raw, raw_snirf = nirx_snirf
    _reorder_nirx(raw_snirf)
    assert raw.ch_names == raw_snirf.ch_names

    assert_allclose(raw._data, raw_snirf._data)

    # Check the timing of annotations match (naming is different)
    assert_allclose(raw.annotations.onset, raw_snirf.annotations.onset)

    assert_array_equal(raw.ch_names, raw_snirf.ch_names)

    # This test fails as snirf encodes name incorrectly.
    # assert raw.info["subject_info"]["first_name"] ==
    # raw_snirf.info["subject_info"]["first_name"]


@requires_testing_data
@pytest.mark.filterwarnings("ignore:.*Extraction of measurement.*:")
def test_nirsport_v2():
    """Test NIRSport2 file."""
    raw = read_raw_nirx(nirsport2, preload=True)
    assert raw._data.shape == (40, 128)

    # Test distance between optodes matches values from
    # nirsite https://github.com/mne-tools/mne-testing-data/pull/86
    # figure 3
    allowed_distance_error = 0.005
    assert_allclose(
        source_detector_distances(raw.copy().pick("S1_D1 760").info),
        [0.0304],
        atol=allowed_distance_error,
    )
    assert_allclose(
        source_detector_distances(raw.copy().pick("S2_D2 760").info),
        [0.0400],
        atol=allowed_distance_error,
    )

    # Test location of detectors
    # The locations of detectors can be seen in the first
    # figure on this page...
    # https://github.com/mne-tools/mne-testing-data/pull/86
    allowed_dist_error = 0.0002
    locs = [ch["loc"][6:9] for ch in raw.info["chs"]]
    head_mri_t, _ = _get_trans("fsaverage", "head", "mri")
    mni_locs = apply_trans(head_mri_t, locs)

    assert raw.info["ch_names"][0][3:5] == "D1"
    assert_allclose(mni_locs[0], [-0.0841, -0.0464, -0.0129], atol=allowed_dist_error)

    assert raw.info["ch_names"][2][3:5] == "D6"
    assert_allclose(mni_locs[2], [-0.0841, -0.0138, 0.0248], atol=allowed_dist_error)

    assert raw.info["ch_names"][34][3:5] == "D5"
    assert_allclose(mni_locs[34], [0.0845, -0.0451, -0.0123], atol=allowed_dist_error)

    # Test location of sensors
    # The locations of sensors can be seen in the second
    # figure on this page...
    # https://github.com/mne-tools/mne-testing-data/pull/86
    locs = [ch["loc"][3:6] for ch in raw.info["chs"]]
    head_mri_t, _ = _get_trans("fsaverage", "head", "mri")
    mni_locs = apply_trans(head_mri_t, locs)

    assert raw.info["ch_names"][0][:2] == "S1"
    assert_allclose(mni_locs[0], [-0.0848, -0.0162, -0.0163], atol=allowed_dist_error)

    assert raw.info["ch_names"][9][:2] == "S2"
    assert_allclose(mni_locs[9], [-0.0, -0.1195, 0.0142], atol=allowed_dist_error)

    assert raw.info["ch_names"][39][:2] == "S8"
    assert_allclose(mni_locs[34], [0.0828, -0.046, 0.0285], atol=allowed_dist_error)

    assert len(raw.annotations) == 3
    assert raw.annotations.description[0] == "1.0"
    assert raw.annotations.description[2] == "6.0"
    # Lose tolerance as I am eyeballing the time differences on screen
    assert_allclose(np.diff(raw.annotations.onset), [2.3, 3.1], atol=0.1)

    mon = raw.get_montage()
    assert len(mon.dig) == 27


@requires_testing_data
@pytest.mark.filterwarnings("ignore:.*Extraction of measurement.*:")
def test_nirsport_v1_wo_sat():
    """Test NIRSport1 file with no saturation."""
    raw = read_raw_nirx(nirsport1_wo_sat, preload=True)

    # Test data import
    assert raw._data.shape == (26, 164)
    assert raw.info["sfreq"] == 10.416667

    # By default real data is returned
    assert np.sum(np.isnan(raw.get_data())) == 0

    raw = read_raw_nirx(nirsport1_wo_sat, preload=True, saturated="nan")
    data = raw.get_data()
    assert data.shape == (26, 164)
    assert np.sum(np.isnan(data)) == 0

    raw = read_raw_nirx(nirsport1_wo_sat, saturated="annotate")
    data = raw.get_data()
    assert data.shape == (26, 164)
    assert np.sum(np.isnan(data)) == 0


@pytest.mark.filterwarnings("ignore:.*Extraction of measurement.*:")
@requires_testing_data
def test_nirsport_v1_w_sat():
    """Test NIRSport1 file with NaNs but not in channel of interest."""
    raw = read_raw_nirx(nirsport1_w_sat)

    # Test data import
    data = raw.get_data()
    assert data.shape == (26, 176)
    assert raw.info["sfreq"] == 10.416667
    assert np.sum(np.isnan(data)) == 0

    raw = read_raw_nirx(nirsport1_w_sat, saturated="nan")
    data = raw.get_data()
    assert data.shape == (26, 176)
    assert np.sum(np.isnan(data)) == 0

    raw = read_raw_nirx(nirsport1_w_sat, saturated="annotate")
    data = raw.get_data()
    assert data.shape == (26, 176)
    assert np.sum(np.isnan(data)) == 0


@pytest.mark.filterwarnings("ignore:.*Extraction of measurement.*:")
@requires_testing_data
@pytest.mark.parametrize("preload", (True, False))
@pytest.mark.parametrize("meas_date", (None, "orig"))
def test_nirsport_v1_w_bad_sat(preload, meas_date):
    """Test NIRSport1 file with NaNs."""
    fname = nirsport1_w_fullsat
    raw = read_raw_nirx(fname, preload=preload)
    data = raw.get_data()
    assert not np.isnan(data).any()
    assert len(raw.annotations) == 5
    # annotated version and ignore should have same data but different annot
    raw_ignore = read_raw_nirx(fname, saturated="ignore", preload=preload)
    assert_allclose(raw_ignore.get_data(), data)
    assert len(raw_ignore.annotations) == 2
    assert not any("NAN" in d for d in raw_ignore.annotations.description)
    # nan version should not have same data, but we can give it the same annot
    raw_nan = read_raw_nirx(fname, saturated="nan", preload=preload)
    data_nan = raw_nan.get_data()
    assert np.isnan(data_nan).any()
    assert not np.allclose(raw_nan.get_data(), data)
    raw_nan_annot = raw_ignore.copy()
    if meas_date is None:
        raw.set_meas_date(None)
        raw_nan.set_meas_date(None)
        raw_nan_annot.set_meas_date(None)
    nan_annots = annotate_nan(raw_nan)
    assert nan_annots.orig_time == raw_nan.info["meas_date"]
    raw_nan_annot.set_annotations(nan_annots)
    use_mask = np.where(raw.annotations.description == "BAD_SATURATED")
    for key in ("onset", "duration"):
        a = getattr(raw_nan_annot.annotations, key)[::2]  # one ch in each
        b = getattr(raw.annotations, key)[use_mask]  # two chs in each
        assert_allclose(a, b)


@requires_testing_data
def test_nirx_hdr_load():
    """Test reading NIRX files using path to header file."""
    fname = fname_nirx_15_2_short / "NIRS-2019-08-23_001.hdr"
    raw = read_raw_nirx(fname, preload=True)

    # Test data import
    assert raw._data.shape == (26, 145)
    assert raw.info["sfreq"] == 12.5


@requires_testing_data
def test_nirx_missing_warn():
    """Test reading NIRX files when missing data."""
    with pytest.raises(FileNotFoundError, match="does not exist"):
        read_raw_nirx(fname_nirx_15_2_short / "1", preload=True)


@requires_testing_data
def test_nirx_missing_evt(tmp_path):
    """Test reading NIRX files when missing data."""
    shutil.copytree(fname_nirx_15_2_short, str(tmp_path) + "/data/")
    os.rename(
        tmp_path / "data" / "NIRS-2019-08-23_001.evt",
        tmp_path / "data" / "NIRS-2019-08-23_001.xxx",
    )
    fname = tmp_path / "data" / "NIRS-2019-08-23_001.hdr"
    raw = read_raw_nirx(fname, preload=True)
    assert raw.annotations.onset.shape == (0,)


@requires_testing_data
def test_nirx_dat_warn(tmp_path):
    """Test reading NIRX files when missing data."""
    shutil.copytree(fname_nirx_15_2_short, str(tmp_path) + "/data/")
    os.rename(
        tmp_path / "data" / "NIRS-2019-08-23_001.dat",
        tmp_path / "data" / "NIRS-2019-08-23_001.tmp",
    )
    fname = tmp_path / "data" / "NIRS-2019-08-23_001.hdr"
    with pytest.warns(RuntimeWarning, match="A single dat"):
        read_raw_nirx(fname, preload=True)


@requires_testing_data
def test_nirx_15_2_short():
    """Test reading NIRX files."""
    raw = read_raw_nirx(fname_nirx_15_2_short, preload=True)

    # Test data import
    assert raw._data.shape == (26, 145)
    assert raw.info["sfreq"] == 12.5
    assert raw.info["meas_date"] == dt.datetime(
        2019, 8, 23, 7, 37, 4, 540000, tzinfo=dt.timezone.utc
    )

    # Test channel naming
    assert raw.info["ch_names"][:4] == [
        "S1_D1 760",
        "S1_D1 850",
        "S1_D9 760",
        "S1_D9 850",
    ]
    assert raw.info["ch_names"][24:26] == ["S5_D13 760", "S5_D13 850"]

    # Test frequency encoding
    assert raw.info["chs"][0]["loc"][9] == 760
    assert raw.info["chs"][1]["loc"][9] == 850

    # Test info import
    assert raw.info["subject_info"] == dict(
        sex=1,
        first_name="MNE",
        middle_name="Test",
        last_name="Recording",
        birthday=dt.date(2014, 8, 23),
        his_id="MNE_Test_Recording",
    )

    # Test distance between optodes matches values from
    # nirsite https://github.com/mne-tools/mne-testing-data/pull/51
    # step 4 figure 2
    allowed_distance_error = 0.0002
    assert_allclose(
        source_detector_distances(raw.copy().pick("S1_D1 760").info),
        [0.0304],
        atol=allowed_distance_error,
    )
    assert_allclose(
        source_detector_distances(raw.copy().pick("S2_D10 760").info),
        [0.0086],
        atol=allowed_distance_error,
    )

    # Test which channels are short
    # These are the ones marked as red at
    # https://github.com/mne-tools/mne-testing-data/pull/51 step 4 figure 2
    is_short = short_channels(raw.info)
    assert_array_equal(is_short[:9:2], [False, True, False, True, False])
    is_short = short_channels(raw.info, threshold=0.003)
    assert_array_equal(is_short[:3:2], [False, False])
    is_short = short_channels(raw.info, threshold=50)
    assert_array_equal(is_short[:3:2], [True, True])

    # Test trigger events
    assert_array_equal(raw.annotations.description, ["3.0", "2.0", "1.0"])

    # Test location of detectors
    # The locations of detectors can be seen in the first
    # figure on this page...
    # https://github.com/mne-tools/mne-testing-data/pull/51
    # And have been manually copied below
    # These values were reported in mm, but according to this page...
    # https://mne.tools/stable/auto_tutorials/intro/plot_40_sensor_locations.html
    # 3d locations should be specified in meters, so that's what's tested below
    # Detector locations are stored in the third three loc values
    allowed_dist_error = 0.0002
    locs = [ch["loc"][6:9] for ch in raw.info["chs"]]
    head_mri_t, _ = _get_trans("fsaverage", "head", "mri")
    mni_locs = apply_trans(head_mri_t, locs)

    assert raw.info["ch_names"][0][3:5] == "D1"
    assert_allclose(mni_locs[0], [-0.0841, -0.0464, -0.0129], atol=allowed_dist_error)

    assert raw.info["ch_names"][4][3:5] == "D3"
    assert_allclose(mni_locs[4], [0.0846, -0.0142, -0.0156], atol=allowed_dist_error)

    assert raw.info["ch_names"][8][3:5] == "D2"
    assert_allclose(mni_locs[8], [0.0207, -0.1062, 0.0484], atol=allowed_dist_error)

    assert raw.info["ch_names"][12][3:5] == "D4"
    assert_allclose(mni_locs[12], [-0.0196, 0.0821, 0.0275], atol=allowed_dist_error)

    assert raw.info["ch_names"][16][3:5] == "D5"
    assert_allclose(mni_locs[16], [-0.0360, 0.0276, 0.0778], atol=allowed_dist_error)

    assert raw.info["ch_names"][19][3:5] == "D6"
    assert_allclose(mni_locs[19], [0.0352, 0.0283, 0.0780], atol=allowed_dist_error)

    assert raw.info["ch_names"][21][3:5] == "D7"
    assert_allclose(mni_locs[21], [0.0388, -0.0477, 0.0932], atol=allowed_dist_error)


@requires_testing_data
def test_nirx_15_3_short():
    """Test reading NIRX files."""
    raw = read_raw_nirx(fname_nirx_15_3_short, preload=True)

    # Test data import
    assert raw._data.shape == (26, 220)
    assert raw.info["sfreq"] == 12.5

    # Test channel naming
    assert raw.info["ch_names"][:4] == [
        "S1_D2 760",
        "S1_D2 850",
        "S1_D9 760",
        "S1_D9 850",
    ]
    assert raw.info["ch_names"][24:26] == ["S5_D13 760", "S5_D13 850"]

    # Test frequency encoding
    assert raw.info["chs"][0]["loc"][9] == 760
    assert raw.info["chs"][1]["loc"][9] == 850

    # Test info import
    assert raw.info["subject_info"] == dict(
        birthday=dt.date(2020, 8, 18),
        sex=0,
        first_name="testMontage\\0ATestMontage",
        his_id="testMontage\\0ATestMontage",
    )

    # Test distance between optodes matches values from
    # https://github.com/mne-tools/mne-testing-data/pull/72
    allowed_distance_error = 0.001
    assert_allclose(
        source_detector_distances(raw.copy().pick("S1_D2 760").info),
        [0.0304],
        atol=allowed_distance_error,
    )
    assert_allclose(
        source_detector_distances(raw.copy().pick("S5_D13 760").info),
        [0.0076],
        atol=allowed_distance_error,
    )

    # Test which channels are short
    # These are the ones marked as red at
    # https://github.com/mne-tools/mne-testing-data/pull/72
    is_short = short_channels(raw.info)
    assert_array_equal(is_short[:9:2], [False, True, False, True, False])
    is_short = short_channels(raw.info, threshold=0.003)
    assert_array_equal(is_short[:3:2], [False, False])
    is_short = short_channels(raw.info, threshold=50)
    assert_array_equal(is_short[:3:2], [True, True])

    # Test trigger events
    assert_array_equal(raw.annotations.description, ["4.0", "2.0", "1.0"])

    # Test location of detectors
    # The locations of detectors can be seen in the first
    # figure on this page...
    # https://github.com/mne-tools/mne-testing-data/pull/72
    # And have been manually copied below
    allowed_dist_error = 0.0002
    locs = [ch["loc"][6:9] for ch in raw.info["chs"]]
    head_mri_t, _ = _get_trans("fsaverage", "head", "mri")
    mni_locs = apply_trans(head_mri_t, locs)

    assert raw.info["ch_names"][0][3:5] == "D2"
    assert_allclose(mni_locs[0], [-0.0841, -0.0464, -0.0129], atol=allowed_dist_error)

    assert raw.info["ch_names"][4][3:5] == "D1"
    assert_allclose(mni_locs[4], [0.0846, -0.0142, -0.0156], atol=allowed_dist_error)

    assert raw.info["ch_names"][8][3:5] == "D3"
    assert_allclose(mni_locs[8], [0.0207, -0.1062, 0.0484], atol=allowed_dist_error)

    assert raw.info["ch_names"][12][3:5] == "D4"
    assert_allclose(mni_locs[12], [-0.0196, 0.0821, 0.0275], atol=allowed_dist_error)

    assert raw.info["ch_names"][16][3:5] == "D5"
    assert_allclose(mni_locs[16], [-0.0360, 0.0276, 0.0778], atol=allowed_dist_error)

    assert raw.info["ch_names"][19][3:5] == "D6"
    assert_allclose(mni_locs[19], [0.0388, -0.0477, 0.0932], atol=allowed_dist_error)

    assert raw.info["ch_names"][21][3:5] == "D7"
    assert_allclose(mni_locs[21], [-0.0394, -0.0483, 0.0928], atol=allowed_dist_error)


@requires_testing_data
def test_locale_encoding(tmp_path):
    """Test NIRx encoding."""
    fname = tmp_path / "latin"
    shutil.copytree(fname_nirx_15_2, fname)
    hdr_fname = fname / "NIRS-2019-10-02_003.hdr"
    hdr = list()
    with open(hdr_fname, "rb") as fid:
        hdr.extend(line for line in fid)
    # French
    hdr[2] = b'Date="jeu. 13 f\xe9vr. 2020"\r\n'
    with open(hdr_fname, "wb") as fid:
        for line in hdr:
            fid.write(line)
    read_raw_nirx(fname, verbose="debug")
    # German
    hdr[2] = b'Date="mi 13 dez 2020"\r\n'
    with open(hdr_fname, "wb") as fid:
        for line in hdr:
            fid.write(line)
    read_raw_nirx(fname, verbose="debug")
    # Italian
    hdr[2] = b'Date="ven 24 gen 2020"\r\n'
    hdr[3] = b'Time="10:57:41.454"\r\n'
    with open(hdr_fname, "wb") as fid:
        for line in hdr:
            fid.write(line)
    raw = read_raw_nirx(fname, verbose="debug")
    want_dt = dt.datetime(2020, 1, 24, 10, 57, 41, 454000, tzinfo=dt.timezone.utc)
    assert raw.info["meas_date"] == want_dt


@requires_testing_data
def test_nirx_15_2():
    """Test reading NIRX files."""
    raw = read_raw_nirx(fname_nirx_15_2, preload=True)

    # Test data import
    assert raw._data.shape == (64, 67)
    assert raw.info["sfreq"] == 3.90625
    assert raw.info["meas_date"] == dt.datetime(
        2019, 10, 2, 9, 8, 47, 511000, tzinfo=dt.timezone.utc
    )

    # Test channel naming
    assert raw.info["ch_names"][:4] == [
        "S1_D1 760",
        "S1_D1 850",
        "S1_D10 760",
        "S1_D10 850",
    ]

    # Test info import
    assert raw.info["subject_info"] == dict(
        sex=1,
        first_name="TestRecording",
        birthday=dt.date(1989, 10, 2),
        his_id="TestRecording",
    )

    # Test trigger events
    assert_array_equal(raw.annotations.description, ["4.0", "6.0", "2.0"])
    print(raw.annotations.onset)

    # Test location of detectors
    allowed_dist_error = 0.0002
    locs = [ch["loc"][6:9] for ch in raw.info["chs"]]
    head_mri_t, _ = _get_trans("fsaverage", "head", "mri")
    mni_locs = apply_trans(head_mri_t, locs)

    assert raw.info["ch_names"][0][3:5] == "D1"
    assert_allclose(mni_locs[0], [-0.0292, 0.0852, -0.0142], atol=allowed_dist_error)

    assert raw.info["ch_names"][15][3:5] == "D4"
    assert_allclose(mni_locs[15], [-0.0739, -0.0756, -0.0075], atol=allowed_dist_error)

    # Old name aliases for backward compat
    assert "fnirs_cw_amplitude" in raw
    with pytest.raises(ValueError, match="Invalid value"):
        "fnirs_raw" in raw
    assert "fnirs_od" not in raw
    picks = pick_types(raw.info, fnirs="fnirs_cw_amplitude")
    assert len(picks) > 0


@requires_testing_data
def test_nirx_aurora_2021_9_6():
    """Test reading NIRX files."""
    raw = read_raw_nirx(nirsport2_2021_9_6, preload=True)
    assert len(raw.annotations) == 3
    assert raw.annotations.description[0] == "1.0"
    assert raw.annotations.description[2] == "3.0"


@requires_testing_data
def test_nirx_15_0():
    """Test reading NIRX files."""
    raw = read_raw_nirx(fname_nirx_15_0, preload=True)

    # Test data import
    assert raw._data.shape == (20, 92)
    assert raw.info["sfreq"] == 6.25
    assert raw.info["meas_date"] == dt.datetime(
        2019, 10, 27, 13, 53, 34, 209000, tzinfo=dt.timezone.utc
    )

    # Test channel naming
    assert raw.info["ch_names"][:12] == [
        "S1_D1 760",
        "S1_D1 850",
        "S2_D2 760",
        "S2_D2 850",
        "S3_D3 760",
        "S3_D3 850",
        "S4_D4 760",
        "S4_D4 850",
        "S5_D5 760",
        "S5_D5 850",
        "S6_D6 760",
        "S6_D6 850",
    ]

    # Test info import
    assert raw.info["subject_info"] == {
        "birthday": dt.date(2004, 10, 27),
        "first_name": "NIRX",
        "last_name": "Test",
        "sex": FIFF.FIFFV_SUBJ_SEX_UNKNOWN,
        "his_id": "NIRX_Test",
    }

    # Test trigger events
    assert_array_equal(raw.annotations.description, ["1.0", "2.0", "2.0"])

    # Test location of detectors
    allowed_dist_error = 0.0002
    locs = [ch["loc"][6:9] for ch in raw.info["chs"]]
    head_mri_t, _ = _get_trans("fsaverage", "head", "mri")
    mni_locs = apply_trans(head_mri_t, locs)

    assert raw.info["ch_names"][0][3:5] == "D1"
    assert_allclose(mni_locs[0], [0.0287, -0.1143, -0.0332], atol=allowed_dist_error)

    assert raw.info["ch_names"][15][3:5] == "D8"
    assert_allclose(mni_locs[15], [-0.0693, -0.0480, 0.0657], atol=allowed_dist_error)

    # Test distance between optodes matches values from
    allowed_distance_error = 0.0002

    assert_allclose(
        source_detector_distances(raw.copy().pick("S1_D1 760").info),
        [0.0300],
        atol=allowed_distance_error,
    )
    assert_allclose(
        source_detector_distances(raw.copy().pick("S7_D7 760").info),
        [0.0392],
        atol=allowed_distance_error,
    )


@requires_testing_data
@pytest.mark.parametrize(
    "fname, boundary_decimal",
    (
        [fname_nirx_15_2_short, 1],
        [fname_nirx_15_2, 0],
        [fname_nirx_15_2, 0],
        [nirsport2_2021_9, 0],
    ),
)
def test_nirx_standard(fname, boundary_decimal):
    """Test standard operations."""
    _test_raw_reader(
        read_raw_nirx, fname=fname, boundary_decimal=boundary_decimal
    )  # low fs


# Below are the native (on-disk) orders, which should be preserved
@requires_testing_data
@pytest.mark.parametrize(
    "fname, want_order",
    [
        (
            fname_nirx_15_0,
            [
                "S1_D1",
                "S2_D2",
                "S3_D3",
                "S4_D4",
                "S5_D5",
                "S6_D6",
                "S7_D7",
                "S8_D8",
                "S9_D9",
                "S10_D10",
            ],
        ),  # noqa: E501
        (
            fname_nirx_15_2,
            [
                "S1_D1",
                "S1_D10",
                "S2_D1",
                "S2_D2",
                "S3_D2",
                "S3_D3",
                "S4_D3",
                "S4_D4",
                "S5_D4",
                "S5_D5",
                "S6_D5",
                "S6_D6",
                "S7_D6",
                "S7_D7",
                "S8_D7",
                "S8_D8",
                "S9_D8",
                "S9_D9",
                "S10_D9",
                "S10_D10",
                "S11_D11",
                "S11_D12",
                "S12_D12",
                "S12_D13",
                "S13_D13",
                "S13_D14",
                "S14_D14",
                "S14_D15",
                "S15_D15",
                "S15_D16",
                "S16_D11",
                "S16_D16",
            ],
        ),  # noqa: E501
        (
            fname_nirx_15_2_short,
            [
                "S1_D1",
                "S1_D9",
                "S2_D3",
                "S2_D10",
                "S3_D2",
                "S3_D11",
                "S4_D4",
                "S4_D12",
                "S5_D5",
                "S5_D6",
                "S5_D7",
                "S5_D8",
                "S5_D13",
            ],
        ),  # noqa: E501
        (
            fname_nirx_15_3_short,
            [
                "S1_D2",
                "S1_D9",
                "S2_D1",
                "S2_D10",
                "S3_D3",
                "S3_D11",
                "S4_D4",
                "S4_D12",
                "S5_D5",
                "S5_D6",
                "S5_D7",
                "S5_D8",
                "S5_D13",
            ],
        ),  # noqa: E501
        (
            nirsport1_wo_sat,
            [
                "S1_D4",
                "S1_D5",
                "S1_D6",
                "S2_D5",
                "S2_D6",
                "S3_D5",
                "S4_D1",
                "S4_D3",
                "S4_D4",
                "S5_D1",
                "S5_D2",
                "S6_D1",
                "S6_D3",
            ],
        ),  # noqa: E501
        (
            nirsport2,
            [
                "S1_D1",
                "S1_D6",
                "S1_D9",
                "S2_D2",
                "S2_D10",
                "S3_D5",
                "S3_D7",
                "S3_D11",
                "S4_D8",
                "S4_D12",
                "S5_D3",
                "S5_D13",
                "S6_D4",
                "S6_D14",
                "S7_D1",
                "S7_D6",
                "S7_D15",
                "S8_D5",
                "S8_D7",
                "S8_D16",
            ],
        ),  # noqa: E501
        (
            nirsport2_2021_9,
            [
                "S1_D1",
                "S1_D3",
                "S2_D1",
                "S2_D2",
                "S2_D4",
                "S3_D2",
                "S3_D5",
                "S4_D1",
                "S4_D3",
                "S4_D4",
                "S4_D6",
                "S5_D2",
                "S5_D4",
                "S5_D5",
                "S5_D7",
                "S6_D3",
                "S6_D6",
                "S7_D4",
                "S7_D6",
                "S7_D7",
                "S8_D5",
                "S8_D7",
            ],
        ),  # noqa: E501
    ],
)
def test_channel_order(fname, want_order):
    """Test that logical channel order is preserved."""
    raw = read_raw_nirx(fname)
    ch_names = raw.ch_names
    prefixes = [ch_name.split()[0] for ch_name in ch_names]
    assert prefixes[::2] == prefixes[1::2]
    prefixes = prefixes[::2]
    assert prefixes == want_order
