# (c) Nelen & Schuurmans.  GPL licensed, see LICENSE.rst.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from __future__ import print_function

from __future__ import absolute_import
from .constants import SHP_DRIVER_NAME, GEO_PACKAGE_DRIVER_NAME, \
    GEOJSON_DRIVER_NAME

from threedigrid.orm.base.models import Model as BaseModel
from threedigrid.orm.fields import LineArrayField
from threedigrid.orm.fields import GeomArrayField
from threedigrid.orm.filters import FILTER_MAP
from threedigrid.admin.exporter_constants import DEFAULT_EXPORT_FIELDS
from threedigrid.admin.serializers import GeoJsonSerializer


class Model(BaseModel):
    _filter_map = FILTER_MAP

    def to_centroid(self):
        """
        Returns: the filtered values with geometries
                 transformed to centroids
        """
        selection = self.data
        for field_name in self._field_names:
            field = self._get_field(field_name)
            if isinstance(field, LineArrayField) and field_name in selection:
                # Use the to_centroid function of the GeomArrayField
                # to allow different geometry to be "centroided"
                selection[field_name] =\
                    field.to_centroid(
                        selection[field_name])
        return selection

    def _is_coords(self, field_name):
        return isinstance(self._get_field(field_name), GeomArrayField)

    def _includes_coords(self, selection):
        """
        Returns: true if selection contains any data that
                 has coordinates.
        """
        found = False
        for field_name in self._field_names:
            if isinstance(self._get_field(field_name), GeomArrayField):
                found = True
                break
        return found

    def reproject_to(self, target_epsg_code):
        """
        Returns: a new class including the reprojection.

        Note: the last reproject_to is used:
            reproject_to('28992').reproject_to('4326') == reproject_to('4326')
        """
        new_class_kwargs = dict(self.class_kwargs)
        new_class_kwargs.update({
            'reproject_to_epsg': target_epsg_code})

        return self.__init_class(
            self.__class__, **new_class_kwargs)

    def _do_reproject_value(self, value, field_name, target_epsg_code):
        if target_epsg_code == self.epsg_code:
            # Already done
            return value

        field = self._get_field(field_name)

        return field.reproject(
            value, self.epsg_code, target_epsg_code)

    def __do_reproject(self, selection, target_epsg_code):
        """
        Reproject coordinate fields in selection to the
        target_epsg_code
        """
        if target_epsg_code == self.epsg_code:
            # Already done
            return selection

        # Reproject all GeomDataField's
        for field_name in self._field_names:
            field = self._get_field(field_name)
            if isinstance(field, GeomArrayField) and field_name in selection:
                # Use the reproject function of the GeomDataField
                # to allow different geometry to be reprojected
                selection[field_name] =\
                    field.reproject(
                        selection[field_name],
                        self.epsg_code, target_epsg_code)
        return selection

    def to_shape(self, file_name, **kwargs):
        self._to_ogr(SHP_DRIVER_NAME, file_name, **kwargs)

    def to_gpkg(self, file_name, **kwargs):
        self._to_ogr(
            GEO_PACKAGE_DRIVER_NAME, file_name, **kwargs)

    def to_geojson(self, file_name, **kwargs):
        if kwargs.get('use_ogr', False):
            self._to_ogr(GEOJSON_DRIVER_NAME, file_name, **kwargs)
        else:
            fields = kwargs.get(
                'fields', DEFAULT_EXPORT_FIELDS[self.__class__.__name__]
            )
            if fields == 'ALL':
                fields = self._field_names

            indent = kwargs.get('indent', None)
            serializer = GeoJsonSerializer(fields, self, indent)
            serializer.save(file_name)

    def _to_ogr(self, driver_name, file_name, **kwargs):
        exporter = self._get_exporter(driver_name)
        if not exporter:
            raise AttributeError(
                "Instance {} has no {} exporter".format(self, driver_name)
            )
        filtered = self.data
        exporter.set_driver(driver_name=driver_name)

        epsg_code = self.epsg_code
        if self.reproject_to_epsg:
            epsg_code = self.reproject_to_epsg

        exporter.save(file_name, filtered, epsg_code,
                      **kwargs)

    def _get_exporter(self, driver_name):
        for exporter in self._exporters:
            if driver_name in exporter.supported_drivers:
                return exporter
        return None
