from pathlib import Path, PureWindowsPath

import evfuncs
import numpy as np
import pytest

import crowsetta.formats

from .asserts import assert_rounded_correct_num_decimals


def test_from_file(a_notmat_path):
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path)
    assert isinstance(notmat, crowsetta.formats.seq.NotMat)


def test_from_file_str(a_notmat_path):
    a_notmat_path_str = str(a_notmat_path)
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path_str)
    assert isinstance(notmat, crowsetta.formats.seq.NotMat)


def test_to_seq(a_notmat_path):
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path)
    seq = notmat.to_seq()
    assert isinstance(seq, crowsetta.Sequence)


@pytest.mark.parametrize(
    "decimals",
    [
        1,
        2,
        3,
        4,
        5,
    ],
)
def test_to_seq_round_times_true(test_data_root, a_notmat_path, decimals):
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path)
    seq = notmat.to_seq(round_times=True, decimals=decimals)
    assert_rounded_correct_num_decimals(seq.onsets_s, decimals)
    assert_rounded_correct_num_decimals(seq.offsets_s, decimals)


def test_to_seq_round_times_false(test_data_root, a_notmat_path):
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path)
    seq = notmat.to_seq(round_times=False)

    assert np.all(np.allclose(seq.onsets_s, notmat.onsets))
    assert np.all(np.allclose(seq.offsets_s, notmat.offsets))


def test_to_annot(a_notmat_path):
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path)
    annot = notmat.to_annot()
    assert isinstance(annot, crowsetta.Annotation)
    assert hasattr(annot, "seq")


@pytest.mark.parametrize(
    "decimals",
    [
        1,
        2,
        3,
        4,
        5,
    ],
)
def test_to_annot_round_times_true(test_data_root, a_notmat_path, decimals):
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path)
    annot = notmat.to_annot(round_times=True, decimals=decimals)
    assert_rounded_correct_num_decimals(annot.seq.onsets_s, decimals)
    assert_rounded_correct_num_decimals(annot.seq.offsets_s, decimals)


def test_to_annot_round_times_false(test_data_root, a_notmat_path):
    notmat = crowsetta.formats.seq.NotMat.from_file(annot_path=a_notmat_path)
    annot = notmat.to_annot(round_times=False)

    assert np.all(np.allclose(annot.seq.onsets_s, notmat.onsets))
    assert np.all(np.allclose(annot.seq.offsets_s, notmat.offsets))


def test_to_file(tmp_path, a_notmat_path):
    notmat_dict = evfuncs.load_notmat(a_notmat_path)

    notmat = crowsetta.formats.seq.NotMat.from_file(a_notmat_path)
    notmat.to_file(
        samp_freq=notmat_dict["Fs"],
        threshold=notmat_dict["threshold"],
        min_syl_dur=notmat_dict["min_dur"] / 1000,
        min_silent_dur=notmat_dict["min_int"] / 1000,
        other_vars=None,
        dst=tmp_path,
    )
    notmat_made_path = tmp_path / (notmat.audio_path.name + ".not.mat")
    notmat_made = evfuncs.load_notmat(notmat_made_path)
    # can't do assert(new_dict == old_dict)
    # because headers will be different (and we want them to be different)
    for key in ["Fs", "fname", "labels", "onsets", "offsets", "min_int", "min_dur", "threshold", "sm_win"]:
        if key == "fname":
            # have to deal with Windows path saved in .not.mat files
            # and then compare file names without path to them
            notmat_dict_path = PureWindowsPath(notmat_dict[key])
            notmat_made_path = Path(notmat_made[key])
            assert notmat_dict_path.name == notmat_made_path.name
        elif type(notmat_dict[key]) == np.ndarray:
            assert np.allclose(notmat_dict[key], notmat_made[key], atol=1e-3, rtol=1e-3)
        else:
            assert notmat_dict[key] == notmat_made[key]
