package com.airmap.airmapsdk.models;

import android.content.Context;
import androidx.core.content.ContextCompat;
import android.text.TextUtils;

import com.airmap.airmapsdk.Analytics;
import com.airmap.airmapsdk.R;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.style.sources.Source;

import java.util.ArrayList;
import java.util.List;

public class CircleContainer extends Container {

    private LatLng center;
    private double radius;
    private List<LatLng> points;

    public CircleContainer(Context context, MapboxMap map) {
        super(context, map);
    }

    public void drawOnMap(LatLng center, double radius) {
        this.center = center;
        this.radius = radius;
        this.points = calculateCirclePoints(center, radius);

        List<Point> positions = latLngsToPositions(points);
        List<List<Point>> coordinates = new ArrayList<>();
        coordinates.add(positions);

        List<Point> lineString = new ArrayList<>(positions);
        lineString.add(positions.get(0));

        // if polygon layer doesn't exist, create and add to map
        if (map.getStyle().getLayer(POINT_LAYER) == null) {
            Source pointSource = new GeoJsonSource(POINT_SOURCE, Feature.fromGeometry(Point.fromLngLat(center.getLongitude(), center.getLatitude())));
            map.getStyle().addSource(pointSource);
            Layer pointLayer = new SymbolLayer(POINT_LAYER, POINT_SOURCE)
                .withProperties(PropertyFactory.iconImage(CORNER_IMAGE));
            map.getStyle().addLayer(pointLayer);

            Source polygonSource = new GeoJsonSource(POLYGON_SOURCE, Feature.fromGeometry(Polygon.fromLngLats(coordinates)));
            map.getStyle().addSource(polygonSource);
            FillLayer polygonLayer = new FillLayer(POLYGON_LAYER, POLYGON_SOURCE)
                    .withProperties(PropertyFactory.fillColor(ContextCompat.getColor(context, R.color.colorAccent)), PropertyFactory.fillOpacity(0.5f));
            map.getStyle().addLayerBelow(polygonLayer, POINT_LAYER);

            Source polylineSource = new GeoJsonSource(POLYLINE_SOURCE, Feature.fromGeometry(LineString.fromLngLats(lineString)));
            map.getStyle().addSource(polylineSource);
            Layer polylineLayer = new LineLayer(POLYLINE_LAYER, POLYLINE_SOURCE)
                    .withProperties(PropertyFactory.lineColor(ContextCompat.getColor(context, R.color.colorPrimary)), PropertyFactory.lineOpacity(0.9f));
            map.getStyle().addLayerAbove(polylineLayer, POLYGON_LAYER);

        // otherwise, update source
        } else {
            GeoJsonSource polygonSource = map.getStyle().getSourceAs(POLYGON_SOURCE);
            polygonSource.setGeoJson(Feature.fromGeometry(Polygon.fromLngLats(coordinates)));

            FillLayer polygonFill = map.getStyle().getLayerAs(Container.POLYGON_LAYER);
            polygonFill.setProperties(PropertyFactory.fillColor(ContextCompat.getColor(context, R.color.colorAccent)));

            GeoJsonSource polylineSource = map.getStyle().getSourceAs(POLYLINE_SOURCE);
            polylineSource.setGeoJson(Feature.fromGeometry(LineString.fromLngLats(lineString)));
        }
    }

    public void move(LatLng center) {
        this.center = center;
        this.points = calculateCirclePoints(center, radius);

        List<Point> positions = latLngsToPositions(points);
        List<List<Point>> coordinates = new ArrayList<>();
        coordinates.add(positions);

        List<Point> lineString = new ArrayList<>(positions);
        lineString.add(positions.get(0));

        GeoJsonSource pointSource = map.getStyle().getSourceAs(POINT_SOURCE);
        pointSource.setGeoJson(Feature.fromGeometry(Point.fromLngLat(center.getLongitude(), center.getLatitude())));

        GeoJsonSource polygonSource = map.getStyle().getSourceAs(POLYGON_SOURCE);
        polygonSource.setGeoJson(Feature.fromGeometry(Polygon.fromLngLats(coordinates)));

        FillLayer polygonFill = map.getStyle().getLayerAs(Container.POLYGON_LAYER);
        polygonFill.setProperties(PropertyFactory.fillColor(ContextCompat.getColor(context, R.color.colorAccent)));

        GeoJsonSource polylineSource = map.getStyle().getSourceAs(POLYLINE_SOURCE);
        polylineSource.setGeoJson(Feature.fromGeometry(LineString.fromLngLats(lineString)));
    }

    @Override
    public LatLngBounds getLatLngBoundsForZoom() {
        Analytics.logDebug("center", center.toString());
        Analytics.logDebug("points", TextUtils.join(" - ", points));
        return new LatLngBounds.Builder().includes(points).build();
    }

    @Override
    public void clear() {
        center = null;
        points = null;

        map.getStyle().removeLayer(POINT_LAYER);
        map.getStyle().removeSource(POINT_SOURCE);

        map.getStyle().removeLayer(POLYGON_LAYER);
        map.getStyle().removeSource(POLYGON_SOURCE);

        map.getStyle().removeLayer(POLYLINE_LAYER);
        map.getStyle().removeSource(POLYLINE_SOURCE);
    }

    public boolean isValid() {
        return center != null && points != null;
    }

    public void setCenter(LatLng center) {
        this.center = center;
    }

    public LatLng getCenter() {
        return center;
    }

    public double getRadius() {
        return radius;
    }

    public List<LatLng> getPoints() {
        return points;
    }

    public static ArrayList<LatLng> calculateCirclePoints(LatLng location, double radius) {
        int degreesBetweenPoints = 8;
        int numberOfPoints = (int) Math.floor(360 / degreesBetweenPoints);
        double distRadians = radius / 6371000.0; // earth radius in meters
        double centerLatRadians = location.getLatitude() * Math.PI / 180;
        double centerLonRadians = location.getLongitude() * Math.PI / 180;
        ArrayList<LatLng> polygons = new ArrayList<>(); //array to hold all the path
        for (int index = 0; index < numberOfPoints; index++) {
            double degrees = index * degreesBetweenPoints;
            double degreeRadians = degrees * Math.PI / 180;
            double pointLatRadians = Math.asin(Math.sin(centerLatRadians) * Math.cos(distRadians) + Math.cos(centerLatRadians) * Math.sin(distRadians) * Math.cos(degreeRadians));
            double pointLonRadians = centerLonRadians + Math.atan2(Math.sin(degreeRadians) * Math.sin(distRadians) * Math.cos(centerLatRadians),
                    Math.cos(distRadians) - Math.sin(centerLatRadians) * Math.sin(pointLatRadians));
            double pointLat = pointLatRadians * 180 / Math.PI;
            double pointLon = pointLonRadians * 180 / Math.PI;
            polygons.add(new LatLng(pointLat, pointLon));
        }
        return polygons;
    }
}