import warnings
from pathlib import Path
from unittest.mock import MagicMock, patch

import numpy as np
from PIL import Image as PILImage
from pytest import raises

import menpo.io as mio
from menpo.shape import PointCloud


def test_import_incorrect_built_in():
    with raises(ValueError):
        mio.import_builtin_asset("adskljasdlkajd.obj")


def test_breaking_bad_import():
    img = mio.import_builtin_asset("breakingbad.jpg")
    assert img.shape == (1080, 1920)
    assert img.n_channels == 3
    assert img.landmarks["PTS"].n_points == 68


def test_breaking_bad_import_kwargs():
    img = mio.import_builtin_asset("breakingbad.jpg", normalize=False)
    assert img.pixels.dtype == np.uint8


def test_takeo_import():
    img = mio.import_builtin_asset("takeo.ppm")
    assert img.shape == (225, 150)
    assert img.n_channels == 3
    assert img.landmarks["PTS"].n_points == 68


def test_einstein_import():
    img = mio.import_builtin_asset("einstein.jpg")
    assert img.shape == (1024, 817)
    assert img.n_channels == 1
    assert img.landmarks["PTS"].n_points == 68


def test_lenna_import():
    img = mio.import_builtin_asset("lenna.png")
    assert img.shape == (512, 512)
    assert img.n_channels == 3
    assert img.landmarks.n_groups == 2
    assert img.landmarks["LJSON"].n_points == 68
    assert img.landmarks["pupils"].n_points == 2


def test_import_builtin_ljson():
    lmarks = mio.import_builtin_asset("lenna.ljson")["LJSON"]
    assert lmarks.n_points == 68


def test_import_builtin_pts():
    lmarks = mio.import_builtin_asset("einstein.pts")["PTS"]
    assert lmarks.n_points == 68


def test_resolve_from_paths_single_group():
    def resolver(path):
        test_dict = {"test": path.with_name("takeo.pts")}
        return mio.input.resolve_from_paths(test_dict)

    image = mio.import_image(
        mio.data_path_to("einstein.jpg"), landmark_resolver=resolver
    )
    assert image.landmarks.n_groups == 1
    assert image.landmarks["test"].path == mio.data_path_to("takeo.pts")


def test_resolve_from_paths_multi_group():
    def resolver(path):
        test_dict = {"test": path.with_name("lenna.ljson")}
        return mio.input.resolve_from_paths(test_dict)

    image = mio.import_image(
        mio.data_path_to("einstein.jpg"), landmark_resolver=resolver
    )
    assert image.landmarks.n_groups == 2
    assert set(image.landmarks.keys()) == {"test_LJSON", "test_pupils"}


def test_path():
    # choose a random asset (all should have it!)
    img = mio.import_builtin_asset("einstein.jpg")
    path = mio.data_path_to("einstein.jpg")
    assert img.path == path
    assert img.path.stem == "einstein"
    assert img.path.suffix == ".jpg"
    assert img.path.parent == mio.data_dir_path()
    assert img.path.name == "einstein.jpg"


@patch("menpo.io.input.base._pathlib_glob_for_pattern")
def test_single_suffix_dot_in_path(pathlib_glob):
    import menpo.io.input.base as mio_base
    from pathlib import Path

    fake_path = Path("fake_path.t0.t1.t2")
    pathlib_glob.return_value = [fake_path]
    ext_map = MagicMock()
    ext_map.__contains__.side_effect = lambda x: x == ".t2"

    ret_val = next(mio_base.glob_with_suffix("*.t0.t1.t2", ext_map))
    assert ret_val == fake_path
    ext_map.__contains__.assert_called_with(".t2")


def test_upper_extension_mapped_to_lower():
    import menpo.io.input.base as mio_base
    from pathlib import Path

    ext_map = MagicMock()

    mio_base.importer_for_filepath(Path("fake_path.JPG"), ext_map)
    ext_map.get.assert_called_with(".jpg")


@patch("menpo.io.input.base._pathlib_glob_for_pattern")
def test_double_suffix(pathlib_glob):
    import menpo.io.input.base as mio_base
    from pathlib import Path

    fake_path = Path("fake_path.t1.t2")
    pathlib_glob.return_value = [fake_path]
    ext_map = MagicMock()
    ext_map.__contains__.side_effect = lambda x: x == ".t1.t2"

    ret_val = next(mio_base.glob_with_suffix("*.t1.t2", ext_map))
    assert ret_val == fake_path
    ext_map.__contains__.assert_any_call(".t1.t2")
    ext_map.__contains__.assert_any_call(".t2")


def test_import_image():
    img_path = mio.data_dir_path() / "einstein.jpg"
    im = mio.import_image(img_path)
    assert im.pixels.dtype == float
    assert im.n_channels == 1


def test_custom_landmark_resolver():
    def lmark_resolver(path):
        return mio.import_landmark_file(mio.data_path_to("takeo.pts"))

    img = mio.import_image(
        mio.data_path_to("lenna.png"), landmark_resolver=lmark_resolver
    )
    assert img.has_landmarks

    takeo_lmarks = mio.import_builtin_asset.takeo_pts()["PTS"]
    np.allclose(img.landmarks["PTS"].points, takeo_lmarks.points)


def test_landmark_resolver_none():
    img = mio.import_image(mio.data_path_to("lenna.png"), landmark_resolver=None)
    assert not img.has_landmarks


def test_import_image_no_norm():
    img_path = mio.data_dir_path() / "einstein.jpg"
    im = mio.import_image(img_path, normalize=False)
    assert im.pixels.dtype == np.uint8


def test_import_landmark_file():
    lm_path = mio.data_dir_path() / "einstein.pts"
    mio.import_landmark_file(lm_path)


def test_import_images():
    imgs = list(mio.import_images(mio.data_dir_path()))
    imgs_filenames = set(i.path.stem for i in imgs)
    exp_imgs_filenames = {
        "einstein",
        "takeo",
        "tongue",
        "breakingbad",
        "lenna",
        "menpo_thumbnail",
    }
    assert exp_imgs_filenames == imgs_filenames


def test_import_images_are_ordered_and_unduplicated():
    # we know that import_images returns images in path order
    imgs = list(mio.import_images(mio.data_dir_path()))
    imgs_filenames = [i.path.stem for i in imgs]
    print(imgs_filenames)
    exp_imgs_filenames = [
        "breakingbad",
        "einstein",
        "lenna",
        "menpo_thumbnail",
        "takeo",
        "tongue",
    ]
    assert exp_imgs_filenames == imgs_filenames


def test_lsimgs_filenamess():
    assert set(mio.ls_builtin_assets()) == {
        "breakingbad.jpg",
        "einstein.jpg",
        "einstein.pts",
        "lenna.png",
        "breakingbad.pts",
        "lenna.ljson",
        "takeo.ppm",
        "takeo.pts",
        "tongue.jpg",
        "tongue.pts",
        "menpo_thumbnail.jpg",
    }


def test_image_paths():
    ls = mio.image_paths(mio.data_dir_path())
    assert len(list(ls)) == 6


def test_import_images_wrong_path_raises_value_error():
    with raises(ValueError):
        list(mio.import_images("asldfjalkgjlaknglkajlekjaltknlaekstjlakj"))


def test_import_landmark_files_wrong_path_raises_value_error():
    with raises(ValueError):
        list(mio.import_landmark_files("asldfjalkgjlaknglkajlekjaltknlaekstjlakj"))


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_no_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("RGBA", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.png", normalize=False)
    assert im.shape == (15, 10)
    assert im.n_channels == 4
    assert im.pixels.dtype == np.uint8


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_RGBA_normalize(is_file, mock_image):
    from menpo.image import MaskedImage

    mock_image.return_value = PILImage.new("RGBA", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=True)
    assert im.shape == (15, 10)
    assert im.n_channels == 3
    assert im.pixels.dtype == float
    assert type(im) == MaskedImage


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_RGBA_no_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("RGBA", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=False)
    assert im.shape == (15, 10)
    assert im.n_channels == 4
    assert im.pixels.dtype == np.uint8


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_L_no_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("L", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=False)
    assert im.shape == (15, 10)
    assert im.n_channels == 1
    assert im.pixels.dtype == np.uint8


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_L_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("L", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=True)
    assert im.shape == (15, 10)
    assert im.n_channels == 1
    assert im.pixels.dtype == float


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_I_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("I", (10, 15))
    is_file.return_value = True
    with raises(ValueError):
        mio.import_image("fake_image_being_mocked.ppm", normalize=True)


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_I_no_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("I", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=False)
    assert im.shape == (15, 10)
    assert im.n_channels == 1
    assert im.pixels.dtype == np.int32


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_1_normalize(is_file, mock_image):
    from menpo.image import BooleanImage

    mock_image.return_value = PILImage.new("1", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=True)
    assert im.shape == (15, 10)
    assert im.n_channels == 1
    assert im.pixels.dtype == bool
    assert type(im) == BooleanImage


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_1_no_normalize(is_file, mock_image):
    from menpo.image import BooleanImage

    mock_image.return_value = PILImage.new("1", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=False)
    assert im.shape == (15, 10)
    assert im.n_channels == 1
    assert im.pixels.dtype == bool
    assert type(im) == BooleanImage


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_1_proper_conversion(is_file, mock_image):
    from menpo.image import BooleanImage

    arr = np.zeros((10, 10), dtype=np.uint8)
    arr[4, 4] = 255
    mock_image.return_value = PILImage.fromarray(arr).convert("1")
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=False)
    assert im.shape == (10, 10)
    assert im.n_channels == 1
    assert im.pixels.dtype == bool
    assert type(im) == BooleanImage
    assert np.all(im.pixels == arr.astype(bool))


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_P_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("P", (10, 10))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=True)
    assert im.shape == (10, 10)
    assert im.n_channels == 3
    assert im.pixels.dtype == float


@patch("PIL.Image.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_PIL_P_no_normalize(is_file, mock_image):
    mock_image.return_value = PILImage.new("P", (10, 15))
    is_file.return_value = True

    im = mio.import_image("fake_image_being_mocked.ppm", normalize=False)
    assert im.shape == (15, 10)
    assert im.n_channels == 3
    assert im.pixels.dtype == np.uint8


@patch("subprocess.Popen")
@patch("menpo.io.input.video.video_infos_ffprobe")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_ffmpeg_GIF_normalize(is_file, video_infos_ffprobe, pipe):
    video_infos_ffprobe.return_value = {
        "duration": 2,
        "width": 100,
        "height": 150,
        "n_frames": 10,
        "fps": 5,
    }
    empty_frame = np.zeros(150 * 100 * 3, dtype=np.uint8).tobytes()
    pipe.return_value.stdout.read.return_value = empty_frame
    is_file.return_value = True

    ll = mio.import_image("fake_image_being_mocked.gif", normalize=True)
    assert ll.path.name == "fake_image_being_mocked.gif"
    assert ll.fps == 5
    assert len(ll) == 10

    im = ll[0]
    assert im.shape == (150, 100)
    assert im.n_channels == 3
    assert im.pixels.dtype == float


@patch("subprocess.Popen")
@patch("menpo.io.input.video.video_infos_ffprobe")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_ffmpeg_GIF_no_normalize(is_file, video_infos_ffprobe, pipe):
    video_infos_ffprobe.return_value = {
        "duration": 2,
        "width": 100,
        "height": 150,
        "n_frames": 10,
        "fps": 5,
    }
    empty_frame = np.zeros(150 * 100 * 3, dtype=np.uint8).tobytes()
    pipe.return_value.stdout.read.return_value = empty_frame
    is_file.return_value = True

    ll = mio.import_image("fake_image_being_mocked.gif", normalize=False)
    assert ll.path.name == "fake_image_being_mocked.gif"
    assert ll.fps == 5
    assert len(ll) == 10

    im = ll[0]
    assert im.shape == (150, 100)
    assert im.n_channels == 3
    assert im.pixels.dtype == np.uint8


@patch("menpo.io.input.landmark.json.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_v1_ljson_null_values(is_file, mock_open, mock_dict):
    v1_ljson = {
        "groups": [
            {
                "connectivity": [[0, 1], [1, 2], [2, 3]],
                "label": "chin",
                "landmarks": [
                    {"point": [987.9, 1294.1]},
                    {"point": [96.78, 1246.8]},
                    {"point": [None, 0.1]},
                    {"point": [303.22, 167.2]},
                ],
            },
            {
                "connectivity": [[0, 1]],
                "label": "leye",
                "landmarks": [{"point": [None, None]}, {"point": [None, None]}],
            },
        ],
        "version": 1,
    }
    mock_dict.return_value = v1_ljson
    is_file.return_value = True

    with warnings.catch_warnings(record=True) as found_warnings:
        lmark = mio.import_landmark_file("fake_lmark_being_mocked.ljson", group="LJSON")
    nan_points = np.isnan(lmark.points)

    # Should raise deprecation warning
    assert len(found_warnings) == 1, [w.message for w in found_warnings]
    assert nan_points[2, 0]  # y-coord None point is nan
    assert not nan_points[2, 1]  # x-coord point is not nan
    assert np.all(nan_points[4:, :])  # all of leye label is nan


@patch("menpo.io.input.landmark.json.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_v2_ljson_null_values(is_file, mock_open, mock_dict):
    v2_ljson = {
        "labels": [
            {"label": "left_eye", "mask": [0, 1, 2]},
            {"label": "right_eye", "mask": [3, 4, 5]},
        ],
        "landmarks": {
            "connectivity": [[0, 1], [1, 2], [2, 0], [3, 4], [4, 5], [5, 3]],
            "points": [
                [None, 200.5],
                [None, None],
                [316.8, 199.15],
                [339.48, 205.0],
                [358.54, 217.82],
                [375.0, 233.4],
            ],
        },
        "version": 2,
    }

    mock_dict.return_value = v2_ljson
    is_file.return_value = True
    with warnings.catch_warnings(record=True) as w:
        lmark = mio.import_landmark_file("fake_lmark_being_mocked.ljson", group="LJSON")
    nan_points = np.isnan(lmark.points)
    assert nan_points[0, 0]  # y-coord None point is nan
    assert not nan_points[0, 1]  # x-coord point is not nan
    assert np.all(nan_points[1, :])  # all of leye label is nan


@patch("menpo.io.input.landmark.json.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_v3_ljson_null_values(is_file, mock_open, mock_dict):
    v3_ljson = {
        "groups": {
            "LJSON": {
                "labels": [
                    {"label": "left_eye", "mask": [0, 1, 2]},
                    {"label": "right_eye", "mask": [3, 4, 5]},
                ],
                "landmarks": {
                    "connectivity": [[0, 1], [1, 2], [2, 0], [3, 4], [4, 5], [5, 3]],
                    "points": [
                        [None, 200.5],
                        [None, None],
                        [316.8, 199.15],
                        [339.48, 205.0],
                        [358.54, 217.82],
                        [375.0, 233.4],
                    ],
                },
            }
        },
        "version": 3,
    }

    mock_dict.return_value = v3_ljson
    is_file.return_value = True
    lmark_dict = mio.import_landmark_file("fake_lmark_being_mocked.ljson")
    assert isinstance(lmark_dict, dict)
    lmark = lmark_dict["LJSON"]
    nan_points = np.isnan(lmark.points)
    assert nan_points[0, 0]  # y-coord None point is nan
    assert not nan_points[0, 1]  # x-coord point is not nan
    assert np.all(nan_points[1, :])  # all of leye label is nan


def test_export_import_pointcloud_ljson_v3(tmpdir):
    tmpdir = Path(tmpdir)
    pc = PointCloud(np.random.random([10, 2]), copy=False)
    mio.export_landmark_file(pc, tmpdir / "test.ljson")
    loaded_pc = mio.import_landmark_file(tmpdir / "test.ljson")["LJSON"]
    np.testing.assert_allclose(pc.points, loaded_pc.points)


@patch("random.shuffle")
def test_shuffle_kwarg_true_calls_shuffle(mock):
    list(mio.import_images(mio.data_dir_path(), shuffle=True))
    assert mock.called


def test_import_as_generator():
    import types

    gen = mio.import_images(mio.data_dir_path(), as_generator=True)
    assert isinstance(gen, types.GeneratorType)
    gen = mio.import_landmark_files(mio.data_dir_path(), as_generator=True)
    assert isinstance(gen, types.GeneratorType)


def test_import_lazy_list():
    from menpo.base import LazyList

    data_path = mio.data_dir_path()
    ll = mio.import_images(data_path)
    assert isinstance(ll, LazyList)
    ll = mio.import_landmark_files(data_path)
    assert isinstance(ll, LazyList)


@patch("menpo.io.input.pickle.pickle.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_pickle(is_file, mock_open, mock_pickle):
    mock_pickle.return_value = {"test": 1}
    is_file.return_value = True

    objs = mio.import_pickle("mocked.pkl")
    assert isinstance(objs, dict)
    assert "test" in objs
    assert objs["test"] == 1


@patch("menpo.io.input.pickle.pickle.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.is_file")
@patch("sys.version_info")
def test_importing_pickle_encoding_py3(version_info, is_file, mock_open, mock_pickle):
    version_info.major = 3
    mock_pickle.return_value = {"test": 1}
    is_file.return_value = True

    mio.import_pickle("mocked.pkl", encoding="latin1")
    assert mock_pickle.call_args[1].get("encoding") == "latin1"


@patch("menpo.io.input.pickle.pickle.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.is_file")
@patch("sys.version_info")
def test_importing_pickle_encoding_ignored_py2(
    version_info, is_file, mock_open, mock_pickle
):
    version_info.major = 2
    mock_pickle.return_value = {"test": 1}
    is_file.return_value = True

    mio.import_pickle("mocked.pkl", encoding="latin1")
    assert "encoding" not in mock_pickle.call_args[1]


@patch("menpo.io.input.pickle.pickle.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.glob")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_pickles(is_file, glob, mock_open, mock_pickle):
    from pathlib import Path
    from menpo.base import LazyList

    mock_pickle.return_value = {"test": 1}
    is_file.return_value = True
    glob.return_value = [Path("mocked1.pkl"), Path("mocked2.pkl")]

    objs = mio.import_pickles("*")
    assert isinstance(objs, LazyList)
    assert len(objs) == 2
    assert objs[0]["test"] == 1
    assert objs[1]["test"] == 1


@patch("menpo.io.input.pickle.pickle.load")
@patch("menpo.io.input.base.Path.open")
@patch("menpo.io.input.base.Path.glob")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_pickles_as_generator(is_file, glob, mock_open, mock_pickle):
    from pathlib import Path
    import types

    mock_pickle.return_value = {"test": 1}
    is_file.return_value = True
    glob.return_value = [Path("mocked1.pkl"), Path("mocked2.pkl")]

    objs = mio.import_pickles("*", as_generator=True)
    assert isinstance(objs, types.GeneratorType)
    objs = list(objs)
    assert len(objs) == 2
    assert objs[0]["test"] == 1
    assert objs[1]["test"] == 1


@patch("subprocess.Popen")
@patch("menpo.io.input.video.video_infos_ffprobe")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_ffmpeg_avi_no_normalize(is_file, video_infos_ffprobe, pipe):
    video_infos_ffprobe.return_value = {
        "duration": 2,
        "width": 100,
        "height": 150,
        "n_frames": 10,
        "fps": 5,
    }
    is_file.return_value = True
    empty_frame = np.zeros(150 * 100 * 3, dtype=np.uint8).tobytes()
    pipe.return_value.stdout.read.return_value = empty_frame
    ll = mio.import_video("fake_image_being_mocked.avi", normalize=False)
    assert ll.path.name == "fake_image_being_mocked.avi"
    assert ll.fps == 5
    assert len(ll) == 5 * 2
    image = ll[0]
    assert image.shape == ((150, 100))
    assert image.n_channels == 3
    assert image.pixels.dtype == np.uint8


@patch("subprocess.Popen")
@patch("menpo.io.input.video.video_infos_ffprobe")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_ffmpeg_avi_normalize(is_file, video_infos_ffprobe, pipe):
    video_infos_ffprobe.return_value = {
        "duration": 2,
        "width": 100,
        "height": 150,
        "n_frames": 10,
        "fps": 5,
    }
    is_file.return_value = True
    empty_frame = np.zeros(150 * 100 * 3, dtype=np.uint8).tobytes()
    pipe.return_value.stdout.read.return_value = empty_frame
    ll = mio.import_video("fake_image_being_mocked.avi", normalize=True)
    assert ll.path.name == "fake_image_being_mocked.avi"
    assert ll.fps == 5
    assert len(ll) == 5 * 2
    image = ll[0]
    assert image.shape == (150, 100)
    assert image.n_channels == 3
    assert image.pixels.dtype == float


@patch("menpo.io.input.video.video_infos_ffprobe")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_ffmpeg_exact_frame_count_no_ffprobe(is_file, video_infos_ffprobe):
    video_infos_ffprobe.side_effect = ValueError
    is_file.return_value = True
    with raises(ValueError):
        mio.import_video(
            "fake_image_being_mocked.avi", normalize=True, exact_frame_count=True
        )


@patch("subprocess.Popen")
@patch("menpo.io.input.video.video_infos_ffprobe")
@patch("menpo.io.input.video.video_infos_ffmpeg")
@patch("menpo.io.input.base.Path.is_file")
def test_importing_ffmpeg_no_exact_frame_count_no_ffprobe(
    is_file, video_infos_ffmpeg, video_infos_ffprobe, pipe
):
    video_infos_ffprobe.side_effect = ValueError
    video_infos_ffmpeg.return_value = {
        "duration": 2,
        "width": 100,
        "height": 150,
        "n_frames": 10,
        "fps": 5,
    }
    is_file.return_value = True
    empty_frame = np.zeros(150 * 100 * 3, dtype=np.uint8).tobytes()
    pipe.return_value.stdout.read.return_value = empty_frame
    ll = mio.import_video(
        "fake_image_being_mocked.avi", normalize=True, exact_frame_count=False
    )
    assert ll.path.name == "fake_image_being_mocked.avi"
    assert ll.fps == 5
    assert len(ll) == 5 * 2
    image = ll[0]
    assert image.shape == (150, 100)
    assert image.n_channels == 3
    assert image.pixels.dtype == float


def test_import_images_negative_max_images():
    with raises(ValueError):
        list(mio.import_images(mio.data_dir_path(), max_images=-2))


def test_import_images_zero_max_images():
    with raises(ValueError):
        # different since the conditional 'if max_assets' is skipped,
        # thus all images might be imported.
        list(mio.import_images(mio.data_dir_path(), max_images=0))


# TODO: remove once the normalise argument is removed.
def test_import_image_deprecated_normalise_kwarg():
    with warnings.catch_warnings(record=True) as w:
        img = mio.import_builtin_asset("breakingbad.jpg", normalise=False)
        assert len(w) == 1
    assert img.pixels.dtype == np.uint8


@patch("menpo.io.input.base.Path.is_file")
def test_register_image_importer(is_file):
    from menpo.image import Image

    image = Image.init_blank((10, 10))

    def foo_importer(filepath, **kwargs):
        return image

    is_file.return_value = True

    with patch.dict(mio.input.extensions.image_types, {}, clear=True):
        mio.register_image_importer(".foo", foo_importer)
        new_image = mio.import_image("fake.foo")
    assert image is new_image


@patch("menpo.io.input.base.Path.is_file")
def test_register_landmark_importer(is_file):
    from menpo.shape import PointCloud

    lmark = PointCloud.init_2d_grid((1, 1))

    def foo_importer(filepath, **kwargs):
        return lmark

    is_file.return_value = True

    with patch.dict(mio.input.extensions.image_landmark_types, {}, clear=True):
        mio.register_landmark_importer(".foo", foo_importer)
        new_lmark = mio.import_landmark_file("fake.foo")
    assert lmark is new_lmark


@patch("menpo.io.input.base.Path.is_file")
def test_register_video_importer(is_file):
    from menpo.image import Image
    from menpo.base import LazyList

    def foo_importer(filepath, **kwargs):
        return LazyList([lambda: Image.init_blank((10, 10))])

    is_file.return_value = True

    with patch.dict(mio.input.extensions.ffmpeg_video_types, {}, clear=True):
        mio.register_video_importer(".foo", foo_importer)
        new_video = mio.import_video("fake.foo")
    assert len(new_video) == 1


@patch("menpo.io.input.base.Path.is_file")
def test_register_pickle_importer(is_file):
    obj = object()

    def foo_importer(filepath, **kwargs):
        return obj

    is_file.return_value = True

    with patch.dict(mio.input.extensions.pickle_types, {}, clear=True):
        mio.register_pickle_importer(".foo", foo_importer)
        new_obj = mio.import_pickle("fake.foo")
    assert new_obj is obj


def test_register_no_leading_period():
    ext_map = {}
    mio.input.base._register_importer(ext_map, "foo", lambda x: x)
    assert ".foo" in ext_map
    assert "foo" not in ext_map
