package com.tsi.navi.util;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PointF;
import android.util.DisplayMetrics;
import android.util.TypedValue;

import androidx.core.graphics.ColorUtils;

import com.naver.maps.geometry.LatLng;
import com.naver.maps.geometry.LatLngBounds;
import com.naver.maps.map.NaverMap;
import com.naver.maps.map.overlay.CircleOverlay;
import com.naver.maps.map.overlay.PolygonOverlay;
import com.tsi.navi.preferences.PrefManager;

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

public class BoundaryUtils {

    public final String DRIVE_SHOW_BOUNDARY = "driveShowBoundary";
    public final String DRIVE_NODE_RADIUS_METER = "driveNodeRadiusMeter";
    public final String DRIVE_BOUNDARY_METER = "driveBoundaryMeter";
    public final String DRIVE_DIRECTION_METER = "driveDirectionMeter";

    private final Context context;
    private NaverMap naverMap;
    private PolygonOverlay polygonBounds;
    private CircleOverlay carBoundsCircle;

    private int driveDirectionMeter;
    private int driveBoundaryMeter;
    private int driveNodeRadiusMeter;

    private float directionPixel;
    private float bottomRadiusPixel;
    private float topRadiusPixel;
    //private float topMarginPixel;

    private boolean driveShowBoundary;
    private PointF ptBL;
    private PointF ptBR;
    private PointF ptTL;
    private PointF ptTR;

    private LatLng latLngCenter;
    private LatLng[] latLngBounds = { null, null, null, null };
    //private Marker[] mkBounds = { null, null, null, null, null, null, null, null };

    public BoundaryUtils(Context context) {
        this.context = context;
        //this.dpPerPixel = convertDpToPixel(1);
        //this.pixelPerDp = convertPixelsToDp(1);
        //this.topMarginPixel = 10;
        //Log.e("dpPerPixel", "dpPerPixel: " + dpPerPixel);
        //Log.e("pixelPerDp", "pixelPerDp: " + pixelPerDp);
        loadConfig();
    }

    public void loadConfig() {
        this.driveNodeRadiusMeter = PrefManager.getInt(this.context, DRIVE_NODE_RADIUS_METER, 40);
        this.driveBoundaryMeter = PrefManager.getInt(this.context, DRIVE_BOUNDARY_METER, 60);
        this.driveDirectionMeter = PrefManager.getInt(this.context, DRIVE_DIRECTION_METER, 300);
        this.driveShowBoundary = PrefManager.getBoolean(this.context, DRIVE_SHOW_BOUNDARY, false);
    }
    public void saveConfig() {
        PrefManager.putInt(this.context, DRIVE_NODE_RADIUS_METER, this.driveNodeRadiusMeter);
        PrefManager.putInt(this.context, DRIVE_BOUNDARY_METER, this.driveBoundaryMeter);
        PrefManager.putInt(this.context, DRIVE_DIRECTION_METER, this.driveDirectionMeter);
        PrefManager.putBoolean(this.context, DRIVE_SHOW_BOUNDARY, this.driveShowBoundary);
    }

    public void setDebugMode(boolean debugMode) {
        this.driveShowBoundary = debugMode;
    }

    //dp를 px로 변환 (dp를 입력받아 px을 리턴)
    public float convertDpToPixel(float dp) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        //Log.e("convertDpToPixel", "convertDpToPixel..................: " + metrics.toString());
        //DisplayMetrics{density=2.25, width=2560, height=1492, scaledDensity=2.25, xdpi=286.197, ydpi=286.449} // 가로모드
        //DisplayMetrics{density=2.25, width=1600, height=2452, scaledDensity=2.25, xdpi=286.197, ydpi=286.449} // 세로모드
        float ht_px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1492, this.context.getResources().getDisplayMetrics());
        float wt_px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2560, this.context.getResources().getDisplayMetrics());
        //Log.e("ht_px", "ht_px..................: " + ht_px);
        //Log.e("wt_px", "wt_px..................: " + wt_px);

        float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
        return px;
    }

    //px을 dp로 변환 (px을 입력받아 dp를 리턴)
    public float convertPixelsToDp(float px) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        float dp = px / ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
        return dp;
    }

    public int dpToPx(int dp) {
        Resources resources = context.getResources();
        return (int)(dp * resources.getDisplayMetrics().density);
    }

    public int pxToDp(int px) {
        Resources resources = context.getResources();
        return (int)(px / resources.getDisplayMetrics().density);
    }

    public void setup(NaverMap map) {
        this.naverMap = map;
        this.ptBL = new PointF(0, 0);
        this.ptBR = new PointF(0, 0);
        this.ptTL = new PointF(0, 0);
        this.ptTR = new PointF(0, 0);


        this.latLngBounds[0] = this.naverMap.getCameraPosition().target;    // top left
        this.latLngBounds[1] = this.naverMap.getCameraPosition().target;    // top right
        this.latLngBounds[2] = this.naverMap.getCameraPosition().target;    // bottom left
        this.latLngBounds[3] = this.naverMap.getCameraPosition().target;    // bottom right

        initMap();

        this.polygonBounds = new PolygonOverlay();
        this.polygonBounds.setColor(ColorUtils.setAlphaComponent(Color.GREEN, 100));
        this.polygonBounds.setOutlineWidth(3);
        this.polygonBounds.setOutlineColor(Color.BLUE);
        //this.polygonBounds.setCoords(getDirectionBounds(new LatLng(this.naverMap.getCameraPosition().target.latitude, this.naverMap.getCameraPosition().target.longitude)));
        //this.polygonBounds.setMap(this.driveShowBoundary ? this.naverMap : null);

        this.carBoundsCircle = new CircleOverlay();
        this.carBoundsCircle.setCenter(this.naverMap.getCameraPosition().target);
        this.carBoundsCircle.setRadius(this.driveNodeRadiusMeter);
        this.carBoundsCircle.setColor(ColorUtils.setAlphaComponent(Color.RED, 100));
        this.carBoundsCircle.setOutlineWidth(3);
        this.carBoundsCircle.setOutlineColor(Color.RED);
        //this.carBoundsCircle.setMap(this.driveShowBoundary ? this.naverMap : null);

/*
        for (int ii = 0; ii < this.mkBounds.length; ii++) {
            Marker marker = new Marker(this.naverMap.getCameraPosition().target);
            marker.setCaptionTextSize(16);
            marker.setCaptionColor(Color.BLACK);
            marker.setCaptionHaloColor(Color.YELLOW);
            marker.setCaptionText(String.valueOf(ii % 4));

            if (ii > 3) {
                marker.setIcon(OverlayImage.fromResource(R.drawable.ic_node_error));
            }
            else {
                marker.setIcon(OverlayImage.fromResource(R.drawable.ic_node_normal));
            }
            marker.setMap(this.driveShowBoundary ? this.naverMap : null);
            this.mkBounds[ii] = marker;
        }
*/
    }

    public void initMap() {
        if (this.naverMap.getCameraPosition().zoom < 15 || this.naverMap.getCameraPosition().zoom > 18) return;

        LatLng center = new LatLng(this.naverMap.getCameraPosition().target.latitude, this.naverMap.getCameraPosition().target.longitude);
        //PointF ptCenter = this.naverMap.getProjection().toScreenLocationAt(center, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
        PointF ptCenter = this.naverMap.getProjection().toScreenLocation(center);

        // 5미터 단위로 계산함.
        PointF to = null;
        PointF basePt = ptCenter;
        LatLng latlng = this.naverMap.getProjection().fromScreenLocationAt(basePt, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
        float ii;
        for (ii = 0; ; ii++) {
            to = new PointF(basePt.x, basePt.y-ii);
            LatLng latLng2 = this.naverMap.getProjection().fromScreenLocationAt(to, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
            double distance = latlng.distanceTo(latLng2);
            if (distance >= 5) {
                //Log.e("TsiMapUtils", "initMap: distance........................: " + ii + ", " + distance);
                break;
            }
        }
        ii = ii * (this.driveDirectionMeter / 5);
        to.y = basePt.y - ii;
        if (to.y < 1)
            this.directionPixel = ptCenter.y - 10;
        else
            this.directionPixel = ii;
//Log.e("TsiMapUtils", "initMap: directionPixel: " + to.toString() + ", " + this.directionPixel);

        for (ii = 0; ; ii++) {
            to = new PointF(basePt.x+ii, basePt.y);
            LatLng latLng2 = this.naverMap.getProjection().fromScreenLocationAt(to, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
            double distance = latlng.distanceTo(latLng2);
            if (distance >= 5) {
                //Log.e("TsiMapUtils", "distance........................: " + ii + ", " + distance);
                break;
            }
        }
        this.bottomRadiusPixel = ii * (this.driveBoundaryMeter / 5);

        basePt = new PointF(ptCenter.x, ptCenter.y-this.directionPixel);
        latlng = this.naverMap.getProjection().fromScreenLocationAt(basePt, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
        for (ii = 0; ; ii++) {
            to = new PointF(basePt.x+ii, basePt.y);
            LatLng latLng2 = this.naverMap.getProjection().fromScreenLocationAt(to, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
            double distance = latlng.distanceTo(latLng2);
            if (distance >= 5) {
                //Log.e("TsiMapUtils", "distance........................: " + ii + ", " + distance);
                break;
            }
        }
        this.topRadiusPixel = ii * (this.driveBoundaryMeter / 5);

        this.carBoundsCircle.setRadius(this.driveNodeRadiusMeter);
    }

    protected List<LatLng> getDirectionBounds(LatLng center) {

        initMap();

        List<LatLng> coords = new ArrayList<>();
        //PointF ptCenter = this.naverMap.getProjection().toScreenLocationAt(center, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
        PointF ptCenter = this.naverMap.getProjection().toScreenLocation(center);
        PointF ptTop = new PointF(ptCenter.x, ptCenter.y-this.directionPixel);

        this.ptBL = new PointF(ptCenter.x-this.bottomRadiusPixel, ptCenter.y);
        this.ptBR = new PointF(ptCenter.x+this.bottomRadiusPixel, ptCenter.y);
        this.ptTL = new PointF(ptTop.x-this.topRadiusPixel, ptTop.y);
        this.ptTR = new PointF(ptTop.x+this.topRadiusPixel, ptTop.y);

        this.latLngBounds[0] = this.naverMap.getProjection().fromScreenLocation(this.ptTL);
        this.latLngBounds[1] = this.naverMap.getProjection().fromScreenLocation(this.ptTR);
        this.latLngBounds[2] = this.naverMap.getProjection().fromScreenLocation(this.ptBL);
        this.latLngBounds[3] = this.naverMap.getProjection().fromScreenLocation(this.ptBR);
/*
//===> 아래는 회전값이 들어가야 해서 사용할 수 없다.
        LatLng bottomLeft = center.offset(0, -60);
        LatLng bottomRight = center.offset(0, 60);
        LatLng topLeft = center.offset(300, -60);
        LatLng topRight = center.offset(300, 60);
*/
        Collections.addAll(coords,
                this.latLngBounds[2],
                this.latLngBounds[3],
                this.latLngBounds[1],
                this.latLngBounds[0],
                this.latLngBounds[2]
        );

        return coords;
    }

    public void drawBounds(LatLng location) {

        if (location != null) {
            this.latLngCenter = location;
        }
        else {
            this.latLngCenter = new LatLng(this.naverMap.getCameraPosition().target.latitude, this.naverMap.getCameraPosition().target.longitude);
        }

        this.carBoundsCircle.setCenter(this.latLngCenter);
        this.carBoundsCircle.setMap(this.driveShowBoundary ? this.naverMap : null);

        this.polygonBounds.setCoords(getDirectionBounds(this.latLngCenter));
        this.polygonBounds.setMap(this.driveShowBoundary ? this.naverMap : null);

/*
        if (this.driveShowBoundary) {
            LatLngBounds carBounds = getCarBounds();
            this.mkBounds[0].setPosition(carBounds.getNorthWest());
            this.mkBounds[1].setPosition(carBounds.getNorthEast());
            this.mkBounds[2].setPosition(carBounds.getSouthEast());
            this.mkBounds[3].setPosition(carBounds.getSouthWest());
            this.mkBounds[4].setPosition(this.latLngBounds[0]);
            this.mkBounds[5].setPosition(this.latLngBounds[1]);
            this.mkBounds[6].setPosition(this.latLngBounds[2]);
            this.mkBounds[7].setPosition(this.latLngBounds[3]);
        }
        for (Marker mkBound : this.mkBounds) {
            mkBound.setMap((this.driveShowBoundary ? this.naverMap : null));
        }
*/
    }

    public LatLng[] getLatLngBounds() {
        return this.latLngBounds;
    }

    public boolean contains(LatLng position) {
/*
// 지도가 회전하고 있어서 경위도로 하면 검지하지 못하는 베어링 영역이 있음.....
        if (position.latitude <= this.latLngBounds[0].latitude && position.latitude >= this.latLngBounds[3].latitude &&
            position.longitude >= this.latLngBounds[0].longitude && position.longitude <= this.latLngBounds[3].longitude) {
            return true;
        }
*/
        //PointF ptNode = this.naverMap.getProjection().toScreenLocationAt(position, this.naverMap.getCameraPosition().zoom, this.naverMap.getCameraPosition().tilt, this.naverMap.getCameraPosition().bearing, false);
        PointF ptNode = this.naverMap.getProjection().toScreenLocation(position);
/*
        Log.e("contains", " position: " + ptNode.toString());
        Log.e("contains", "this.ptTL: " + this.ptTL.toString());
        Log.e("contains", "this.ptTR: " + this.ptTR.toString());
        Log.e("contains", "this.ptBL: " + this.ptBL.toString());
        Log.e("contains", "this.ptBR: " + this.ptBR.toString());
*/

        if (ptNode.x >= this.ptTL.x && ptNode.x <= this.ptTR.x &&
            ptNode.y >= this.ptTL.y && ptNode.y <= this.ptBL.y) {
            return true;
        }
        return false;
    }

    public LatLngBounds getPolygonBounds() {
        // 지도가 회전하고 있어서 베어링값에 따라서 바운더리가 달라짐
        return new LatLngBounds(this.latLngBounds[2], this.latLngBounds[1]);
        //return this.polygonBounds.getBounds();
    }
    public LatLngBounds getCarBounds() {
        return this.carBoundsCircle.getBounds();
    }

    public PointF getCarPosition() {
        return this.naverMap.getProjection().toScreenLocation(this.latLngCenter);
    }
}
