"""The Basic Modeling Interface."""


class Error(Exception):
    """Base class for BMI exceptions."""

    pass


class FatalError(Exception):
    """Raise this exception if an unrecoverable error was found."""

    pass


class BadVarNameError(Error):
    """Exception to indicate a bad input/output variable name."""

    def __init__(self, name):
        super().__init__()
        self.name = name

    def __str__(self):
        return self.name


class MissingModelAttributeError(Error):
    """Raise this exception if a component is missing a required attribute."""

    def __init__(self, attrib):
        super().__init__()
        self.attrib = attrib

    def __str__(self):
        return self.attrib


class TimeBoundsError(Error):
    """Raise this exception if a component updates beyond its time horizon."""

    pass


class BmiGridType(int):
    """Base type to indicate the type of a BMI model's grid.

    :code: Grid type code as an int
    :name: Name of the grid type as a string
    """

    def __new__(cls, code, name):
        obj = super().__new__(cls, code)
        obj.name = name
        return obj

    def __str__(self):
        return self.name

    def __repr__(self):
        return "BmiGridType(%d, %s)" % (self, self.name)


GRID_TYPE_UNKNOWN = BmiGridType(-1, "Unknown")
GRID_TYPE_NONE = BmiGridType(0, "No grid")
GRID_TYPE_UNIFORM = BmiGridType(1, "Uniform rectilinear")
GRID_TYPE_RECTILINEAR = BmiGridType(2, "Rectilinear")
GRID_TYPE_STRUCTURED = BmiGridType(3, "Structured")
GRID_TYPE_UNSTRUCTURED = BmiGridType(4, "Unstructured")


class BmiBase:
    """Definition of the Basic Modeling Interface."""

    def initialize(self, file_name):
        """Initialize model.

        :file_name: String of configuration file
        """
        pass

    def update(self, **kwds):
        """Update model by one time step."""
        pass

    def finalize(self):
        """Clean-up model."""
        pass

    def get_input_var_names(self):
        """Get names of input variables to the model as standard names.

        :returns: A list of input standard names as strings
        """
        pass

    def get_output_var_names(self):
        """Get names of output variables to the model as standard names.

        :returns: A list of output standard names as strings
        """
        pass

    def get_var_type(self, var_name):
        """Get type of an exchange item."""
        pass

    def get_var_units(self, var_name):
        """Get units of an exchange item."""
        pass

    def get_var_rank(self, var_name):
        """Rank of exchange item."""
        pass

    def get_time_step(self):
        """Model time step."""
        pass

    def get_start_time(self):
        """Model start time."""
        pass

    def get_current_time(self):
        """Current time of model."""
        pass

    def get_end_time(self):
        """Model stop time."""
        pass


class BmiExtendedBase:
    """An extension interface for a BMI."""

    def update_until(self, time):
        """Update model until some time.

        :time: Update duration
        """
        pass

    def run_model(self):
        """Initialize, run, and finalize a model."""
        pass


class BmiUnstructured:
    """BMI for a model that uses an unstructured grid."""

    def get_x(self, name):
        """Get x-coordinates of grid nodes."""
        pass

    def get_y(self, name):
        """Get y-coordinates of grid nodes."""
        pass

    def get_connectivity(self, name):
        """Get cell connectivity."""
        pass

    def get_offset(self, name):
        """Get cell offset."""
        pass


class BmiStructured:
    """BMI for a model that uses a structured grid."""

    def get_grid_shape(self, name):
        """Get shape of grid for variable, name.

        :name: Standard name
        """
        pass

    def get_x(self, name):
        """Get x-coordinates of grid nodes."""
        pass

    def get_y(self, name):
        """Get y-coordinates of grid nodes."""
        pass


class BmiRectilinear:
    """BMI for a model that uses a rectilinear grid."""

    def get_grid_shape(self, name):
        """Get shape of grid for variable, name.

        :name: Standard name
        """
        pass

    def get_columns(self, name):
        """Get coordinates of grid columns."""
        pass

    def get_rows(self, name):
        """Get coordinates of grid rows."""
        pass


class BmiUniformRectilinear:
    """BMI for a model that exposes a uniform rectilinear grid."""

    def get_grid_shape(self, name):
        """Get shape of grid for variable, name.

        :name: Standard name
        """
        pass

    def get_grid_spacing(self, name):
        """Get spacing of grid for variable, name.

        :name: Standard name
        """
        pass

    def get_grid_origin(self, name):
        """Get origin of grid for variable, name.

        :name: Standard name
        """
        pass


class BmiNoGrid:
    """BMI for a model that does not have a grid."""
