package cpm.com.gskmtorange.storeinmap;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;

import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.appindexing.Thing;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import cpm.com.gskmtorange.GetterSetter.StoreBean;
import cpm.com.gskmtorange.R;
import cpm.com.gskmtorange.constant.CommonString;

public class StoreRouteActivity extends FragmentActivity implements OnMapReadyCallback {

    private Context context;
    private GoogleMap mMap;
    private StoreBean store;
    private LatLng origin, destination;

    private FusedLocationProviderClient fusedLocationClient;
    private LocationRequest locationRequest;
    private LocationCallback locationCallback;

    private static final int LOCATION_PERMISSION_REQUEST = 1001;

    FloatingActionButton fab_cancel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_store_route);
        context = this;

        fab_cancel = findViewById(R.id.fab_cancel);
        fab_cancel.setOnClickListener(v -> {
            finish();
            overridePendingTransition(R.anim.activity_back_in, R.anim.activity_back_out);
        });

        store = (StoreBean) getIntent().getSerializableExtra(CommonString.KEY_STORE_LIST);

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

        locationRequest = LocationRequest.create()
                .setInterval(2000) // 2 sec
                .setFastestInterval(1000) // 1 sec
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(@NonNull LocationResult locationResult) {
                if (locationResult.getLastLocation() != null) {
                    Location loc = locationResult.getLastLocation();
                    updateMapWithCurrentLocation(loc);
                }
            }
        };

        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        if (mapFragment != null) {
            mapFragment.getMapAsync(this);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Enable zoom controls for user
        mMap.getUiSettings().setZoomControlsEnabled(true);

        // Request permissions
        if (checkLocationPermission()) {
            startLocationUpdates();
            fusedLocationClient.getLastLocation()
                    .addOnSuccessListener(this, location -> {
                        if (location != null) {
                            updateMapWithCurrentLocation(location); // show instantly with last known location
                        } else {
                            Toast.makeText(this, "Waiting for current location...", Toast.LENGTH_SHORT).show();
                        }
                    });
        }
       /* if (store != null) {
            LatLng storeLoc = new LatLng(Double.parseDouble(store.getLATITUDE()), Double.parseDouble(store.getLONGITUDE()));
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(storeLoc, 15));
        }*/
    }

    private boolean checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    LOCATION_PERMISSION_REQUEST
            );
            return false;
        }
        return true;
    }

    private void startLocationUpdates() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
        }
    }

    private void updateMapWithCurrentLocation(Location location) {
        double latitude = location.getLatitude();
        double longitude = location.getLongitude();

        origin = new LatLng(latitude, longitude);
        destination = new LatLng(Double.parseDouble(store.getLATITUDE()), Double.parseDouble(store.getLONGITUDE()));

        // Show markers
        StoreBean currentStore = new StoreBean();
        currentStore.setLATITUDE(latitude + "");
        currentStore.setLONGITUDE(longitude + "");
        currentStore.setSTORE_NAME("Current Location");
        showMarker(currentStore, R.layout.current_marker_view);

        showMarker(store, R.layout.marker_view);

        // Move camera to fit both points
        LatLngBounds.Builder builder = new LatLngBounds.Builder();
        builder.include(origin);
        builder.include(destination);
        mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 100));

        // Fetch and draw route
        String url = getUrl(origin, destination);
        new FetchUrl().execute(url);

        // Stop updates once we get location
        fusedLocationClient.removeLocationUpdates(locationCallback);
    }

    public void showMarker(StoreBean store, int layout_resource) {
        LinearLayout tv = (LinearLayout) this.getLayoutInflater().inflate(layout_resource, null, false);
        TextView desc = tv.findViewById(R.id.tv_desc);
        desc.setText(store.getSTORE_NAME());

        tv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());

        tv.setDrawingCacheEnabled(true);
        Bitmap bm = tv.getDrawingCache();

        LatLng loc = new LatLng(Double.parseDouble(store.getLATITUDE()), Double.parseDouble(store.getLONGITUDE()));
        mMap.addMarker(new MarkerOptions()
                .position(loc)
                .anchor(0.5f, 0.5f)
                .icon(BitmapDescriptorFactory.fromBitmap(bm)));
    }

    private String getUrl(LatLng origin, LatLng dest) {
        String str_origin = "origin=" + origin.latitude + "," + origin.longitude;
        String str_dest = "destination=" + dest.latitude + "," + dest.longitude;
        String parameters = str_origin + "&" + str_dest + "&sensor=false";
        String output = "json";
        return "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters + "&key=AIzaSyCPo-giwVv5h8AzsbE1IDRsFsPHjVlCm5A";
    }

    private String downloadUrl(String strUrl) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(strUrl);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.connect();
            iStream = urlConnection.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
        } catch (Exception e) {
            Log.e("Exception", e.toString());
        } finally {
            if (iStream != null) iStream.close();
            if (urlConnection != null) urlConnection.disconnect();
        }
        return sb.toString();
    }

    // AsyncTask to fetch directions
    private class FetchUrl extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... url) {
            try {
                return downloadUrl(url[0]);
            } catch (IOException e) {
                Log.e("Background Task", e.toString());
                return "";
            }
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            Log.d("DirectionsResponse", result);
            new ParserTask().execute(result);
        }
    }

    private class ParserTask extends AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {
        @Override
        protected List<List<HashMap<String, String>>> doInBackground(String... jsonData) {
            try {
                JSONObject jObject = new JSONObject(jsonData[0]);
                DataParser parser = new DataParser();
                return parser.parse(jObject);
            } catch (Exception e) {
                Log.e("ParserTask", e.toString());
                return null;
            }
        }

        @Override
        protected void onPostExecute(List<List<HashMap<String, String>>> result) {
            if (result == null) return;

            ArrayList<LatLng> points;
            PolylineOptions lineOptions = null;

            for (List<HashMap<String, String>> path : result) {
                points = new ArrayList<>();
                lineOptions = new PolylineOptions();

                for (HashMap<String, String> point : path) {
                    double lat = Double.parseDouble(point.get("lat"));
                    double lng = Double.parseDouble(point.get("lng"));
                    points.add(new LatLng(lat, lng));
                }

                lineOptions.addAll(points);
                lineOptions.width(12);
                lineOptions.color(Color.parseColor("#5ac614"));
            }

            if (lineOptions != null) {
                mMap.addPolyline(lineOptions);
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == LOCATION_PERMISSION_REQUEST) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startLocationUpdates();
            } else {
                Toast.makeText(this, "Location permission required", Toast.LENGTH_SHORT).show();
            }
        }
    }
}
