package com.casic.detector.cluster;

import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.TextView;

import androidx.collection.LruCache;

import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.LatLngBounds;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.animation.AlphaAnimation;
import com.casic.detector.R;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 整体设计采用了两个线程,一个线程用于计算组织聚合数据,一个线程负责处理Marker相关操作
 */
public class ClusterOverlay implements AMap.OnCameraChangeListener, AMap.OnMarkerClickListener {
    private final AMap amap;
    private final Context context;
    private final List<ClusterItem> clusterItems;
    private final List<Cluster> clusters;
    private final int clusterSize;
    private ClusterOnClickListener clusterClickListener;
    private ClusterRender clusterRender;
    private final CopyOnWriteArrayList<Marker> addMarkers = new CopyOnWriteArrayList<>();
    private double clusterDistance;
    private final LruCache<Integer, BitmapDescriptor> lruCache;
    private final HandlerThread markerHandlerThread = new HandlerThread("addMarker");
    private final HandlerThread signClusterThread = new HandlerThread("calculateCluster");
    private Handler markerHandler;
    private Handler signClusterHandler;
    private float pxInMeters;
    private boolean isCanceled = false;
    private final AlphaAnimation addAnimation = new AlphaAnimation(0, 1);

    /**
     * 构造函数,批量添加聚合元素时,调用此构造函数
     */
    public ClusterOverlay(Context context, AMap amap, int clusterSize) {
        this(context, amap, null, clusterSize);
    }

    /**
     * 构造函数,批量添加聚合元素时,调用此构造函数
     */
    public ClusterOverlay(Context context, AMap amap, List<ClusterItem> clusterItems, int clusterSize) {
        //默认最多会缓存80张图片作为聚合显示元素图片,根据自己显示需求和app使用内存情况,可以修改数量
        this.lruCache = new LruCache<Integer, BitmapDescriptor>(80) {
        };
        if (clusterItems != null) {
            this.clusterItems = clusterItems;
        } else {
            this.clusterItems = new ArrayList<>();
        }
        this.context = context;
        this.clusters = new ArrayList<>();
        this.amap = amap;
        this.clusterSize = clusterSize;
        this.pxInMeters = amap.getScalePerPixel();
        this.clusterDistance = pxInMeters * clusterSize;
        amap.setOnCameraChangeListener(this);
        amap.setOnMarkerClickListener(this);
        initThreadHandler();
        assignClusters();
    }

    /**
     * 设置聚合点的点击事件
     */
    public void setOnClusterClickListener(ClusterOnClickListener clusterClickListener) {
        this.clusterClickListener = clusterClickListener;
    }

    /**
     * 添加一个聚合点
     */
    public void addClusterItem(ClusterItem item) {
        Message message = Message.obtain();
        message.what = SignClusterHandler.CALCULATE_SINGLE_CLUSTER;
        message.obj = item;
        signClusterHandler.sendMessage(message);
    }

    /**
     * 设置聚合元素的渲染样式，不设置则默认为气泡加数字形式进行渲染
     */
    public void setClusterRender(ClusterRender render) {
        this.clusterRender = render;
    }

    public void onDestroy() {
        isCanceled = true;
        signClusterHandler.removeCallbacksAndMessages(null);
        markerHandler.removeCallbacksAndMessages(null);
        signClusterThread.quit();
        markerHandlerThread.quit();
        for (Marker marker : addMarkers) {
            marker.remove();
        }
        addMarkers.clear();
        lruCache.evictAll();
    }

    //初始化Handler
    private void initThreadHandler() {
        markerHandlerThread.start();
        signClusterThread.start();
        markerHandler = new MarkerHandler(markerHandlerThread.getLooper());
        signClusterHandler = new SignClusterHandler(signClusterThread.getLooper());
    }

    @Override
    public void onCameraChange(CameraPosition arg0) {

    }

    @Override
    public void onCameraChangeFinish(CameraPosition arg0) {
        pxInMeters = amap.getScalePerPixel();
        clusterDistance = pxInMeters * clusterSize;
        assignClusters();
    }

    @Override
    public boolean onMarkerClick(Marker arg0) {
        if (clusterClickListener == null) {
            return true;
        }
        Cluster cluster = (Cluster) arg0.getObject();
        if (cluster != null) {
            clusterClickListener.onClick(arg0, cluster.getClusterItems());
            return true;
        }
        return false;
    }


    /**
     * 将聚合元素添加至地图上
     */
    private void addClusterToMap(List<Cluster> clusters) {
        ArrayList<Marker> removeMarkers = new ArrayList<>(addMarkers);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
        RemoveAnimationListener removeAnimationListener = new RemoveAnimationListener(removeMarkers);
        for (Marker marker : removeMarkers) {
            marker.setAnimation(alphaAnimation);
            marker.setAnimationListener(removeAnimationListener);
            marker.startAnimation();
        }

        for (Cluster cluster : clusters) {
            addSingleClusterToMap(cluster);
        }
    }

    /**
     * 将单个聚合元素添加至地图显示
     */
    private void addSingleClusterToMap(Cluster cluster) {
        LatLng latlng = cluster.getCenterLatLng();
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.icon(getBitmapDes(cluster.getClusterCount())).position(latlng);
        Marker marker = amap.addMarker(markerOptions);
        marker.setAnimation(addAnimation);
        marker.setObject(cluster);

        marker.startAnimation();
        cluster.setMarker(marker);
        addMarkers.add(marker);
    }

    private void calculateClusters() {
        isCanceled = false;
        clusters.clear();
        LatLngBounds visibleBounds = amap.getProjection().getVisibleRegion().latLngBounds;
        for (ClusterItem clusterItem : clusterItems) {
            if (isCanceled) {
                return;
            }
            LatLng latlng = clusterItem.getPosition();
            if (visibleBounds.contains(latlng)) {
                Cluster cluster = getCluster(latlng, clusters);
                if (cluster == null) {
                    cluster = new Cluster(latlng);
                    clusters.add(cluster);
                }
                cluster.addClusterItem(clusterItem);
            }
        }

        //复制一份数据，规避同步
        List<Cluster> tempClusters = new ArrayList<>();
        tempClusters.addAll(clusters);
        Message message = Message.obtain();
        message.what = MarkerHandler.ADD_CLUSTER_LIST;
        message.obj = tempClusters;
        if (isCanceled) {
            return;
        }
        markerHandler.sendMessage(message);
    }

    /**
     * 对点进行聚合
     */
    private void assignClusters() {
        isCanceled = true;
        signClusterHandler.removeMessages(SignClusterHandler.CALCULATE_CLUSTER);
        signClusterHandler.sendEmptyMessage(SignClusterHandler.CALCULATE_CLUSTER);
    }

    /**
     * 在已有的聚合基础上，对添加的单个元素进行聚合
     */
    private void calculateSingleCluster(ClusterItem clusterItem) {
        LatLngBounds visibleBounds = amap.getProjection().getVisibleRegion().latLngBounds;
        LatLng latlng = clusterItem.getPosition();
        if (!visibleBounds.contains(latlng)) {
            return;
        }
        Cluster cluster = getCluster(latlng, clusters);
        if (cluster != null) {
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.UPDATE_SINGLE_CLUSTER;

            message.obj = cluster;
            markerHandler.removeMessages(MarkerHandler.UPDATE_SINGLE_CLUSTER);
            markerHandler.sendMessageDelayed(message, 5);
        } else {
            cluster = new Cluster(latlng);
            clusters.add(cluster);
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.ADD_SINGLE_CLUSTER;
            message.obj = cluster;
            markerHandler.sendMessage(message);
        }
    }

    /**
     * 根据一个点获取是否可以依附的聚合点，没有则返回null
     */
    private Cluster getCluster(LatLng latLng, List<Cluster> clusters) {
        for (Cluster cluster : clusters) {
            LatLng clusterCenterPoint = cluster.getCenterLatLng();
            double distance = AMapUtils.calculateLineDistance(latLng, clusterCenterPoint);
            if (distance < clusterDistance && amap.getCameraPosition().zoom < 19) {
                return cluster;
            }
        }
        return null;
    }


    /**
     * 获取每个聚合点的绘制样式
     */
    private BitmapDescriptor getBitmapDes(int num) {
        BitmapDescriptor bitmapDescriptor = lruCache.get(num);
        if (bitmapDescriptor == null) {
            TextView textView = new TextView(context);
            if (num > 1) {
                String tile = String.valueOf(num);
                textView.setText(tile);
            }
            textView.setGravity(Gravity.CENTER);
            textView.setTextColor(Color.WHITE);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
            if (clusterRender != null && clusterRender.getDrawableByCount(num) != null) {
                textView.setBackgroundDrawable(clusterRender.getDrawableByCount(num));
            } else {
                textView.setBackgroundResource(R.mipmap.defaultcluster);
            }
            bitmapDescriptor = BitmapDescriptorFactory.fromView(textView);
            lruCache.put(num, bitmapDescriptor);

        }
        return bitmapDescriptor;
    }

    /**
     * 更新已加入地图聚合点的样式
     */
    private void updateCluster(Cluster cluster) {
        Marker marker = cluster.getMarker();
        marker.setIcon(getBitmapDes(cluster.getClusterCount()));
    }

    /**
     * 处理market添加，更新等操作
     */
    class MarkerHandler extends Handler {

        static final int ADD_CLUSTER_LIST = 0;

        static final int ADD_SINGLE_CLUSTER = 1;

        static final int UPDATE_SINGLE_CLUSTER = 2;

        public MarkerHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {

            switch (message.what) {
                case ADD_CLUSTER_LIST:
                    List<Cluster> clusters = (List<Cluster>) message.obj;
                    addClusterToMap(clusters);
                    break;
                case ADD_SINGLE_CLUSTER:
                    Cluster cluster = (Cluster) message.obj;
                    addSingleClusterToMap(cluster);
                    break;
                case UPDATE_SINGLE_CLUSTER:
                    Cluster updateCluster = (Cluster) message.obj;
                    updateCluster(updateCluster);
                    break;
            }
        }
    }

    /**
     * 处理聚合点算法线程
     */
    class SignClusterHandler extends Handler {
        static final int CALCULATE_CLUSTER = 0;
        static final int CALCULATE_SINGLE_CLUSTER = 1;

        public SignClusterHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {
            switch (message.what) {
                case CALCULATE_CLUSTER:
                    calculateClusters();
                    break;
                case CALCULATE_SINGLE_CLUSTER:
                    ClusterItem item = (ClusterItem) message.obj;
                    clusterItems.add(item);
                    calculateSingleCluster(item);
                    break;
            }
        }
    }
}
