package com.atmob.map_amap_android.amap;

import android.content.Context;

import androidx.annotation.NonNull;

import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapOptions;
import com.amap.api.maps.CameraUpdate;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.LatLngBounds;
import com.atmob.map_amap_android.bean.MapPadding;
import com.atmob.map_amap_android.contants.Constants;
import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
import com.atmob.map_amap_android.util.GsonUtil;
import com.atmob.map_amap_android.util.LogUtil;
import com.atmob.map_amap_android.util.ParamUtil;
import com.atmob.map_amap_android.util.SizeUtil;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.util.List;
import java.util.Map;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class MapController implements MyMethodCallHandler {

    private final String TAG = "MapController";

    private final AMap map;

    private final Context context;

    private final MethodChannel methodChannel;

    int nowMapTypeIndex = 0;

    private final Gson gson;

    private final int[] mapType = {
            AMap.MAP_TYPE_NORMAL,
            AMap.MAP_TYPE_SATELLITE,
            AMap.MAP_TYPE_NIGHT,
            AMap.MAP_TYPE_NAVI,
            AMap.MAP_TYPE_BUS,
            AMap.MAP_TYPE_NAVI_NIGHT,
    };

    public MapController(Context context, MethodChannel methodChannel, AMap map) {
        this.context = context;
        this.methodChannel = methodChannel;
        this.map = map;
        map.setMapType(mapType[nowMapTypeIndex]);
        gson = GsonUtil.getInstance();
    }


    @Override
    public void doMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        switch (call.method) {
            case Constants.METHOD_MOVE_CAMERA:
                moveToCamera(call, result);
                break;
            case Constants.METHOD_ANIMATE_CAMERA:
                animateCamera(call, result);
                break;
            case Constants.METHOD_MAP_CLEAR:
                clearMap(result);
                break;
            case Constants.METHOD_MAP_MOVE_TO_SUITABLE_LOCATION:
                animateSuitableLocation(call, result);
                break;
            case Constants.METHOD_MAP_INTERACTION_ENABLED:
                mapInteractionEnabled(call, result);
                break;
            case Constants.METHOD_MAP_LOGO_VISIBLE:
                mapLogoVisible(call, result);
                break;

        }
    }

    private void mapInteractionEnabled(MethodCall call, MethodChannel.Result result) {
        Map<String, Object> arguments = call.arguments();
        boolean isEnabled = ParamUtil.getBoolean(arguments, "enabled", true);
        if (map == null) {
            result.error("map is null", null, null);
            return;
        }
        map.getUiSettings().setAllGesturesEnabled(isEnabled);
        result.success(null);
    }

    private void mapLogoVisible(MethodCall call, MethodChannel.Result result) {
        Map<String, Object> arguments = call.arguments();
        boolean isVisible = ParamUtil.getBoolean(arguments, "visible", true);
        if (map == null) {
            result.error("map is null", null, null);
            return;
        }
        map.getUiSettings().setLogoBottomMargin(isVisible ? 0 : -100);
        result.success(null);
    }

    private void animateSuitableLocation(MethodCall call, MethodChannel.Result result) {
        try {
            Map<String, Object> arguments = call.arguments();
            if (arguments == null) {
                result.error("arguments is null", null, null);
                return;
            }
            boolean isAnimateCamera = ParamUtil.getBoolean(arguments, "isAnimateCamera", true);
            LogUtil.d(TAG, "isAnimateCamera===>" + isAnimateCamera);
            String points = (String) arguments.get("points");
            if (points == null || points.isEmpty()) {
                result.error("latLngList is null", null, null);
                return;
            }
            List<LatLng> latLngList = gson.fromJson(points, new TypeToken<List<LatLng>>() {
            }.getType());
            LogUtil.d(TAG, "latLngList===>" + latLngList);
            if (latLngList == null || latLngList.isEmpty()) {
                result.error("latLngList is empty", null, null);
                return;
            }
            String paddingStr = (String) arguments.get("mapPadding");

            MapPadding padding = null;
            if (paddingStr != null && !paddingStr.isEmpty()) {
                padding = gson.fromJson(paddingStr, MapPadding.class);
            }
            LogUtil.d(TAG, "padding===>" + padding);
            moveToSuitableLocation(latLngList, padding);
            result.success(null);
        } catch (Exception e) {
            LogUtil.d(TAG, "addPolyline error===>" + e.getMessage());
            result.error("JsonSyntaxException", e.getMessage(), null);
        }
    }

    private void moveToSuitableLocation(List<LatLng> points, MapPadding padding) {
        if (map == null) {
            return;
        }
        LatLngBounds.Builder builder = LatLngBounds.builder();
        for (int i = 0; i < points.size(); i++) {
            LatLng latLng = points.get(i);
            builder.include(latLng);
        }
        LatLngBounds bounds = builder.build();
        int left = 0;
        int top = 0;
        int right = 0;
        int bottom = 0;
        if (padding != null) {
            left = (int) SizeUtil.dp2px(context, padding.getLeft());
            top = (int) SizeUtil.dp2px(context, padding.getTop());
            right = (int) SizeUtil.dp2px(context, padding.getRight());
            bottom = (int) SizeUtil.dp2px(context, padding.getBottom());
        }
        LogUtil.d(TAG, "left===>" + left + "  top===>" + top + "  right===>" + right + "  bottom===>" + bottom);
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBoundsRect(bounds, left, right, top, bottom);
        map.animateCamera(cameraUpdate);
    }


    private void clearMap(MethodChannel.Result result) {
        LogUtil.i(TAG, "clearMap");
        map.clear();
        result.success(null);
    }

    private void animateCamera(MethodCall call, MethodChannel.Result result) {
        Map<String, Object> arguments = call.arguments();
        Double longitude = ParamUtil.getDouble(arguments, "longitude");
        Double latitude = ParamUtil.getDouble(arguments, "latitude");
        Integer zoom = ParamUtil.getInt(arguments, "zoom");
        if (longitude == null || latitude == null) {
            result.error("error", "longitude or latitude is null", null);
            return;
        }
        if (zoom == null) {
            zoom = 18;
        }
        map.animateCamera(CameraUpdateFactory.newLatLngZoom(new com.amap.api.maps.model.LatLng(latitude, longitude), zoom));
        LogUtil.i(TAG, "animateCamera===>success");
        result.success(null);
    }

    private void moveToCamera(MethodCall call, MethodChannel.Result result) {
        LogUtil.i(TAG, "moveToCamera===>" + call.arguments());
        Map<String, Object> arguments = call.arguments();
        Double longitude = ParamUtil.getDouble(arguments, "longitude");
        Double latitude = ParamUtil.getDouble(arguments, "latitude");
        Integer zoom = ParamUtil.getInt(arguments, "zoom");
        if (longitude == null || latitude == null) {
            result.error("error", "longitude or latitude is null", null);
            return;
        }
        if (zoom == null) {
            zoom = 18;
        }
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(new com.amap.api.maps.model.LatLng(latitude, longitude), zoom));
        LogUtil.i(TAG, "moveToCamera===>success");
        result.success(null);
    }

    @Override
    public String[] getRegisterMethodIdArray() {
        return Constants.METHOD_ID_LIST_FOR_MAP;
    }
}
