package com.atmob.map_amap_android.amap;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;

import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.MapView;
import com.amap.api.maps.TextureMapView;
import com.atmob.map_amap_android.contants.Constants;
import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
import com.atmob.map_amap_android.overlays.marker.MarkersController;
import com.atmob.map_amap_android.overlays.polyline.PolylineController;
import com.atmob.map_amap_android.util.LogUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.platform.PlatformView;

public class AmapView implements PlatformView, DefaultLifecycleObserver, MethodChannel.MethodCallHandler, ActivityPluginBinding.OnSaveInstanceStateListener {

    private final String TAG = "AmapView";

    private static final int DEFAULT_MAP_ZOOM_SCALE = 18;

    private final MethodChannel channel;

    private TextureMapView mapView = null;

    private boolean disposed = false;

    private final Map<String, MyMethodCallHandler> myMethodCallHandlerMap;

    private MapController mapController;

    private MarkersController markersController;

    private PolylineController polylineController;

    private final int viewId;

    private Bundle bundle;

    public AmapView(Context context, Activity activity, BinaryMessenger messenger, int viewId, Map<String, Object> args, LifecycleOwner lifecycleProvider) {
        this.viewId = viewId;
        channel = new MethodChannel(messenger, Constants.MAP_VIEW_CHANNEL_NAME + viewId);
        channel.setMethodCallHandler(this);
        myMethodCallHandlerMap = new HashMap<>(8);

        LogUtil.i(TAG, "AmapView() ==> viewId: " + viewId + ", hash: " + this.hashCode());

        try {
            mapView = new TextureMapView(context);
            mapView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                @Override
                public void onViewAttachedToWindow(@NonNull View v) {
                    LogUtil.i(TAG, "onViewAttachedToWindow() ==> viewId: " + viewId + ", disposed: " + disposed);
                    mapView.postInvalidate();
                    mapView.getParent().requestLayout();
                }

                @Override
                public void onViewDetachedFromWindow(@NonNull View v) {
                    LogUtil.i(TAG, "onViewDetachedFromWindow() ==> viewId: " + viewId + ", disposed: " + disposed);
                }
            });
            AMap amap = mapView.getMap();
            initMapDefaultSetting(amap);
            mapController = new MapController(context, channel, amap);
            markersController = new MarkersController(context, channel, amap);
            polylineController = new PolylineController(context, channel, amap);

            initMyMethodCallHandlerMap();

            lifecycleProvider.getLifecycle().addObserver(this);
        } catch (Exception e) {
            LogUtil.e(TAG, "<init>", e);
        }
    }

    private void initMyMethodCallHandlerMap() {
        String[] methodIdArray = markersController.getRegisterMethodIdArray();
        if (null != methodIdArray) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, markersController);
            }
        }

        methodIdArray = mapController.getRegisterMethodIdArray();
        if (null != methodIdArray) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, mapController);
            }
        }

        methodIdArray = polylineController.getRegisterMethodIdArray();
        if (null != methodIdArray) {
            for (String methodId : methodIdArray) {
                myMethodCallHandlerMap.put(methodId, polylineController);
            }
        }
    }

    private void initMapDefaultSetting(AMap map) {
        if (map == null) {
            return;
        }
        //目前直接设置
        map.getUiSettings().setZoomControlsEnabled(false);
        map.moveCamera(CameraUpdateFactory.zoomTo(DEFAULT_MAP_ZOOM_SCALE));
    }

    @Nullable
    @Override
    public View getView() {
        LogUtil.i(TAG, "getView() ==> viewId: " + viewId + ", disposed: " + disposed);
        return mapView;
    }

    @Override
    public void dispose() {
        LogUtil.i(TAG, "dispose() ==> viewId: " + viewId + ", disposed: " + disposed);
        try {
            if (disposed) {
                return;
            }
            channel.setMethodCallHandler(null);
            destroyMapViewIfNecessary();
            disposed = true;
        } catch (Throwable e) {
            LogUtil.e(TAG, "dispose", e);
        }
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        LogUtil.i(TAG, "onMethodCall==>" + call.method + ", arguments==> " + call.arguments);
        String methodId = call.method;
        if (myMethodCallHandlerMap.containsKey(methodId)) {
            try {
                Objects.requireNonNull(myMethodCallHandlerMap.get(methodId)).doMethodCall(call, result);
            } catch (Exception e) {
                LogUtil.e(TAG, "onMethodCall", e);
                result.error("Exception", e.getMessage(), e.getStackTrace());
            }
        } else {
            LogUtil.w(TAG, "onMethodCall, the methodId: " + call.method + ", not implemented");
            result.notImplemented();
        }

    }

    @Override
    public void onCreate(@NonNull LifecycleOwner owner) {
        LogUtil.i(TAG, "onCreate() ==> viewId: " + viewId + ", disposed: " + disposed);
        try {
            if (disposed) {
                return;
            }
            if (null != mapView) {
                mapView.onCreate(bundle);
            }
        } catch (Throwable e) {
            LogUtil.e(TAG, "onCreate", e);
        }
    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        LogUtil.i(TAG, "onStart() ==> viewId: " + viewId + ", disposed: " + disposed);
    }

    @Override
    public void onResume(@NonNull LifecycleOwner owner) {
        LogUtil.i(TAG, "onResume() ==> viewId: " + viewId + ", disposed: " + disposed);
        try {
            if (disposed) {
                return;
            }
            if (null != mapView) {
                mapView.onResume();
            }
        } catch (Throwable e) {
            LogUtil.e(TAG, "onResume", e);
        }
    }

    @Override
    public void onPause(@NonNull LifecycleOwner owner) {
        LogUtil.i(TAG, "onPause() ==> viewId: " + viewId + ", disposed: " + disposed);
        try {
            if (disposed) {
                return;
            }
            mapView.onPause();
        } catch (Throwable e) {
            LogUtil.e(TAG, "onPause", e);
        }
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        LogUtil.i(TAG, "onStop() ==> viewId: " + viewId + ", disposed: " + disposed);
    }

    @Override
    public void onDestroy(@NonNull LifecycleOwner owner) {
        LogUtil.i(TAG, "onDestroy() ==> viewId: " + viewId + ", disposed: " + disposed);
        try {
            if (disposed) {
                return;
            }
            destroyMapViewIfNecessary();
        } catch (Throwable e) {
            LogUtil.e(TAG, "onDestroy", e);
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle bundle) {
        LogUtil.i(TAG, "onSaveInstanceState() ==> viewId: " + viewId + ", disposed: " + disposed);
        try {
            if (disposed) {
                return;
            }
            mapView.onSaveInstanceState(bundle);
        } catch (Throwable e) {
            LogUtil.e(TAG, "onSaveInstanceState", e);
        }
    }

    @Override
    public void onRestoreInstanceState(@Nullable Bundle bundle) {
        this.bundle = bundle;
        LogUtil.i(TAG, "onRestoreInstanceState() ==> viewId: " + viewId + ", disposed: " + disposed);
    }

    private void destroyMapViewIfNecessary() {
        if (mapView == null) {
            return;
        }
        mapView.onDestroy();
    }
}
