"""Test DAP4 parsing functions."""

import os
import re
import unittest
from xml.etree import ElementTree as ET

import numpy as np

from ..lib import walk
from ..model import BaseType
from ..parsers.dmr import dmr_to_dataset, get_groups


def load_dmr_file(file_path):
    abs_path = os.path.join(os.path.dirname(__file__), file_path)
    with open(abs_path, "r") as dmr_file:
        dmr = dmr_file.read()
    dataset = dmr_to_dataset(dmr)
    return dataset


class TestDMRParser(unittest.TestCase):
    """Test parsing a DMR."""

    def test_single_scalar(self):
        """Test a single scalar case."""
        single_scalar_dmr = """<Dataset name="foo"><Int32 name="x"/></Dataset>"""
        dataset = dmr_to_dataset(single_scalar_dmr)
        self.assertEqual(dataset["x"].dtype, ">i4")

    def test_missing_value(self):
        """Test cases with missing value."""
        NAN_dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="missing_value" type="Float32">\n
                            <Value>NaN</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        INF_dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="missing_value" type="Float32">\n
                            <Value>Inf</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""

        dataset_nan = dmr_to_dataset(NAN_dmr)
        dataset_inf = dmr_to_dataset(INF_dmr)

        X_nan = dataset_nan["x"]
        X_inf = dataset_inf["x"]
        self.assertEqual(np.isnan(X_nan.attributes["missing_value"]), True)
        self.assertEqual(np.isinf(X_inf.attributes["missing_value"]), True)

    def test_attribute_warning(self):
        byte_dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="missing_value" type="Byte">\n
                            <Value>x00</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        with self.assertRaises(Warning):
            dmr_to_dataset(byte_dmr)

    def test_coads_climatology2(self):
        dataset = load_dmr_file("data/dmrs/coads_climatology.nc.dmr")
        self.assertEqual(
            dataset["SST"].attributes["long_name"], "SEA SURFACE TEMPERATURE"
        )
        self.assertEqual(dataset["SST"].attributes["missing_value"], -9.99999979e33)
        self.assertEqual(dataset["AIRT"].shape, (12, 90, 180))
        self.assertEqual(dataset["VWND"].dtype.str, ">f4")

    def test_atl03(self):
        # Contains groups
        dataset = load_dmr_file(
            "data/dmrs/ATL03_20181228015957_13810110_003_01.2var.h5.dmrpp.dmr"
        )
        self.assertEqual(
            dataset["/gt1r/bckgrd_atlas/bckgrd_int_height"].attributes["contentType"],
            "modelResult",
        )

    def test_jpl(self):
        # Contains groups
        dataset = load_dmr_file(
            "data/dmrs/20220102090000-JPL-L4_GHRSST-SSTfnd-MUR-GLOB-v02.0-fv04.1.dmr"
        )
        self.assertEqual(
            dataset["sea_ice_fraction"].dimensions, ["/time", "/lat", "/lon"]
        )
        self.assertEqual(dataset.attributes["Conventions"], "CF-1.7")

    def test_mod05(self):
        dataset = load_dmr_file(
            "data/dmrs/MOD05_L2.A2019336.2315.061.2019337071952.hdf.dmr"
        )
        self.assertEqual(
            dataset["Water_Vapor_Infrared"].dimensions,
            [
                "/Cell_Along_Swath_5km",
                "/Cell_Across_Swath_5km",
            ],
        )

    def test_SWOT(self):
        dataset = load_dmr_file("data/dmrs/SWOT_GPR.dmr")
        # pick a single variable Maps
        maps = ("/data_01/longitude", "/data_01/latitude")
        self.assertEqual(dataset["data_01/ku/swh_ocean"].Maps, maps)

    def tests_global_dimensions(self):
        dataset = load_dmr_file("data/dmrs/SimpleGroup.dmr")
        # pick a single variable Maps
        names = [key for key in dataset.dimensions.keys()]
        sizes = [v[1] for v in dataset.dimensions.items()]
        self.assertEqual(names, ["time", "nv"])
        self.assertEqual(sizes, [1, 2])

    def tests_named_dimension(self):
        dataset = load_dmr_file("data/dmrs/SimpleGroup.dmr")
        # get only names of dimensions
        names = [key for key in dataset.dimensions.keys()]
        # get all variables/arrays
        variables = []
        for var in walk(dataset, BaseType):
            variables.append(var.name)
        # assert nv is a Global dimension
        self.assertIn("nv", names)
        # assert nv is NOT a variable/array
        self.assertNotIn("nv", variables)

    def tests_FlatGroups(self):
        dataset = load_dmr_file("data/dmrs/SimpleGroupFlat.dmr")
        # pick a single variable Maps
        Groups = dataset.groups()
        Variables = [item for (item, _) in dataset.variables().items()]
        self.assertEqual(Groups, {})
        self.assertEqual(
            Variables,
            [
                "SimpleGroup/Temperature",
                "SimpleGroup/Salinity",
                "SimpleGroup/Y",
                "SimpleGroup/X",
                "time",
                "time_bnds",
            ],
        )

    def test_get_groups(self):
        dmr_file = "data/dmrs/SimpleGroup.dmr"
        abs_path = os.path.join(os.path.dirname(__file__), dmr_file)
        with open(abs_path, "r") as dmr_file:
            dmr = dmr_file.read()
        _dmr = re.sub(' xmlns="[^"]+"', "", dmr, count=1)
        node = ET.fromstring(_dmr)
        groups = get_groups(node)
        assert isinstance(groups, dict)
        assert list(groups.keys()) == ["/SimpleGroup"]
        entry = groups["/SimpleGroup"]
        assert entry["attributes"] == {"Description": "Test group with numerical data"}
        assert entry["dimensions"] == {"Y": 4, "X": 4}
        assert entry["path"] == "/"
        assert entry["Maps"] == ()


class TestAttrsTypesDMRParser(unittest.TestCase):
    """Test parsing a DMR with all types"""

    def test_int8(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Int8">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_uint8(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="UInt8">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_Char(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Char">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_int16(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Int16">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_uint16(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="UInt16">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_int32(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Int32">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_uint32(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="UInt32">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_int64(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Int64">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_uint64(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="UInt64">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], int)

    def test_float32(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Float32">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], float)

    def test_float64(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Float64">\n
                            <Value>1</Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], float)

    def test_floatNone(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Float32">\n
                            <Value></Value>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert ds["x"].attributes["attr"] is None

    def test_TDSfloat64(self):
        dmr = """<Dataset name="foo">\n    <Int32 name="x">\n
                <Attribute name="attr" type="Float64">\n
                            <Value value="0.0"/>\n        </Attribute>\n
                                </Int32>\n</Dataset>"""
        ds = dmr_to_dataset(dmr)
        assert isinstance(ds["x"].attributes["attr"], float)
