"""
Functions to open or create space time datasets

Usage:

.. code-block:: python

    import grass.temporal as tgis

    tgis.register_maps_in_space_time_dataset(type, name, maps)


(C) 2012-2014 by the GRASS Development Team
This program is free software under the GNU General Public
License (>=v2). Read the file COPYING that comes with GRASS
for details.

:authors: Soeren Gebbert
"""

from .abstract_map_dataset import AbstractMapDataset
from .core import get_current_mapset, get_tgis_message_interface, init_dbif
from .factory import dataset_factory

###############################################################################


def open_old_stds(name, type, dbif=None):
    """This function opens an existing space time dataset and return the
    created and initialized object of the specified type.

    This function will call exit() or raise a
    grass.pygrass.messages.FatalError in case the type is wrong,
    or the space time dataset was not found.

    :param name: The name of the space time dataset, if the name does not
                 contain the mapset (name@mapset) then the current mapset
                 will be used to identify the space time dataset
    :param type: The type of the space time dataset (strd, str3ds, stvds,
                 raster, vector, raster3d)
    :param dbif: The optional database interface to be used

    :return: New stds object

    """
    msgr = get_tgis_message_interface()

    # Check if the dataset name contains the mapset and the semantic label as well
    if name.find("@") < 0:
        mapset = get_current_mapset()
    else:
        name, mapset = name.split("@")
    semantic_label = None
    if name.find(".") > -1:
        try:
            name, semantic_label = name.split(".")
        except ValueError:
            msgr.fatal("Invalid name of the space time dataset. Only one dot allowed.")
    id = name + "@" + mapset

    if type in {"strds", "rast", "raster"}:
        sp = dataset_factory("strds", id)
        if semantic_label:
            sp.set_semantic_label(semantic_label)
    elif type in {"str3ds", "raster3d", "rast3d", "raster_3d"}:
        sp = dataset_factory("str3ds", id)
    elif type in {"stvds", "vect", "vector"}:
        sp = dataset_factory("stvds", id)
    else:
        msgr.fatal(_("Unknown type: %s") % (type))

    dbif, connection_state_changed = init_dbif(dbif)

    if not sp.is_in_db(dbif):
        dbif.close()
        msgr.fatal(
            _("Space time %(sp)s dataset <%(id)s> not found")
            % {"sp": sp.get_new_map_instance(None).get_type(), "id": id}
        )
    # Read content from temporal database
    sp.select(dbif)
    if connection_state_changed:
        dbif.close()

    return sp


###############################################################################


def check_new_stds(name, type, dbif=None, overwrite: bool = False):
    """Check if a new space time dataset of a specific type can be created

    :param name: The name of the new space time dataset
    :param type: The type of the new space time dataset (strd, str3ds,
                 stvds, raster, vector, raster3d)
    :param dbif: The temporal database interface to be used
    :param overwrite: Flag to allow overwriting

    :return: A space time dataset object that must be filled with
            content before insertion in the temporal database

    This function will raise a FatalError in case of an error.
    """

    # Get the current mapset to create the id of the space time dataset

    mapset = get_current_mapset()
    msgr = get_tgis_message_interface()

    if name.find("@") < 0:
        id = name + "@" + mapset
    else:
        n, m = name.split("@")
        if mapset != m:
            msgr.fatal(
                _("Space time datasets can only be created in the current mapset")
            )
        id = name

    if type in {"strds", "rast", "raster"}:
        if name.find(".") > -1:
            # a dot is used as a separator for semantic label filtering
            msgr.fatal(
                _("Illegal dataset name <{}>. Character '.' not allowed.").format(name)
            )
        sp = dataset_factory("strds", id)
    elif type in {"str3ds", "raster3d", "rast3d ", "raster_3d"}:
        sp = dataset_factory("str3ds", id)
    elif type in {"stvds", "vect", "vector"}:
        sp = dataset_factory("stvds", id)
    else:
        msgr.error(_("Unknown type: %s") % (type))
        return None

    dbif, connection_state_changed = init_dbif(dbif)

    if sp.is_in_db(dbif) and overwrite is False:
        msgr.fatal(
            _(
                "Space time %(sp)s dataset <%(name)s> is already in the"
                " database. Use the overwrite flag."
            )
            % {"sp": sp.get_new_map_instance(None).get_type(), "name": name}
        )
    if connection_state_changed:
        dbif.close()

    return sp


###############################################################################


def open_new_stds(
    name, type, temporaltype, title, descr, semantic, dbif=None, overwrite: bool = False
):
    """Create a new space time dataset of a specific type

    :param name: The name of the new space time dataset
    :param type: The type of the new space time dataset (strd, str3ds,
                 stvds, raster, vector, raster3d)
    :param temporaltype: The temporal type (relative or absolute)
    :param title: The title
    :param descr: The dataset description
    :param semantic: Semantical information
    :param dbif: The temporal database interface to be used
    :param overwrite: Flag to allow overwriting

    :return: The new created space time dataset

    This function will raise a FatalError in case of an error.
    """
    dbif, connection_state_changed = init_dbif(dbif)
    msgr = get_tgis_message_interface()
    sp = check_new_stds(name, type, dbif, overwrite)

    if sp.is_in_db(dbif):
        msgr.warning(
            _(
                "Overwriting space time %(sp)s dataset <%(name)s> and "
                "unregistering all maps"
            )
            % {"sp": sp.get_new_map_instance(None).get_type(), "name": name}
        )
        id = sp.get_id()
        sp.delete(dbif)
        sp = sp.get_new_instance(id)

    msgr.verbose(
        _("Creating a new space time %s dataset")
        % sp.get_new_map_instance(None).get_type()
    )

    sp.set_initial_values(
        temporal_type=temporaltype,
        semantic_type=semantic,
        title=title,
        description=descr,
    )

    sp.insert(dbif)

    if connection_state_changed:
        dbif.close()

    return sp


############################################################################


def check_new_map_dataset(
    name, layer=None, type="raster", overwrite: bool = False, dbif=None
):
    """Check if a new map dataset of a specific type can be created in
     the temporal database

    :param name: The name of the new map dataset
    :param layer: The layer of the new map dataset
    :param type: The type of the new map dataset (raster, vector, raster3d)
    :param dbif: The temporal database interface to be used
    :param overwrite: Flag to allow overwriting

    :return: A map dataset object

    This function will raise a FatalError in case of an error.
    """
    mapset = get_current_mapset()
    msgr = get_tgis_message_interface()

    dbif, connection_state_changed = init_dbif(dbif)
    map_id = AbstractMapDataset.build_id(name, mapset, layer)

    new_map = dataset_factory(type, map_id)
    # Check if new map is in the temporal database
    if new_map.is_in_db(dbif):
        if not overwrite:
            if connection_state_changed:
                dbif.close()
            msgr.fatal(
                _(
                    "Map <%s> is already in temporal database,"
                    " use overwrite flag to overwrite"
                )
                % (map_id)
            )

    if connection_state_changed:
        dbif.close()

    return new_map


############################################################################


def open_new_map_dataset(
    name,
    layer=None,
    type="raster",
    temporal_extent=None,
    overwrite: bool = False,
    dbif=None,
):
    """Create a new map dataset object of a specific type that can be
     registered in the temporal database

    :param name: The name of the new map dataset
    :param layer: The layer of the new map dataset
    :param type: The type of the new map dataset (raster, vector, raster3d)
    :param dbif: The temporal database interface to be used
    :param overwrite: Flag to allow overwriting

    :return: A map dataset object

    """

    dbif, connection_state_changed = init_dbif(dbif)
    new_map = check_new_map_dataset(name, layer, type, overwrite, dbif)

    # Check if new map is in the temporal database
    if new_map.is_in_db(dbif):
        # Remove the existing temporal database entry
        map_id = new_map.get_id()
        new_map.delete(dbif)
        new_map = new_map.get_new_instance(map_id)

    if temporal_extent:
        new_map.set_temporal_extent(temporal_extent)

    if connection_state_changed:
        dbif.close()

    return new_map
