from django.contrib.gis.geos import Point
from rest_framework import pagination, serializers

from rest_framework_gis import serializers as gis_serializers
from rest_framework_gis.fields import GeometryField, GeometrySerializerMethodField

from .models import (
    BoxedLocation,
    GeometryModel,
    LineStringModel,
    Location,
    MultiLineStringModel,
    MultiPointModel,
    MultiPolygonModel,
    Nullable,
    OtherSridLocation,
    PointModel,
    PolygonModel,
)

__all__ = [
    "LocationGeoSerializer",
    "PaginatedLocationGeoSerializer",
    "LocationGeoFeatureSerializer",
    "LocationGeoFeatureSlugSerializer",
    "LocationGeoFeatureFalseIdSerializer",
    "LocationGeoFeatureNoIdSerializer",
    "LocatedFileGeoFeatureSerializer",
    "BoxedLocationGeoFeatureSerializer",
    "LocationGeoFeatureBboxSerializer",
    "LocationGeoFeatureMethodSerializer",
    "NoneGeoFeatureMethodSerializer",
    "PointSerializer",
    "ChildPointSerializer",
    "ListChildPointSerializer",
    "LineStringSerializer",
    "PolygonSerializer",
    "MultiPolygonSerializer",
    "MultiLineStringSerializer",
    "MultiPointSerializer",
    "GeometrySerializerMethodFieldSerializer",
    "GeometrySerializer",
    "BoxedLocationGeoFeatureWithBBoxGeoFieldSerializer",
    "OtherSridLocationGeoSerializer",
]


class LocationGeoSerializer(serializers.ModelSerializer):
    """location geo serializer"""

    details = serializers.HyperlinkedIdentityField(view_name="api_location_details")

    class Meta:
        model = Location
        fields = "__all__"


class OtherSridLocationGeoSerializer(gis_serializers.GeoFeatureModelSerializer):
    """Other SRID location geo serializer"""

    geometry = GeometryField(auto_bbox=True, transform=4326)

    class Meta:
        model = OtherSridLocation
        geo_field = "geometry"
        fields = "__all__"


class PaginatedLocationGeoSerializer(pagination.PageNumberPagination):
    page_size_query_param = "limit"
    page_size = 40
    max_page_size = 10000


class LocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer):
    """location geo serializer"""

    details = serializers.HyperlinkedIdentityField(
        view_name="api_geojson_location_details"
    )
    fancy_name = serializers.SerializerMethodField()

    def get_fancy_name(self, obj):
        return "Kool %s" % obj.name

    class Meta:
        model = Location
        geo_field = "geometry"
        fields = "__all__"


class LocationGeoFeatureSlugSerializer(LocationGeoFeatureSerializer):
    """use slug as id attribute"""

    class Meta:
        model = Location
        geo_field = "geometry"
        id_field = "slug"
        fields = ("name", "slug", "timestamp")


class LocationGeoFeatureFalseIdSerializer(LocationGeoFeatureSerializer):
    """id attribute set as False"""

    class Meta:
        model = Location
        geo_field = "geometry"
        id_field = False
        fields = "__all__"


class LocationGeoFeatureNoIdSerializer(LocationGeoFeatureSerializer):
    """
    id attribute left out, issue #82
    see: https://github.com/openwisp/django-rest-framework-gis/issues/82
    """

    class Meta:
        model = Location
        geo_field = "geometry"
        fields = ("name",)


class LocationGeoFeatureWritableIdSerializer(LocationGeoFeatureSerializer):
    """default id attribute"""

    class Meta:
        model = Location
        geo_field = "geometry"
        fields = ("id", "name", "timestamp")

    id = serializers.CharField()


class LocatedFileGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer):
    """located file geo serializer"""

    details = serializers.HyperlinkedIdentityField(
        view_name="api_geojson_located_file_details"
    )
    fancy_name = serializers.SerializerMethodField()
    file = serializers.FileField(allow_empty_file=True)

    def get_fancy_name(self, obj):
        return "Nice %s" % obj.name

    class Meta:
        model = Location
        geo_field = "geometry"
        exclude = []


class BoxedLocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer):
    """location geo serializer"""

    details = serializers.HyperlinkedIdentityField(
        view_name="api_geojson_boxedlocation_details"
    )

    class Meta:
        model = BoxedLocation
        geo_field = "geometry"
        bbox_geo_field = "bbox_geometry"
        fields = ["name", "details", "id", "timestamp"]


class LocationGeoFeatureBboxSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = Location
        geo_field = "geometry"
        auto_bbox = True
        exclude = []


class LocationGeoFeatureMethodSerializer(gis_serializers.GeoFeatureModelSerializer):
    new_geometry = gis_serializers.GeometrySerializerMethodField()

    def get_new_geometry(self, obj):
        if obj.name.startswith("hidden"):
            return Point(0.0, 0.0)
        else:
            return obj.geometry

    class Meta:
        model = Location
        geo_field = "new_geometry"
        exclude = []


class NoneGeoFeatureMethodSerializer(gis_serializers.GeoFeatureModelSerializer):
    new_geometry = gis_serializers.GeometrySerializerMethodField()

    def get_new_geometry(self, obj):
        return None

    class Meta:
        model = Location
        geo_field = "new_geometry"
        fields = ["name", "slug", "id"]


class NoGeoFeatureMethodSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = Nullable
        geo_field = None
        fields = ["name", "slug", "id"]


class PointSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = PointModel
        geo_field = "location"
        fields = "__all__"


class ChildPointSerializer(serializers.Serializer):
    point = PointSerializer()


class ListChildPointSerializer(serializers.Serializer):
    points = PointSerializer(many=True)


class LineStringSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = LineStringModel
        geo_field = "points"
        fields = "__all__"


class PolygonSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = PolygonModel
        geo_field = "polygon"
        fields = "__all__"


class PolygonModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = PolygonModel
        fields = "__all__"


class MultiPolygonSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = MultiPolygonModel
        geo_field = "polygon"
        fields = "__all__"


class MultiLineStringSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = MultiLineStringModel
        geo_field = "points"
        fields = "__all__"


class MultiPointSerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = MultiPointModel
        geo_field = "points"
        fields = "__all__"
        auto_bbox = True


class GeometrySerializer(gis_serializers.GeoFeatureModelSerializer):
    class Meta:
        model = GeometryModel
        geo_field = "points"
        fields = "__all__"
        auto_bbox = True


class GeometrySerializerMethodFieldSerializer(PointSerializer):
    other_point = GeometrySerializerMethodField()

    @staticmethod
    def get_other_point(obj):
        return Point(obj.location.lat / 2, obj.location.lon / 2)

    class Meta:
        model = Location
        geo_field = "other_point"
        fields = "__all__"


class BoxedLocationGeoFeatureWithBBoxGeoFieldSerializer(
    BoxedLocationGeoFeatureSerializer
):
    class Meta(BoxedLocationGeoFeatureSerializer.Meta):
        fields = BoxedLocationGeoFeatureSerializer.Meta.fields + [
            BoxedLocationGeoFeatureSerializer.Meta.bbox_geo_field
        ]
