package com.tofly.ghrq_route.common.ui.activity;

import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcB;
import android.nfc.tech.NfcF;
import android.nfc.tech.NfcV;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;

import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.geometry.Geometry;
import com.esri.arcgisruntime.geometry.GeometryEngine;
import com.esri.arcgisruntime.geometry.LineSegment;
import com.esri.arcgisruntime.geometry.Part;
import com.esri.arcgisruntime.geometry.PartCollection;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.PointCollection;
import com.esri.arcgisruntime.geometry.Polygon;
import com.esri.arcgisruntime.geometry.Polyline;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.Graphic;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.IdentifyGraphicsOverlayResult;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.esri.arcgisruntime.symbology.MarkerSymbol;
import com.esri.arcgisruntime.symbology.PictureMarkerSymbol;
import com.esri.arcgisruntime.symbology.SimpleLineSymbol;
import com.esri.arcgisruntime.symbology.Symbol;
import com.esri.arcgisruntime.util.ListenableList;
import com.google.gson.JsonParseException;
import com.mgq.base.utils.progressdialog.ProgressManager;
import com.mgq.base.utils.string.StringUtil;
import com.mgq.base.utils.toast.ToastUtil;
import com.tofly.ghrq_route.R;
import com.tofly.ghrq_route.base.BaseApp;
import com.tofly.ghrq_route.base.ui.BaseMapFileActivity;
import com.tofly.ghrq_route.base.C;
import com.tofly.ghrq_route.common.ui.dialog.DangerDetailBottomDialog;
import com.tofly.ghrq_route.common.ui.dialog.PlanApplyRecalculateDialog;
import com.tofly.ghrq_route.common.ui.dialog.PlanPointDetailBottomDialog;
import com.tofly.ghrq_route.common.ui.dialog.ReadNFCDeviceDialog;
import com.tofly.ghrq_route.common.ui.dialog.RepairDetailBottomDialog;
import com.tofly.ghrq_route.common.ui.view.BottomChildPlanDetail;
import com.tofly.ghrq_route.common.ui.view.MyAudioRecordButton;
import com.tofly.ghrq_route.module.OfflineWork.domain.OfflineWorkEvent;
import com.tofly.ghrq_route.module.deviceInfo.DeviceInfoTool;
import com.tofly.ghrq_route.module.gps.GpsTool;
import com.tofly.ghrq_route.module.plan.PlanTool;
import com.tofly.ghrq_route.module.plan.controller.PlanCallBack;
import com.tofly.ghrq_route.module.plan.domain.OfflinePathBean;
import com.tofly.ghrq_route.module.plan.domain.PlanBean;
import com.tofly.ghrq_route.module.plan.domain.PlanBean.PlanDeviceType;
import com.tofly.ghrq_route.module.planPointRepair.domain.PointRepairEvent;
import com.tofly.ghrq_route.module.sign.domain.SignDataBean;
import com.tofly.ghrq_route.module.user.domain.UserBean;
import com.tofly.ghrq_route.common.util.ArcgisUtils.ArcgisUtils;
import com.tofly.ghrq_route.common.util.ArcgisUtils.SizeMarkerSymbol;
import com.tofly.ghrq_route.common.util.dialog.DialogUtil;
import com.tofly.latte_core.base.IPresenter;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import butterknife.BindView;

/**
 * Created by Amu on 2017/7/6.
 */
public class UiPlanDetail extends BaseMapFileActivity {
    @BindView(R.id.mapview)
    MapView mapView;
    @BindView(R.id.my_location)
    ImageView imageMyLocation;
    @BindView(R.id.checktext_line_all)
    CheckBox checkBoxLineAll;
    @BindView(R.id.layout_bottom_plan_detail)
    BottomChildPlanDetail bottomChildPlanDetail;

    public static final String TAG = UiPlanDetail.class.getName();
    private PlanTool planTool;
    private PlanBean mPlan = null;
    private String mPlanId = null;
    private List<GraphicsOverlay> childLineLayers = new ArrayList<>();
    private GraphicsOverlay pointLayer;
    private GraphicsOverlay lineLayer;
    private GraphicsOverlay serviceLocationLayer = null;
    private Graphic locationGraphic = null;
    private Graphic locCircleGraphic = null;
    private MarkerSymbol locationSymbol = null;
    private MarkerSymbol locationCarSymbol = null;
    private SimpleLineSymbol locCircleSymbol = null;
    private List<OfflinePathBean> mPlanPointList = null;
    private List<OfflinePathBean> mLocalPlanPointList = null;

    private Point mLastLocation = null;

    private String mSelectPointId = null;

    private int mDeviceTypeId = 0;

    private boolean isNeedSetExtent = true;

    private boolean isLineRenderAll = true;

    private GraphicsOverlay lineOverLayer = null;
    private GraphicsOverlay lineNotOverLayer = null;

    private int mDownPointTimes = 0;

    private MenuItem menuItemRecalculate;

    private boolean isUpdatingRecalculateState = false;

    /*NFC模块*/
    private MenuItem menuItemNFC;

    private ReadNFCDeviceDialog nfcDeviceDialog;

    private NfcAdapter nfcAdapter;

    private PendingIntent pendingIntent;

    private IntentFilter[] mFilters;

    private String[][] mTechLists;

    private boolean mIsNFCUsable = false;

    private boolean mIsDeviceUseNFC = false;

    private MarkerSymbol unsignedSymbol;
    private MarkerSymbol signedUndoneSymbol;
    private MarkerSymbol signedDoneSymbol;
    private MarkerSymbol unsignedNfcSymbol;
    private MarkerSymbol undoneNfcSymbol;
    private MarkerSymbol doneNfcSymbol;

    private int overColor;
    private int notOverColor;
    private final float overLineWidth = 8.0f;
    private final float notOverLineWidth = 5.0f;

    @Override
    public void onBaseMapLoaded() {
        runOnUiThread(() -> {
            serviceLocationLayer = null;
            /*加载计划详情数据*/
            loadPlanDataAndLayer();
        });
    }

    @Subscribe
    public void onMessageEvent(UiPlan.PlanEvent event) {
        switch (event.message) {
            case UiPlan.PLAN_POINT_OVER:
                Map<String, List<OfflinePathBean>> overMaps = (Map<String, List<OfflinePathBean>>) event.arg0;
                if (overMaps != null && mPlan != null) {
                    String childPlanId = mPlan.getPlan_id();
                    if (overMaps.containsKey(childPlanId)) {
                        if (mPlan != null) {
                            /*更新计划完成率*/
                            getChildPlanInfo();
                            /*使用本地离线点来渲染*/
                            renderPageWithOfflinePoint(true);
                        }
                    }
                }
                break;
            case UiPlan.PLAN_SERVICE_LOCATION:
                Point point = (Point) event.arg0;
                if (point != null) {
                    mLocation = point;
                    /*更新计划完成率*/
                    getChildPlanInfo();

                    MarkerSymbol symbol = mPlan.getPlan_mode() == UserBean.PATROL_TYPE_CAR ?
                            locationCarSymbol : locationSymbol;
                    if (serviceLocationLayer == null) {
                        locationGraphic = new Graphic(point, symbol);
                        GraphicsOverlay pointLayer = new GraphicsOverlay();
                        pointLayer.getGraphics().add(locationGraphic);
                        Polygon polygon = getLocationCircleGeometry(point);
                        if (polygon != null) {
                            locCircleGraphic = new Graphic(polygon, locCircleSymbol);
                            pointLayer.getGraphics().add(locCircleGraphic);
                        }
                        mapView.getGraphicsOverlays().add(pointLayer);
                        serviceLocationLayer = pointLayer;
                    } else {
                        ListenableList<Graphic> graphics = serviceLocationLayer.getGraphics();
                        for (Graphic graphic : graphics) {
                            graphic.setGeometry(point);
                        }
                        if (locationGraphic != null) {
                            serviceLocationLayer.getGraphics().remove(locationGraphic);
                            locationGraphic = new Graphic(point, symbol);
                            serviceLocationLayer.getGraphics().add(locationGraphic);
                        }
                        if (locCircleGraphic != null) {
                            serviceLocationLayer.getGraphics().remove(locCircleGraphic);
                            Polygon polygon = getLocationCircleGeometry(point);
                            if (polygon != null) {
                                locCircleGraphic = new Graphic(polygon, locCircleSymbol);
                                serviceLocationLayer.getGraphics().add(locCircleGraphic);
                            }
                        }
                    }

                    updateLocationLine(point);
                }
                break;
            case UiPlan.PLAN_POINT_DOWNLOAD_STARTED:
                String strUploading = getResources().getString(R.string.updating_plan_point_data);
                ProgressManager.getInstance().showProgress(this, null, strUploading, false);
                break;
            case UiPlan.PLAN_POINT_DOWNLOAD_FINISHED:
                boolean isSuccess = (boolean) event.arg0;
                ProgressManager.getInstance().cancelProgress();
                String msg = "获取当天的巡检计划：" + (isSuccess ? "成功" : "失败");
                ToastUtil.showToast(this, msg);
                if (mPlan != null) {
                    renderPageWithOfflinePoint(false);
                }
                break;
        }
    }

    private void updateLocationLine(Point newLoc) {
        boolean isOutOfDate = isOutOfDate();
        if (isOutOfDate)
            return;

        if (mPlan != null && mPlan.getType() == OfflinePathBean.PATH_TYPE_LINE) {
            if (newLoc == null)
                return;

            if (mLastLocation == null) {
                mLastLocation = newLoc;
                return;
            }

            Point point1 = new Point(mLastLocation.getX(), mLastLocation.getY(), ArcgisUtils.getWgs84Spatial());
            Point point2 = new Point(newLoc.getX(), newLoc.getY(), ArcgisUtils.getWgs84Spatial());
            double distance = ArcgisUtils.getTwoPointDistance1(point1, point2);
            if (distance >= 2) {
                mLastLocation = newLoc;
                isNeedSetExtent = true;
                renderPageWithOfflinePoint(true);
            }
        }
    }

    private Polygon getLocationCircleGeometry(Point point) {
        int radius = mPlan.getPlan_mode() == 0 ? BaseApp.getInstance().getPointCheckRadius():
                BaseApp.getInstance().getPointCheckRadiusCar();
        if (mPlan.isDeviceType()) {
            radius = BaseApp.getInstance().getPointCheckRadiusDevice();
        }
        double tolerance = mPlan.getTolerance();
        if (!Double.isNaN(tolerance) && tolerance > 0) {
            radius = (int) tolerance;
        }
        return ArcgisUtils.getLocationCircleGeometry(point, radius, mapView.getSpatialReference());
    }

    @Subscribe
    public void onMessageEvent(UiReportedClear.ReportClearMessageEvent messageEvent){
        if ( messageEvent.message.equals(UiReportedClear.REPORT_ROUTE_UPLOADED)||
                messageEvent.message.equals(UiReportedClear.REPORT_ROUTE_SAVED) ||
                messageEvent.message.equals(UiReportedClear.REPORT_CLEAR_UPLOADED) ||
                messageEvent.message.equals(UiReportedClear.REPORT_CLEAR_SAVED)){
            if (mPlan != null) {
                /*更新计划完成率*/
                getChildPlanInfo();
                /*更新计划的点*/
                getChildPlanPointList();
            }
        }
    }

    @Subscribe
    public void onMessageEvent(PointRepairEvent event){
        if (event.message.equals(PointRepairEvent.POINT_REPAIR_REMOVED) ||
                event.message.equals(PointRepairEvent.REPAIR_ROUTE_SAVED) ||
                event.message.equals(PointRepairEvent.REPAIR_ROUTE_UPLOADED)){
            if (mPlan != null) {
                /*更新计划完成率*/
                getChildPlanInfo();
                /*更新计划的点*/
                getChildPlanPointList();
            }
        }
    }

    @Override
    public void initView() {
        super.initView();

        initRenderSymbol();

        //测试用，暂时设置每次详情页默认显示未完成的点数据
        //BaseApp.getInstance().setPlanLineRenderAll(false);

        //测试用，暂时默认详情页关闭管线数据
        //UiMainLayerManagerDailog.setIsPipeLayerOn(false);
    }

    private void initRenderSymbol() {
        Drawable unsignedLoc = getResources().getDrawable(R.mipmap.danger_location);
        unsignedSymbol = SizeMarkerSymbol.sizeSymbol((BitmapDrawable)unsignedLoc, 18, 18);
        Drawable signedUndoneLoc = getResources().getDrawable(R.mipmap.location_edit);
        signedUndoneSymbol = SizeMarkerSymbol.sizeSymbol((BitmapDrawable) signedUndoneLoc, 18, 18);
        Drawable signedDoneLoc = getResources().getDrawable(R.mipmap.location_signed);
        signedDoneSymbol = SizeMarkerSymbol.sizeSymbol((BitmapDrawable) signedDoneLoc, 18, 18);
        Drawable unsignedNfc = getResources().getDrawable(R.mipmap.rfid_unsigned);
        unsignedNfcSymbol = SizeMarkerSymbol.sizeSymbol((BitmapDrawable) unsignedNfc, 18, 18);
        Drawable undoneNfc = getResources().getDrawable(R.mipmap.rfid_undone);
        undoneNfcSymbol = SizeMarkerSymbol.sizeSymbol((BitmapDrawable) undoneNfc, 18, 18);
        Drawable doneNfc = getResources().getDrawable(R.mipmap.rfid_done);
        doneNfcSymbol = SizeMarkerSymbol.sizeSymbol((BitmapDrawable) doneNfc, 18, 18);

        overColor = getResources().getColor(R.color.colorPrimary);
        notOverColor = getResources().getColor(R.color.colorAccent);

        BitmapDrawable serviceLoc = (BitmapDrawable)getResources().getDrawable(R.drawable.ic_people_purple_400_24dp);
        locationSymbol = SizeMarkerSymbol.sizeSymbol(serviceLoc, 24, 24);
        BitmapDrawable serviceCarLoc = (BitmapDrawable)getResources().getDrawable(R.mipmap.profile_car_mode);
        locationCarSymbol = SizeMarkerSymbol.sizeSymbol(serviceCarLoc, 24, 24);

        int lineColor = getResources().getColor(R.color.colorPrimary);
        locCircleSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, lineColor, 2.0f);
    }

    @Override
    public void initNet() {
        super.initNet();

        Intent intent = this.getIntent();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            if (bundle.containsKey("plan")) {
                mPlan = (PlanBean) bundle.getSerializable("plan");
                if (mPlan != null)
                    mPlanId = mPlan.getPlan_id();
            } else if (bundle.containsKey("plan_id")) {
                mPlanId = bundle.getString("plan_id");
                mPlan = planTool.getLocalPlan(mPlanId);
            } else
                return;
        } else
            return;

        planTool = PlanTool.getInstance();

        if (C.bPlanDetailNFCModual_debug) {
            initNFCModule();
        }

        /*加载计划详情数据*/
        //loadPlanDataAndLayer();
        initListener();
    }

    private void initNFCModule(){
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);

        mIsNFCUsable = ifNFCUsable();

        pendingIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

        IntentFilter tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
        try{
            tech.addDataType("*/*");
        } catch (IntentFilter.MalformedMimeTypeException e) {
            e.printStackTrace();
        }

        mFilters = new IntentFilter[] {tech};

        mTechLists = new String[][]{
                new String[] {MifareClassic.class.getName()},
                new String[] {NfcA.class.getName()},
                new String[] {NfcB.class.getName()},
                new String[] {NfcF.class.getName()},
                new String[] {NfcV.class.getName()}
        };
    }

    private boolean ifNFCUsable(){
        if (nfcAdapter == null){
            //textNfc.setText("设备不支持NFC！");
            return false;
        }

        return nfcAdapter.isEnabled();
    }

    private void loadPlanDataAndLayer(){
        if (mPlan != null) {
            mDeviceTypeId = mPlan.getDevice_type_id();
            getChildPlanList();
        }else if (mPlanId != null){
            getPlanInfo(mPlanId);
        }
    }

    private void initListener() {
        mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mapView) {
            @Override
            public boolean onSingleTapConfirmed(MotionEvent event) {
                onMapTaped(Math.round(event.getX()), Math.round(event.getY()));
                return true;
            }
        });


        checkBoxLineAll.setOnCheckedChangeListener((buttonView, isChecked) -> {
            isLineRenderAll = isChecked;
            BaseApp.getInstance().setPlanLineRenderAll(isChecked);
            if (mPlan != null) {
                renderPageWithOfflinePoint(false);
            }
        });
    }

    private void onMapTaped(int x, int y) {
        if(mPlan == null)
            return;
        int type = mPlan.getType();
        /*当前巡检计划为巡检点*/
        if (type == 0){
            identifyPointGraphic(x, y);
        }else if (type == 1){
            for (GraphicsOverlay graphicLayer : childLineLayers){
                identifyLineGraphic(graphicLayer, type, x, y);
            }
        }
    }

    private void identifyPointGraphic(int x, int y) {
        if (pointLayer == null)
            return;

        pointLayer.clearSelection();
        final android.graphics.Point screenPoint = new android.graphics.Point(x, y);
        final ListenableFuture<IdentifyGraphicsOverlayResult> query =
                mapView.identifyGraphicsOverlayAsync(pointLayer, screenPoint, 10, false, 10);
        query.addDoneListener(() -> {
            try {
                IdentifyGraphicsOverlayResult result = query.get();
                List<Graphic> graphics = result.getGraphics();
                if (graphics != null && graphics.size() > 0) {
                    showTapedDevices(graphics);
                }
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    private OfflinePathBean getPathBeanFromGraphic(Graphic graphic) {
        OfflinePathBean pathBean = null;
        if (graphic != null) {
            Symbol graphicSymbol = graphic.getSymbol();
            if (graphicSymbol instanceof PictureMarkerSymbol) {
                Map<String, Object> attributes = graphic.getAttributes();
                if (attributes != null && attributes.size() > 0) {
                    pathBean = OfflinePathBean.getPathBeanFromAttrs(attributes);
                }
            }
        }
        return pathBean;
    }

    private void showTapedDevices(List<Graphic> graphics) {
        pointLayer.clearSelection();
        List<String> nameList = new ArrayList<>();
        for (int i = 0; i < graphics.size(); i++) {
            Graphic graphic =  graphics.get(i);
            OfflinePathBean pathBean = getPathBeanFromGraphic(graphic);
            if (pathBean != null) {
                String code = pathBean.getCode();
                if (code == null || code.isEmpty()) {
                    code = pathBean.getPoint_id();
                }
                if (code == null || code.isEmpty()) {
                    code = pathBean.getTemp_id();
                }
                nameList.add(code);
            }else {
                String name = String.format("Point %d", i + 1);
                nameList.add(name);
            }
        }
        if (!nameList.isEmpty()) {
            String[] names = nameList.toArray(new String[nameList.size()]);
            new AlertDialog.Builder(UiPlanDetail.this).setSingleChoiceItems
                    (names, -1, (dialog, which) -> {
                        Graphic graphic = graphics.get(which);
                        if (graphic != null) {
                            graphic.setSelected(true);
                            OfflinePathBean pathBean = getPathBeanFromGraphic(graphic);
                            Geometry geometry = graphic.getGeometry();
                            if (pathBean != null) {
                                showTapedDevice(pathBean, geometry);
                            }else {
                                showTapedDevice(geometry);
                            }
                        }
                        dialog.dismiss();
                    }).show();
        }
    }

    private void showTapedDevice(OfflinePathBean pathBean, Geometry geometry) {
        if (pathBean == null || pathBean.getTemp_id() == null)
            return;

        OfflinePathBean planPoint = planTool.getLocalPlanPoint(pathBean.getTemp_id());
        if (planPoint != null) {
            mSelectPointId = planPoint.getTemp_id();
            if (geometry != null) {
                Point point = (Point) geometry;
                Point toPoint = new Point(point.getX(), point.getY(), mapView.getSpatialReference());
                if (mDeviceTypeId != PlanDeviceType.PLAN_DEVICE_TYPE_XJD) {
                    /*巡检点为设备*/
                    showRepairBottomLayout(planPoint, toPoint);
                } else { /*巡检点*/
                    showNavigationCallout(toPoint);
                }
            }
        }
    }

    private void showTapedDevice(Geometry geometry) {
        if (geometry instanceof Point) {
            Point point = (Point) geometry;
            Point toPoint = new Point(point.getX(), point.getY(), mapView.getSpatialReference());
            showNavigationCallout(toPoint);
        }
    }

    private void identifyLineGraphic(GraphicsOverlay graphicLayer, final int type, int x, int y) {
        final android.graphics.Point screenPoint = new android.graphics.Point(x, y);
        graphicLayer.clearSelection();
        final ListenableFuture<IdentifyGraphicsOverlayResult> query =
                mapView.identifyGraphicsOverlayAsync(graphicLayer, screenPoint,
                        10, false, 10);
        query.addDoneListener(() -> {
            try {
                IdentifyGraphicsOverlayResult result = query.get();
                List<Graphic> selects = new ArrayList<>();
                List<Graphic> graphics = result.getGraphics();
                if (graphics != null && graphics.size() > 0) {
                    Graphic graphic = graphics.get(0);
                    selects.add(graphic);
                    result.getGraphicsOverlay().selectGraphics(selects);
                    Geometry geometry = graphic.getGeometry();
                    if (geometry instanceof Point || geometry instanceof Polyline) {
                        Point toPoint = mapView.screenToLocation(screenPoint);
                        showNavigationCallout(toPoint);
                    }
                }
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    @Override
    public void onBackPressed() {
        EventBus.getDefault().post(new UiPlan.PlanEvent(UiPlan.PLAN_NEED_REFRESH, null));
        finish();
    }

    private void clearAllLayers() {
        if (pointLayer != null) {
            pointLayer.getGraphics().clear();
            mapView.getGraphicsOverlays().remove(pointLayer);
        }
        pointLayer = null;

        if (childLineLayers.size() > 0) {
            for (GraphicsOverlay graphicsLayer : childLineLayers) {
                mapView.getGraphicsOverlays().remove(graphicsLayer);
            }
            childLineLayers.clear();
        }
    }

    private void showViewDetailClicked() {
        if (mPlan != null) {
            bottomChildPlanDetail.initChildPlanDetail(mPlan);
            bottomChildPlanDetail.setDetailClickListener(() -> {
                /*如果是巡检设备，显示多个设备的属性*/
                if (mPlan.isDeviceType()){
                    showPlanPointRepairBottomLayout();
                }else {
                    final int type = mPlan.getType();
                    if (type == OfflinePathBean.PATH_TYPE_LINE){
                        Intent intent = new Intent();
                        Bundle bundle = new Bundle();
                        bundle.putSerializable("plan", mPlan);
                        intent.setClass(UiPlanDetail.this, UiPlanMapPoint.class);
                        intent.putExtras(bundle);
                        startActivity(intent);
                    }else {
                        isNeedSetExtent = true;
                        renderPageWithOfflinePoint(false);
                    }
                }
            });
        }
    }

    private void showDangerBottomLayout(OfflinePathBean planPoint, final Point toPoint) {
        String troubleId = planPoint.getTrouble_id();
        DangerDetailBottomDialog detailBottomDialog = new DangerDetailBottomDialog(this, troubleId, planPoint, mPlan, mLocation);
        boolean isValid = checkPlanValid(planPoint);
        detailBottomDialog.setDangerClearEnable(isValid);
        detailBottomDialog.show();
        detailBottomDialog.setOnRouteClickListener(v -> showNavigation(toPoint));
    }

    private void showRepairBottomLayout(OfflinePathBean planPoint, final Point toPoint) {
        RepairDetailBottomDialog detailBottomDialog = new RepairDetailBottomDialog(this, planPoint, mPlan, mLocation);
        boolean isValid = checkPlanValid(planPoint);
        detailBottomDialog.setPlanEditable(isValid);
        detailBottomDialog.show();
        detailBottomDialog.setOnRouteClickListener(v -> showNavigation(toPoint));
    }

    private void showPlanPointRepairBottomLayout(){
        final List<OfflinePathBean> planLineList = planTool.getLocalPlanPoints(mPlanId);
        mLocalPlanPointList = planLineList;
        if (mLocalPlanPointList != null && mPlan != null) {
            PlanPointDetailBottomDialog detailBottomDialog = new PlanPointDetailBottomDialog(this, mLocalPlanPointList, mSelectPointId, mPlan, mLocation);
            detailBottomDialog.show();
            detailBottomDialog.setOnRouteClickListener((view, planPoint) -> {
                if (planPoint != null) {
                    double dLng = planPoint.getLng();
                    double dLat = planPoint.getLat();
                    showNavigation(new Point(dLng, dLat));
                }
            });

            detailBottomDialog.setOnItemClickListener((view, planPoint) -> {
                centerToPointAndHighlight(planPoint);
                Point toPoint = new Point(planPoint.getLng(), planPoint.getLat());
                /*if (mPlan.isDangerDeviceType()){
                    showDangerBottomLayout(planPoint, toPoint);
                }else*/ if (mPlan.isDeviceType()){
                    showRepairBottomLayout(planPoint, toPoint);
                }
            });
        }
    }

    /**
     * 在图层上定位巡检点的位置，并高亮显示出来
     * @param planPoint 巡检点
     * @return Point 地图上的坐标
     */
    private Point centerToPointAndHighlight(OfflinePathBean planPoint){
        if (planPoint == null)
            return null;

        Point dstPoint = null;
        /*图层高亮，并移动到该位置*/
        if (pointLayer != null){
            pointLayer.clearSelection();
            List<Graphic> graphics = pointLayer.getGraphics();
            List<Graphic> selects = new ArrayList<>();
            if (graphics != null && graphics.size() > 0) {
                Graphic graphic = graphics.get(0);
                selects.add(graphic);
                pointLayer.selectGraphics(selects);
                Geometry geometry = graphic.getGeometry();
                if (geometry instanceof Point) {
                    Point toPoint = (Point) geometry;
                    dstPoint = toPoint;
                    mapView.setViewpointCenterAsync(toPoint);
                }
            }
        }

        return dstPoint;
    }

    public boolean isOutOfDate() {
        boolean isOutOfDate = false;
        if (mPlan != null) {
            isOutOfDate = mPlan.isOutOfDate();
        }
        return isOutOfDate;
    }

    private boolean checkPlanValid(OfflinePathBean planPoint) {
        boolean isOutDate;
        boolean is_finished;
        boolean isOver;

        is_finished = planPoint.getState() == OfflinePathBean.POINT_STATE_FINISHED;
        if (is_finished)
            return false;

        isOutDate = isOutOfDate();
        if (isOutDate) {
            ToastUtil.showToast(this, "当前巡检点时间未到或者已超过最后期限。");
            return false;
        }

        Point curLocation = mLocation;
        Location location = GpsTool.getInstance().getLocationInfo();
        if (location != null){
            curLocation = ArcgisUtils.projectLocationDefault(location.getLongitude(), location.getLatitude());
        }

        if (false) {
            isOver = planTool.checkPlanDeviceInPlace(curLocation, planPoint);
            if (!isOver) {
                ToastUtil.showToast(this, "未达到巡检设备附近位置，不能进行录入操作！");
                return false;
            }
        }

        return true;
    }

    private void getPlanInfo(String plan_id) {
        if (plan_id != null && !plan_id.isEmpty()) {
            String strUploading = getString(R.string.updating_data);
            ProgressManager.getInstance().showProgress(this, null, strUploading, false);
            planTool.getOnlinePlanInfo(this, plan_id, new PlanCallBack(){
                @Override
                public void onGetPlanInfo(boolean isSuccess, int errorCode, String errMsg, PlanBean plan) {
                    super.onGetPlanInfo(isSuccess, errorCode, errMsg, plan);
                    ProgressManager.getInstance().cancelProgress();
                    if (isSuccess) {
                        if (plan != null && plan.getPointInfo() != null) {
                            mPlan.setPointInfo(plan.getPointInfo());
                            mDeviceTypeId = mPlan.getDevice_type_id();
                            getChildPlanList();
                        }
                    } else {
                        ToastUtil.showToast(UiPlanDetail.this, errMsg);
                    }
                }
            });
        }
    }

    private void getChildPlanInfo() {
        boolean isOutDate = isOutOfDate();
        if (mPlan != null && !isOutDate){
            String plan_id = mPlan.getPlan_id();
            if (plan_id != null && !plan_id.isEmpty()) {
                /*加载本地计划之前，先检测其完成率*/
                mPlan = planTool.setPlanIsOver(plan_id);
                updateChildPercent(mPlan);
            }
        }
    }

    private void updateChildPercent(PlanBean planBean){
        if (bottomChildPlanDetail != null) {
            bottomChildPlanDetail.initChildPlanDetail(planBean);
        }

        int plan_state = planBean.getState();
        if (plan_state == PlanBean.PLAN_CALCULATE_FINISHED){
            if (!isUpdatingRecalculateState){
                updateRecalculateState();
            }
        }
/*        if (menuItemRecalculate != null){
            menuItemRecalculate.setEnabled(plan_state == PlanBean.PLAN_CALCULATE_UPDATED ||
            plan_state == PlanBean.PLAN_CALCULATE_UNAPPLYED);
        }*/
    }

    private void updateRecalculateState(){
        String strUploading = getResources().getString(R.string.downloading_recalculate_point_data);
        ProgressManager.getInstance().showProgress(this, null, strUploading, false);

        isUpdatingRecalculateState = true;
        planTool.updatePlanRecalculateState(this, mPlanId, new PlanCallBack(){
            @Override
            public void onGetRecalculatePlanPoints(boolean isSuccess, int errorCode, String errMsg, List<String> pointIds) {
                super.onGetRecalculatePlanPoints(isSuccess, errorCode, errMsg, pointIds);
                ProgressManager.getInstance().cancelProgress();
                isUpdatingRecalculateState = false;
                if (isSuccess){
                    updateLocalPointOver(pointIds);
                    updateServerRecalculateState();
                }
            }
        });
    }

    private void updateLocalPointOver(List<String> pointIds){
        if (pointIds != null && pointIds.size() > 0){
            String strUploading = getResources().getString(R.string.updating_local_recalculate_point_data);
            ProgressManager.getInstance().showProgress(this, null, strUploading, false);
            planTool.updateLocalPointOver(pointIds);
            if (mPlan != null) {
                renderPageWithOfflinePoint(false);
            }
            ProgressManager.getInstance().cancelProgress();
        }
    }

    private void updateServerRecalculateState(){
        String strUploading = getResources().getString(R.string.updating_server_recalculate_state);
        ProgressManager.getInstance().showProgress(this, null, strUploading, false);

        isUpdatingRecalculateState = true;
        planTool.updateServerRecalculateState(this, mPlanId, new PlanCallBack(){
            @Override
            public void onUpdateRecalculatePlanState(boolean isSuccess, int errorCode, String errMsg) {
                super.onUpdateRecalculatePlanState(isSuccess, errorCode, errMsg);
                ProgressManager.getInstance().cancelProgress();
                isUpdatingRecalculateState = false;
            }
        });
    }

    private static final boolean LINE_RENDER_WITH_POINT = true;
    /*使用本地的离线点数据渲染页面*/
    private void renderPageWithOfflinePoint(final boolean isUpdate) {
        final int type = mPlan.getType();
        String plan_id = mPlan.getPlan_id();
        long local_count = planTool.getLocalPlanPointCount(plan_id);
        int point_count = mPlan.getPoint_num();
        if (local_count == point_count && point_count > 0) {
            Thread thread = new Thread(() -> {
                if (type == OfflinePathBean.PATH_TYPE_LINE){
                    /*if (LINE_RENDER_WITH_POINT){
                        addPlanOfflinePointLayer(planLineList);
                    }else*/ {
                        addPlanPathLinesByGeometry(isUpdate);
                    }
                }else {
                    addPlanOfflinePointLayer();
                }
            });
            thread.start();
        } else {
            boolean isOutOfDate = isOutOfDate();
            if (isOutOfDate || mDownPointTimes >= DeviceInfoTool.MAX_PLAN_POINT_DOWN_ERR_TIMES - 1){
                renderPageWithServerPoint();
            }else {
                SignDataBean signOnData = planTool.getTodaySignOn();
                if (signOnData != null) {
                    String strUploading = getResources().getString(R.string.retry_downloading_plan_point_data);
                    ProgressManager.getInstance().showProgress(this, null, strUploading, false);
                    Handler handler = new Handler(getMainLooper());
                    handler.postDelayed(() -> {
                        /*有要下载的离线点数据*/
                        planTool.needCurrentPlanPoint(mPlanId);
                        mDownPointTimes++;
                        ProgressManager.getInstance().cancelProgress();
                    }, 1000);
                }
            }
        }
    }

    private void renderPageWithServerPoint() {
        String pointInfo = mPlan.getPointInfo();
        if (pointInfo != null && !pointInfo.isEmpty()){
            Thread thread = new Thread(() -> {
                addPlanPolyLines(pointInfo, mPlan.isLineType());
            });
            thread.start();
        }else {
            if (mPlanId != null)
                getPlanInfo(mPlanId);
        }
    }

    /*更新线的完成状态-渲染时采用连线的方式*/
    private void updateLineOver(List<OfflinePathBean> pathBeens){
        String strUploading = getString(R.string.updating_data);
        ProgressManager.getInstance().showProgress(this, null, strUploading, false);

        List<String> overLineIds = new ArrayList<>();
        for (OfflinePathBean path : pathBeens)
        {
            if (!overLineIds.contains(path.getPath_id())) {
                overLineIds.add(path.getPath_id());
            }
        }

        if (overLineIds.size() > 0){
            for (String strLineId : overLineIds){
                List<OfflinePathBean> pathPoints = planTool.getLocalPathPlanPoints(strLineId);
                if (pathPoints != null && pathPoints.size() > 0){
                    float lineWidth;
                    Polyline polyline = null;
                    PointCollection collection = null;
                    GraphicsOverlay graphicsLayer;
                    boolean isLineToOver;
                    boolean isLastOver = false;
                    String pathId = "";
                    boolean isNewLine = false;
                    for (int i = 0; i < pathPoints.size(); ) {
                        OfflinePathBean pathBean = pathPoints.get(i);
                        Point point = new Point(pathBean.getLng(), pathBean.getLat());
                        int lineColor;
                        if (i == 0) {
                            pathId = pathBean.getPath_id();
                            isNewLine = true;
                        } else {
                            if (!pathBean.getPath_id().equals(pathId)) {
                                pathId = pathBean.getPath_id();
                                isNewLine = true;
                            }
                        }
                        if (isNewLine) {
                            i++;
                            collection = new PointCollection(mapView.getSpatialReference());
                            collection.add(point);
                            isLastOver = pathBean.getIsover() == 1;
                            isNewLine = false;
                        } else {
                            isLineToOver = pathBean.getIsover() == 1;
                            if (isLastOver && isLineToOver){
                                lineColor = overColor;
                                lineWidth = overLineWidth;
                            } else{
                                lineColor = notOverColor;
                                lineWidth = notOverLineWidth;
                            }

                            if (collection != null) {
                                collection.add(point);
                                polyline = new Polyline(collection);

                                SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, lineColor, lineWidth);
                                graphicsLayer = new GraphicsOverlay();
                                graphicsLayer.getGraphics().add(new Graphic(polyline, lineSymbol));
                                mapView.getGraphicsOverlays().add(graphicsLayer);
                                childLineLayers.add(graphicsLayer);
                                isNewLine = true;
                            }
                        }
                    }
                }
            }
        }

        ProgressManager.getInstance().cancelProgress();
    }

    /*更新线的完成状态-渲染时采用单个点的方式*/
    private void updateLineOverByPoint(List<OfflinePathBean> planPoints){
        if (planPoints == null || planPoints.size() == 0)
            return;

        int point_type = mPlan.getType();

        for (OfflinePathBean offlinePathBean : planPoints) {
            if (!Double.isNaN(offlinePathBean.getLng()) && !Double.isNaN(offlinePathBean.getLat())) {
                double lon = offlinePathBean.getLng();
                double lat = offlinePathBean.getLat();
                Point point = new Point(lon, lat);
                MarkerSymbol symbol = null;
                if (mDeviceTypeId == PlanDeviceType.PLAN_DEVICE_TYPE_XJD
                        || point_type == OfflinePathBean.PATH_TYPE_LINE){
                    boolean hasArrived = (offlinePathBean != null && offlinePathBean.getIsover() == 1);
                    if (hasArrived) {
                        symbol = signedDoneSymbol;
                    } else {
                        symbol = unsignedSymbol;
                    }
                }

                if (lon != 0 && lat != 0){
                    android.graphics.Point screenPoint = mapView.locationToScreen(point);
                    int x = screenPoint.x;
                    int y = screenPoint.y;
                    GraphicsOverlay result = null;
                    for (GraphicsOverlay lineLayer : childLineLayers) {
                        List<Graphic> graphics = lineLayer.getGraphics();
                        if (graphics != null && graphics.size() > 0) {
                            Graphic graphic = graphics.get(0);
                            if (graphic != null){
                                Point geometry = (Point) graphic.getGeometry();
                                screenPoint = mapView.locationToScreen(geometry);
                                int x1 = screenPoint.x;
                                int y1 = screenPoint.y;
                                if (Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1)) < 50) {
                                    result = lineLayer;
                                    break;
                                }
                            }
                        }
                    }

                    if (result != null){
                        mapView.getGraphicsOverlays().remove(result);
                        childLineLayers.remove(result);
                    }

                    Graphic graphic = new Graphic(point, symbol);
                    GraphicsOverlay pointLayer = new GraphicsOverlay();
                    pointLayer.getGraphics().add(graphic);
                    mapView.getGraphicsOverlays().add(pointLayer);
                    if (mDeviceTypeId == PlanDeviceType.PLAN_DEVICE_TYPE_XJD
                            || point_type == OfflinePathBean.PATH_TYPE_LINE) {
                        childLineLayers.add(pointLayer);
                    }
                }
            }
        }
    }

    /*从服务器获取数据渲染页面*/
    private void getChildPlanPointList() {
/*        if (C.bPlanDetailNFCModual_debug){
            if (menuItemRecalculate != null)
                menuItemRecalculate.setVisible(mPlan.isNeedOperate() == 0);
        }*/
        int type = mPlan.getType();
        /*如果是线直接用本地的数据渲染*/
        if (type == OfflinePathBean.PATH_TYPE_LINE){
            checkBoxLineAll.setVisibility(View.VISIBLE);
            isLineRenderAll = BaseApp.getInstance().isPlanLineRenderAll();
            checkBoxLineAll.setChecked(isLineRenderAll);
            /*boolean isPipeOn = UiMainLayerManagerDailog.isPipeLayerOn();
            if (!isPipeOn)*/{
                renderPageWithOfflinePoint(false);
            }
        }else {
            if (mDeviceTypeId != PlanDeviceType.PLAN_DEVICE_TYPE_XJD
                    && mDeviceTypeId != PlanDeviceType.PLAN_DEVICE_TYPE_YHXJ
                    && mDeviceTypeId != PlanDeviceType.PLAN_DEVICE_TYPE_YHZG){
                mIsDeviceUseNFC = true;
                if (C.bPlanDetailNFCModual_debug){
                    if (menuItemNFC != null){
                        menuItemNFC.setVisible(true);
                    }
                }
            }
            /*如果是点先获取一次服务器的点数据，再使用本地的点*/
            //getPointFromServerAndRender(true);
            renderPageWithOfflinePoint(false);
        }
    }

    private void getPointFromServerAndRender(final boolean offline) {
        if (mPlan != null) {
            String strUploading = getString(R.string.updating_data);
            ProgressManager.getInstance().showProgress(UiPlanDetail.this, null, strUploading, false);

            String plan_id = mPlan.getPlan_id();
            planTool.getPlanPointsOnline(this, plan_id, new PlanCallBack(){
                @Override
                public void onGetChildPlanPointList(boolean isSuccess, int errorCode, String errMsg, List<OfflinePathBean> planPointList) {
                    super.onGetChildPlanPointList(isSuccess, errorCode, errMsg, planPointList);
                    ProgressManager.getInstance().cancelProgress();
                    if (isSuccess && planPointList != null) {
                        mPlanPointList = planPointList;
                        if (planPointList.size() > 0) {
                            if (offline){
                                renderPageWithOfflinePoint(false);
                            }else {
                                renderPageWithServerPoint();
                            }
                        }
                    }
                }
            });
        }
    }

    private void addPlanPointLayer(List<OfflinePathBean> planPoints) {
        clearAllLayers();
        if (planPoints == null || planPoints.size() == 0)
            return;

        pointLayer = new GraphicsOverlay();
        mapView.getGraphicsOverlays().add(pointLayer);

        int numOver = 0;
        int numNotOver = 0;
        PointCollection collection = new PointCollection(mapView.getSpatialReference());
        boolean bHasStart = false;
        Point firstPoint = null;
        for (OfflinePathBean planPoint : planPoints) {
            if (planPoint != null ) {
                double lon = planPoint.getLng();
                double lat = planPoint.getLat();
                boolean isOver = planPoint.getState() == 1;
                Point point = new Point(lon, lat);
                if (firstPoint == null)
                    firstPoint = point;
                if (bHasStart) {
                } else {
                    bHasStart = true;
                }
                collection.add(point);
                MarkerSymbol symbol;
                boolean isRfidDevice = (planPoint.getRfid() != null && !planPoint.getRfid().isEmpty());
                if (isOver) {
                    numOver++;
                    symbol = isRfidDevice ? doneNfcSymbol : signedDoneSymbol;
                } else {
                    numNotOver++;
                    boolean hasArrived = (planPoint.getIsover() == 1);
                    symbol = isRfidDevice ? unsignedNfcSymbol : unsignedSymbol;
                    if (mDeviceTypeId == PlanDeviceType.PLAN_DEVICE_TYPE_YHXJ
                            || mDeviceTypeId == PlanDeviceType.PLAN_DEVICE_TYPE_YHZG) {
                        if (hasArrived){
                            symbol = signedUndoneSymbol;
                        }
                    } else if (mDeviceTypeId != PlanDeviceType.PLAN_DEVICE_TYPE_XJD) {
                        if (hasArrived){
                            symbol = isRfidDevice ? undoneNfcSymbol : signedUndoneSymbol;
                        }
                    }else {
                        symbol = unsignedSymbol;
                    }
                }
                Graphic graphic = new Graphic(point, symbol);
                pointLayer.getGraphics().add(graphic);
            }
        }
        if (isNeedSetExtent){
            if (planPoints.size() == 1){
                if(firstPoint != null){
                    mapView.setViewpointCenterAsync(firstPoint);
                }
            }else if (planPoints.size() > 1){
                GraphicsOverlay graphicsLayer = new GraphicsOverlay();
                int lineColor = getResources().getColor(R.color.transparent);
                SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, lineColor, 0.0f);
                Polyline polyline = new Polyline(collection);
                graphicsLayer.getGraphics().add(new Graphic(polyline, lineSymbol));
                mapView.getGraphicsOverlays().add(graphicsLayer);
                //mapView.setExtent(polyline, 300);
            }
            isNeedSetExtent = false;
        }

        final int finalNumOver = numOver;
        final int finalNumNotOver = numNotOver;
        runOnUiThread(() -> bottomChildPlanDetail.setPlanOverCount(finalNumOver, finalNumNotOver));
    }

    private void addPlanOfflinePointLayer() {
        runOnUiThread(() -> {
            String strUploading = getString(R.string.updating_data);
            ProgressManager.getInstance().showProgress(UiPlanDetail.this, null, strUploading, false);
        });

        clearAllLayers();
        final List<OfflinePathBean> planPoints = planTool.getLocalPlanPoints(mPlanId);
        if (planPoints == null || planPoints.size() == 0)
            return;

        final int point_type = mPlan.getType();


        int numOver = 0;
        int numNotOver = 0;

        pointLayer = new GraphicsOverlay();
        mapView.getGraphicsOverlays().add(pointLayer);

        PointCollection collection = new PointCollection(mapView.getSpatialReference());
        boolean bHasStart = false;

        Point firstPoint = null;
        for (OfflinePathBean planPoint : planPoints) {
            double lon = planPoint.getLng();
            double lat = planPoint.getLat();
            if (!Double.isNaN(lon) && !Double.isNaN(lat)) {
                boolean isOver = false;
                Point point = new Point(lon, lat);
                if (firstPoint == null)
                    firstPoint = point;
                if (lon != 0 && lat != 0){
                    if (bHasStart) {
                    } else {
                        bHasStart = true;
                    }
                    collection.add(point);
                }
                MarkerSymbol symbol;
                if (mDeviceTypeId == PlanDeviceType.PLAN_DEVICE_TYPE_XJD
                        || point_type == OfflinePathBean.PATH_TYPE_LINE){
                    boolean hasArrived = (planPoint.getIsover() == 1);
                    if (hasArrived) {
                        numOver++;
                        symbol = signedDoneSymbol;
                    } else {
                        numNotOver++;
                        symbol = unsignedSymbol;
                    }
                }else {
                    boolean isRfidDevice = (planPoint.getRfid() != null && !planPoint.getRfid().isEmpty());
                    if ((planPoint.getState() == 1)){
                        isOver = true;
                    }
                    if (isOver) {
                        numOver++;
                        symbol = isRfidDevice ? doneNfcSymbol : signedDoneSymbol;
                    } else {
                        numNotOver++;
                        boolean hasArrived = (planPoint.getIsover() == 1);
                        symbol = isRfidDevice ? unsignedNfcSymbol : unsignedSymbol;
                        if (mDeviceTypeId == PlanDeviceType.PLAN_DEVICE_TYPE_YHXJ ||
                                mDeviceTypeId == PlanDeviceType.PLAN_DEVICE_TYPE_YHZG) {
                            if (hasArrived){
                                symbol = signedUndoneSymbol;
                            }
                        } else if (mDeviceTypeId != PlanDeviceType.PLAN_DEVICE_TYPE_XJD) {
                            if (hasArrived){
                                symbol = isRfidDevice ? undoneNfcSymbol : signedUndoneSymbol;
                            }
                        }
                    }
                }

                if (lon != 0 && lat != 0){
                    HashMap<String, Object> attributes = planPoint.getAttrsFromPathBean();
                    Graphic graphic = new Graphic(point, attributes, symbol);
                    if (pointLayer != null)
                        pointLayer.getGraphics().add(graphic);
                }
            }
        }
        if (isNeedSetExtent){
            if (planPoints.size() == 1){
                Point centerPoint = firstPoint;
                if(centerPoint != null){
                    Handler handler = new Handler(getMainLooper());
                    handler.postDelayed(() -> {
                        mapView.setViewpointCenterAsync(centerPoint, 5000);
                    }, 500);

                }
            }else if (planPoints.size() > 1){
                Polyline polyline = new Polyline(collection);
                if (!polyline.getExtent().isEmpty()){
                    Handler handler = new Handler(getMainLooper());
                    handler.postDelayed(() -> {
                        mapView.setViewpointGeometryAsync(polyline, ArcgisUtils.render_padding);
                    }, 500);
                }
                //mapView.setExtent(polyline, 300);
            }
            isNeedSetExtent = false;
        }

        final int finalNumOver = numOver;
        final int finalNumNotOver = numNotOver;
        Handler handler = new Handler(getMainLooper());
        handler.postDelayed(() -> {
            bottomChildPlanDetail.setPlanOverCount(finalNumOver, finalNumNotOver);
            ProgressManager.getInstance().cancelProgress();
        }, 500);
    }

    private void addPlanPolyLines(String pointInfo, boolean isLine) {
        clearAllLayers();
        try {
            JSONArray jsonArray = new JSONArray(pointInfo);
            if (jsonArray.length() > 0) {
                List<Geometry> geometries = new ArrayList<>();
                int lineColor;
                for (int i = 0; i < jsonArray.length() ; i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    String json = jsonObject.getString("geometry");
                    boolean isOver = false;
                    lineColor = isOver ? overColor : notOverColor;
                    if (json != null) {
                        SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, lineColor, notOverLineWidth);
                        MarkerSymbol pointSymbol = unsignedSymbol;
                        Geometry geo = Geometry.fromJson(json, mapView.getSpatialReference());
                        if (geo != null) {
                            geometries.add(geo);
                            if (isLine) {
                                if (lineLayer == null) {
                                    lineLayer = new GraphicsOverlay();
                                    mapView.getGraphicsOverlays().add(lineLayer);
                                    childLineLayers.add(lineLayer);
                                }
                                Graphic graphic = new Graphic(geo, lineSymbol);
                                lineLayer.getGraphics().add(graphic);
                            }else {
                                if (pointLayer == null) {
                                    pointLayer = new GraphicsOverlay();
                                    mapView.getGraphicsOverlays().add(pointLayer);
                                }
                                pointLayer.getGraphics().add(new Graphic(geo, pointSymbol));
                            }
                        }
                    }
                }
                if (!isLine) {
                    runOnUiThread(() -> bottomChildPlanDetail.setPlanOverCount(0, geometries.size()));
                }

                /*设置地图显示有效范围*/
                if (isNeedSetExtent) {
                    if (geometries.size() > 0) {
                        try {
                            Handler handler = new Handler(getMainLooper());
                            handler.postDelayed(() -> {
                                Geometry geometry = GeometryEngine.union(geometries);
                                if (geometry != null && !geometry.getExtent().isEmpty())
                                    mapView.setViewpointGeometryAsync(geometry, ArcgisUtils.render_padding);
                            }, 500);
                        }catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }
                    isNeedSetExtent = false;
                }
            }
        } catch (JSONException | JsonParseException e) {
            e.printStackTrace();
        }
    }

    private synchronized void addPlanPathLines(List<OfflinePathBean> pathPoints, boolean isAllNotOver) {
        runOnUiThread(() -> {
            String strUploading = getString(R.string.updating_data);
            ProgressManager.getInstance().showProgress(UiPlanDetail.this, null, strUploading, false);
        });

        /*清除所有已经存在的Layer*/
        clearAllLayers();
        if (pathPoints == null || pathPoints.size() == 0)
            return;

        float lineWidth;
        PointCollection collection = new PointCollection(mapView.getSpatialReference());
        Polyline polyline = new Polyline(collection);
        GraphicsOverlay graphicsLayer;
        boolean isLineToOver;
        boolean isLastOver = false;
        String pathId = "";
        boolean isNewLine = false;
        for (int i = 0; i < pathPoints.size(); ) {
            OfflinePathBean pathBean = pathPoints.get(i);
            Point point = new Point(pathBean.getLng(), pathBean.getLat());
            int lineColor;
            if (i == 0) {
                pathId = pathBean.getPath_id();
                isNewLine = true;
            } else {
                if (!pathBean.getPath_id().equals(pathId)) {
                    pathId = pathBean.getPath_id();
                    isNewLine = true;
                }
            }
            if (isNewLine) {
                i++;
                collection.add(point);
                isLastOver = pathBean.getIsover() == 1;
                isNewLine = false;
            } else {
                if (!isAllNotOver) {
                    isLineToOver = pathBean.getIsover() == 1;
                    if (isLastOver && isLineToOver){
                        lineColor = overColor;
                        lineWidth = overLineWidth;
                    } else{
                        lineColor = notOverColor;
                        lineWidth = notOverLineWidth;
                    }
                } else {
                    lineColor = notOverColor;
                    lineWidth = notOverLineWidth;
                }

                if (polyline != null) {
                    if (isLineRenderAll || lineWidth == notOverLineWidth){
                        collection.add(point);
                        SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, lineColor, lineWidth);
                        graphicsLayer = new GraphicsOverlay();
                        graphicsLayer.getGraphics().add(new Graphic(polyline, lineSymbol));
                        //collection.add(polyline, false);
                        mapView.getGraphicsOverlays().add(graphicsLayer);
                        childLineLayers.add(graphicsLayer);
                    }
                    isNewLine = true;
                }
            }
        }

        if (isNeedSetExtent) {
            if (!polyline.getExtent().isEmpty())
                mapView.setViewpointGeometryAsync(polyline, ArcgisUtils.render_padding);
            isNeedSetExtent = false;
        }

        Handler handler = new Handler(getMainLooper());
        handler.postDelayed(() -> ProgressManager.getInstance().cancelProgress(), 500);
    }

    private List<OfflinePathBean> getPathsById(List<OfflinePathBean> pathPoints, int[] indexes){
        List<OfflinePathBean> pathList = new ArrayList<>();
        int index = indexes[0];
        String pathId = pathPoints.get(index).getPath_id();
        for (int i = index; i < pathPoints.size(); i++){
            OfflinePathBean pathBean = pathPoints.get(i);
            if (pathId.equals(pathBean.getPath_id())){
                pathList.add(pathBean);
                if (i == pathPoints.size() - 1){
                    indexes[0] = i+1;
                    break;
                }
            }else {
                indexes[0] = i;
                break;
            }
        }
        return pathList;
    }

    private boolean isRenderLineIng = false;
    private synchronized void addPlanPathLinesByGeometry(boolean isUpdate) {
        if (mPlan == null || mLocation == null)
            return;

        if (isRenderLineIng)
            return;

        Map<String, List<OfflinePathBean>> pathMaps = planTool.getLocalPlanPointsInRegion(mLocation, mapView.getSpatialReference(), mPlanId);
        if (pathMaps == null || pathMaps.size() == 0)
            return;

        isRenderLineIng = true;

        if (!isUpdate){
            runOnUiThread(() -> {
                String strUploading = getString(R.string.updating_data);
                ProgressManager.getInstance().showProgress(UiPlanDetail.this, null, strUploading, false);
            });
        }

        PartCollection allCollection = new PartCollection(mapView.getSpatialReference());
        PartCollection notAllCollection = new PartCollection(mapView.getSpatialReference());
        if (pathMaps != null){
            for (Map.Entry<String, List<OfflinePathBean>> entry : pathMaps.entrySet()) {
                List<OfflinePathBean> pathList = entry.getValue();
                if (pathList != null){
                    int total_over = 0;
                    int path_size = pathList.size();
                    for (OfflinePathBean pathBean : pathList){
                        if (pathBean.getIsover() == 1)
                            total_over++;
                    }

                    Part lineOverPart = new Part(mapView.getSpatialReference());
                    Part lineNoOverPart = new Part(mapView.getSpatialReference());
                    if (total_over == path_size){
                        int index = 0;
                        for (OfflinePathBean pathBean : pathList) {
                            if (index == 0){
                                lineOverPart.addPoint(pathBean.getLng(), pathBean.getLat());
                                //lineOver.startPath(pathBean.getLng(), pathBean.getLat());
                            }else {
                                lineOverPart.addPoint(pathBean.getLng(), pathBean.getLat());
                                //lineOver.lineTo(pathBean.getLng(), pathBean.getLat());
                            }
                            index++;
                        }
                        allCollection.add(new Part(lineOverPart, mapView.getSpatialReference()));
                        //lineOverAll.add(lineOver,false);
                    }else if (total_over > 0 && total_over < path_size){
                        int index = 0;
                        for (OfflinePathBean pathBean : pathList) {
                            //Polyline polyline = new Polyline();
                            Point start = new Point(pathBean.getLng(), pathBean.getLat());
                            //polyline.startPath(pathBean.getLng(), pathBean.getLat());
                            if (index >= path_size -1)
                                break;
                            OfflinePathBean nextPathBean = pathList.get(index+1);
                            if (nextPathBean != null){
                                Point end = new Point(nextPathBean.getLng(), nextPathBean.getLat());
                                //polyline.lineTo(nextPathBean.getLng(), nextPathBean.getLat());
                                LineSegment segment = new LineSegment(start, end, mapView.getSpatialReference());
                                Part part = new Part(mapView.getSpatialReference());
                                part.add(segment);
                                if (pathBean.getIsover() == 1 && nextPathBean.getIsover() == 1){
                                    allCollection.add(part);
                                    //lineOverPart.add(segment);
                                    //lineOver.add(polyline, false);
                                }else {
                                    notAllCollection.add(part);
                                    //lineNoOverPart.add(segment);
                                    //lineNotOver.add(polyline, false);
                                }
                            }
                            index++;
                        }
                        //allCollection.add(new Part(lineOverPart, mapView.getSpatialReference()));
                        //notAllCollection.add(new Part(lineNoOverPart, mapView.getSpatialReference()));
                        //lineOverAll.add(lineOver, false);
                        //lineNotOverAll.add(lineNotOver, false);
                    }else if (total_over == 0){
                        /*全部未到位*/
                        int index = 0;
                        for (OfflinePathBean pathBean : pathList) {
                            if (index == 0){
                                lineNoOverPart.addPoint(pathBean.getLng(), pathBean.getLat());
                                //lineNotOver.startPath(pathBean.getLng(), pathBean.getLat());
                            }else {
                                lineNoOverPart.addPoint(pathBean.getLng(), pathBean.getLat());
                                //lineNotOver.lineTo(pathBean.getLng(), pathBean.getLat());
                            }
                            index++;
                        }
                        notAllCollection.add(new Part(lineNoOverPart, mapView.getSpatialReference()));
                        //lineNotOverAll.add(lineNotOver,false);
                    }
                }
            }
        }

        if (!isUpdate){
            /*清除所有已经存在的Layer*/
            clearAllLayers();
        }

        Polyline lineOverAll = new Polyline(allCollection, mapView.getSpatialReference());
        Polyline lineNotOverAll = new Polyline(notAllCollection, mapView.getSpatialReference());

        /*记载未到位的点*/
        renderLineWithJsonString(lineNotOverAll, false, isUpdate);
        Geometry extent = lineNotOverAll;
        /*加载到位的点*/
        if (isLineRenderAll) {
            renderLineWithJsonString(lineOverAll, true, isUpdate);
            if (!lineOverAll.isEmpty() && !lineNotOverAll.isEmpty()) {
                extent = GeometryEngine.union(lineNotOverAll, lineOverAll);
            }else {
                if (!lineNotOverAll.isEmpty()) {
                    extent = lineNotOverAll;
                }else if (!lineOverAll.isEmpty()) {
                    extent = lineOverAll;
                }
            }
        }

        /*设置地图显示有效范围*/
        if (isNeedSetExtent) {
            Geometry center = extent;
            if (!center.getExtent().isEmpty()){
                Handler handler = new Handler(getMainLooper());
                handler.postDelayed(() -> {
                    mapView.setViewpointGeometryAsync(center, ArcgisUtils.render_padding);
                }, 500);
            }
            isNeedSetExtent = false;
        }

        if (!isUpdate){
            Handler handler = new Handler(getMainLooper());
            handler.postDelayed(() -> ProgressManager.getInstance().cancelProgress(), 500);
        }

        isRenderLineIng = false;
    }

    private void renderLineWithJsonString(Geometry geo, boolean isOver, boolean isUpdate){
        if (geo != null) {
            int lineColor = isOver ? overColor : notOverColor;
            float lineWidth = isOver ? overLineWidth : notOverLineWidth;
            SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, lineColor, lineWidth);
            Graphic graphic = new Graphic(geo, lineSymbol);
            GraphicsOverlay graphicsLayer = isOver ? lineOverLayer : lineNotOverLayer;
            if (!isUpdate || graphicsLayer == null){
                graphicsLayer = new GraphicsOverlay();
                graphicsLayer.getGraphics().add(graphic);
                mapView.getGraphicsOverlays().add(graphicsLayer);
                childLineLayers.add(graphicsLayer);
                if (isOver)
                    lineOverLayer = graphicsLayer;
                else
                    lineNotOverLayer = graphicsLayer;
            }else {
                if (graphicsLayer.getGraphics().size() > 0){
                    graphicsLayer.getGraphics().set(0, graphic);
                }
            }
        }
    }

    private OfflinePathBean findPlanPointFromRfid(String rfid){
        OfflinePathBean pointBean = null;

        OfflinePathBean pathBean = planTool.getLocalPlanPointByRFID(mPlanId, rfid);
        if (pathBean != null){
            pointBean = pathBean;
        }else if (mPlanPointList != null){
            for (OfflinePathBean planPointBean : mPlanPointList) {
                if (planPointBean.getRfid().equals(rfid)){
                    pointBean = planPointBean;
                    break;
                }
            }
        }
        return pointBean;
    }

    private void getChildPlanList() {
        if (mPlan != null) {
            getChildPlanPointList();
            showViewDetailClicked();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_plan_detaill, menu);
        menuItemRecalculate = menu.findItem(R.id.action_recalculate);
/*        if (menuItemRecalculate != null) {
            menuItemRecalculate.setVisible(true);
        }*/
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_recalculate: {
                showSyncPlanPercentDialog();
            }
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void moveToMyLocation() {
        if (mLocation != null) {
            mapView.setViewpointCenterAsync(mLocation);
            //mapView.centerAt(mLocation, true);
            int type = mPlan.getType();
            boolean outOfDate = isOutOfDate();
            if (!outOfDate && type == OfflinePathBean.PATH_TYPE_LINE){
                isNeedSetExtent = true;
                renderPageWithOfflinePoint(false);
            }
        }
    }

    /*
     * 读取NFC标签数据的操作
     */
    private boolean readMifareFromTag(Intent intent, String[] result) {
        String err = getResources().getString(R.string.nfc_read_no_content);
        try {
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            if (tag != null){
                MifareClassic mf = MifareClassic.get(tag);
                if (mf != null){
                    byte[] tagBytes = mf.getTag().getId();

                    String rfid = StringUtil.byte2HexString(tagBytes);
                    result[0] = rfid;
                    return true;
                }else {
                    byte[] tagBytes = tag.getId();

                    if (tagBytes != null){
                        String rfid = StringUtil.byte2HexString(tagBytes);
                        result[0] = rfid;
                        return true;
                    }
                }
                result[0] = err;
                return false;
            }
        }catch (Exception ignored){

        }
        result[0] = err;
        return false;
    }

    /**
     * 通过nfc读取到的RFID编码，查找当前计划的巡检设备，如果找到，则高亮并显示设备详情。
     * @param rfid rfid标签码
     */
    private void findDeviceByRfid(String rfid){
        if (mPlanId == null || rfid == null)
            return;

        OfflinePathBean planPoint = findPlanPointFromRfid(rfid);
        if (planPoint != null){
            /*高亮设备并置中*/
            Point toPoint = centerToPointAndHighlight(planPoint);
            if (toPoint != null){
                /*显示设备详情*/
                showRepairBottomLayout(planPoint, toPoint);
            }
        }else {
            String err = "未找到对应的设备";
            ToastUtil.showToast(this, err);
        }
    }

    private void showNFCReadDialog(){
        mIsNFCUsable = ifNFCUsable();
        if (nfcDeviceDialog == null){
            nfcDeviceDialog = new ReadNFCDeviceDialog(this, mIsNFCUsable);
            nfcDeviceDialog.setClickListener(rfid -> findDeviceByRfid(rfid));
        }else {
            nfcDeviceDialog.init(mIsNFCUsable);
        }
        nfcDeviceDialog.show();
    }

    private void showApplyRecalculateDialog(){
        PlanApplyRecalculateDialog applyRecalculateDialog = new PlanApplyRecalculateDialog(this, mPlan);
        applyRecalculateDialog.setOnApplyRecalculate(isSuccess -> {
            if (isSuccess){
                if (menuItemRecalculate != null)
                    menuItemRecalculate.setEnabled(false);
            }
        });
        applyRecalculateDialog.show();
    }

    private void showSyncPlanPercentDialog(){
        DialogUtil.showAlertWithConfirmCancelButton(this, "完成率同步",
                "是否手动将本地的巡检数据同步到服务器?", true,
                (dialogInterface, i) -> checkUnUploadOverPoints());
    }

    private void checkUnUploadOverPoints() {
        GpsTool gpsTool = GpsTool.getInstance();
        long count = gpsTool.getUnUploadPointsCount();
        if (count <= 0) {
            ToastUtil.showToast(this, "当前本地的到位数据都已经提交");
            return;
        }

        String strUploading = "正在提交本地的巡检数据到服务器，请稍后...";
        ProgressManager.getInstance().showProgress(this, null, strUploading, false);
        gpsTool.checkPathOverUpload(new PlanCallBack() {
            @Override
            public void onUpdatePointOver(boolean isSuccess, int errorCode, String errMsg, List<String> transfer_ids) {
                super.onUpdatePointOver(isSuccess, errorCode, errMsg, transfer_ids);

                ProgressManager.getInstance().cancelProgress();
                ToastUtil.showToast(UiPlanDetail.this, "本地巡检数据已经提到到服务器，请在网页端查看");
                EventBus.getDefault().post(new OfflineWorkEvent(OfflineWorkEvent.OFFLINE_JOB_UPLOAED));

            }
        }, -1);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (!C.bPlanDetailNFCModual_debug){
            return;
        }
        if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())){
            if (!mIsDeviceUseNFC)
                return;

            showNFCReadDialog();

            /*开始读取，显示进度条*/
            EventBus.getDefault().post(new ReadNFCDeviceDialog.NFCREADMessageEvent(
                    ReadNFCDeviceDialog.NFCREADMessageEvent.NFC_READ_START, true, null));

            final String[] result = new String[1];
            final boolean isOk = readMifareFromTag(intent, result);

            Handler handler = new Handler(getMainLooper());
            handler.postDelayed(() -> EventBus.getDefault().post(new ReadNFCDeviceDialog.NFCREADMessageEvent(
                    ReadNFCDeviceDialog.NFCREADMessageEvent.NFC_READ_RESULT, isOk, result[0])), 1000);
        }
    }

    @Override
    protected IPresenter createPresenter() {
        return null;
    }

    @Override
    public int setLayout() {
        return R.layout.ui_plan_detail;
    }

    @Override
    public boolean enableLoadMap() {
        return true;
    }

    @Override
    public boolean enableMoveMapListener() {
        return false;
    }

    @Override
    public boolean enableArcgisLocating() {
        return true;
    }

    @Override
    public boolean enableAmapLocationListener() {
        return true;
    }

    @Override
    public boolean enableMapMoveToLocation() {
        return false;
    }

    @Override
    public TextView getTextAddrView() {
        return null;
    }

    @Override
    public Button getAddPhotoBtn() {
        return null;
    }

    @Override
    public LinearLayout getPhotoGallery() {
        return null;
    }

    @Override
    public MyAudioRecordButton getAudioRecordBtn() {
        return null;
    }

    @Override
    protected void onResume() {
        super.onResume();

        // 前台分发系统,这里的作用在于第二次检测NFC标签时该应用有最高的捕获优先权.
        if (C.bPlanDetailNFCModual_debug){
            if (nfcAdapter != null)
                nfcAdapter.enableForegroundDispatch(this, pendingIntent, mFilters, mTechLists);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (C.bPlanDetailNFCModual_debug){
            if (nfcAdapter != null)
                nfcAdapter.disableForegroundDispatch(this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (C.bPlanDetailNFCModual_debug) {
            if (nfcDeviceDialog != null) {
                nfcDeviceDialog.unregisterEvent();
            }
        }
    }
}
