"""
Lightweight UGRID-1.0 parser

"""

import netCDF4

import numpy as np


def _valid_x(var):
    names = [
        'longitude',
        'grid_longitude',
        'projection_x_coordinate',
    ]

    units = [
        'degrees_east',
        'degree_east',
        'degree_E',
        'degrees_E',
        'degreeE',
        'degreesE',
    ]
    if getattr(var, 'standard_name', None) in names:
        return True
    if getattr(var, 'axis', 'None').lower() == 'x':
        return True
    # Units are mandatory, fail if not present.
    if var.units in units:
        return True
    return False


def _valid_y(var):
    names = [
        'latitude',
        'grid_latitude',
        'projection_y_coordinate',
    ]

    units = [
        'degrees_north',
        'degree_north',
        'degree_N',
        'degrees_N',
        'degreeN',
        'degreesN',
    ]

    if getattr(var, 'standard_name', None) in names:
        return True
    if getattr(var, 'axis', 'None').lower() == 'y':
        return True
    # Units are mandatory, fail if not present.
    if var.units in units:
        return True
    return False


def _mandatory_attr(var, attribute):
    if not hasattr(var, attribute):
        raise ValueError(f'Could not find required attribute {attribute} in {var}.')
    return


def get_mesh_var(nc):
    """Returns the mesh_topology variable for `nc` (netCDF4.Dataset object)."""
    mesh_var = nc.get_variables_by_attributes(cf_role='mesh_topology')
    if not mesh_var:
        raise ValueError(f'Could not find mesh_topology variable in the dataset {nc}')
    if len(mesh_var) > 1:
        raise ValueError(f'Expected 1 mesh_topology variable, found {len(mesh_var)}.')

    mesh_var = mesh_var[0]
    _mandatory_attr(mesh_var, attribute='node_coordinates')
    _mandatory_attr(mesh_var, attribute='topology_dimension')

    if mesh_var.topology_dimension not in (1, 2):
        raise ValueError(f'Expected mesh dimension to be 1 or 2, got {mesh_var.topology_dimension}.')
    return mesh_var


def connectivity_array(connectivity, num_ind):
    """Returns the connectivity array for its correspdonding `netCDF4.Variable`
    according to UGRID-1.0.

    """
    array = connectivity[:]
    if not issubclass(array.dtype.type, np.integer):
        array = np.int_(array)
    if array.shape[0] == num_ind:
        array = array.T

    start_index = int(getattr(connectivity, 'start_index', 0))
    if start_index >= 1:
        array -= start_index

    # FIXME: This won't work for more than one flag value.
    flag_values = getattr(connectivity, 'flag_values', None)
    if flag_values:
        array[array == flag_values - start_index] = flag_values
    return array


def ugrid(nc):
    """Parse UGRID conventions.

    Take a netCDF4.Dataset object or a netCDF4 file/url string
    and returns a dictionary with the grid nodes, edges, and connectivy matrix.

    """
    if isinstance(nc, netCDF4.Dataset):
        pass
    else:
        nc = netCDF4.Dataset(nc)

    mesh_var = get_mesh_var(nc)
    valid_coords = (
        'node_coordinates',
        'face_coordinates',
        'edge_coordinates',
        'boundary_coordinates'
    )
    valid_connectivity = {
        'face_node_connectivity': 3,
        'face_face_connectivity': 3,
        'boundary_node_connectivity': 2,
        'edge_node_connectivity': 2,
    }

    # Used for compatibility with pyugrid.
    rename = {
        'node_coordinates': 'nodes',
        'face_node_connectivity': 'faces',
        'boundary_node_connectivity': 'boundaries',
        'edge_node_connectivity': 'edges',
    }

    grid = {}
    for key, value in mesh_var.__dict__.items():
        if key in valid_coords:
            coord_names = mesh_var.getncattr(key).strip().split()
            for name in coord_names:
                if _valid_x(nc[name]):
                    x = nc[name][:]
                elif _valid_y(nc[name]):
                    y = nc[name][:]
                else:
                    raise ValueError(f'Could not recognize axis for {nc[name]}')
            grid.update({key: {'x': x, 'y': y}})
        if key in valid_connectivity.keys():
            connectivity = nc[mesh_var.getncattr(key).strip()]
            num_ind = valid_connectivity[key]
            array = connectivity_array(connectivity, num_ind)
            grid.update({key: array})

    return {rename.get(k, k): v for k, v in grid.items()}
