package com.creativeapps.schoolbustracker.ui.activity.main.settings;

import static android.content.Context.LOCATION_SERVICE;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.SwitchPreferenceCompat;

import com.creativeapps.schoolbustracker.R;
import com.creativeapps.schoolbustracker.data.Util;
import com.creativeapps.schoolbustracker.data.network.models.Parent;
import com.creativeapps.schoolbustracker.ui.activity.main.MainActivity;
import com.creativeapps.schoolbustracker.ui.activity.main.MainActivityModel;
import com.google.android.gms.common.api.Status;
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.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.widget.AutocompleteSupportFragment;
import com.google.android.libraries.places.widget.listener.PlaceSelectionListener;

import java.util.Arrays;
import java.util.Locale;

public class ParentHomeFragment extends Fragment implements View.OnClickListener, OnMapReadyCallback,
        LocationListener {

    final String TAG = "ParentHomeFragment";

    //constants to set the mode of the map
    private static final int AUTO = 0;//AUTO: map is adjusted automatically to view both the parent's home and driver locations
    private static final int MANUAL = 1;//MANUAL: settings of the map (zoom level, position, ..) adjusted manually by the user
    //default zoom level for the Google map
    private static final int DEFAULT_ZOOM = 13;
    //view model of the main activity
    private MainActivityModel mViewModel;

    //Google map object
    private GoogleMap mGoogleMap;
    private AutocompleteSupportFragment autocompleteFragment;
    //parent object that holds parent information
    private Parent mParent;

    private Marker mParentHomeMarker;

    private int mViewMode = AUTO;

    private Bundle mBundle;

    private double mLatitude;
    private double mLongitude;

    private String mAddress;

    private ImageView currentLoc, zoomin, zoomout;

    //boolean to indicate if the location access permission granted
    private boolean mLocationPermissionGranted;
    //parent's home location
    private LatLng mParentHomeLocation;

    private LocationManager mLocationManager;

    private boolean mShouldListenForLocationUpdate = true;
    private Marker mParentLocationMarker;
    private boolean mEnableCameraMove = true;
    private boolean isMapZooming;
    private float mZoom = -1;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        //inflate the layout
        View root = inflater.inflate(R.layout.fragment_parent_home, container, false);
        //instantiate the view model object

        mViewModel = ((MainActivity) getActivity()).createViewModel();

        return root;

    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    // This event is triggered soon after onCreateView().
    // Any view setup should occur here.  E.g., view lookups and attaching view listeners.
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {

        //instantiate the location manager object
        mLocationManager = (LocationManager) getActivity().getSystemService(LOCATION_SERVICE);


        Button save_location = view.findViewById(R.id.save_parent_address);
        save_location.setOnClickListener(this);
        MainActivity m = (MainActivity) getActivity();
        //m.title.setText("Pickup/Drop-off Location");
        //get the last saved parent information from the SharedPreference

        mParent = mViewModel.getParent().getValue();

        //start Google map
        SupportMapFragment parentMapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.parent_map);
        parentMapFragment.getMapAsync(this);



        currentLoc = view.findViewById(R.id.getcurrentLocation);
        currentLoc.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // LatLng latLng =new LatLng(mGoogleMap.getLatitude(),mGoogleMap.getMyLocation().getLongitude());
                // updatePickupDropoffMarker(latLng);
                mShouldListenForLocationUpdate = true;
                startLocationUpdates();
            }
        });

        zoomin = view.findViewById(R.id.zoom_in);
        zoomin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                float currentZoom = (mZoom == -1 ? DEFAULT_ZOOM : mZoom);
                mZoom = ++currentZoom;
                mGoogleMap.animateCamera( CameraUpdateFactory.zoomTo( mZoom ) );
            }
        });

        zoomout = view.findViewById(R.id.zoom_out);
        zoomout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                float currentZoom = (mZoom == -1 ? DEFAULT_ZOOM : mZoom);
                mZoom = --currentZoom;
                mGoogleMap.animateCamera( CameraUpdateFactory.zoomTo( mZoom ) );
            }
        });

        String GoogleMapsKey = "";
        ApplicationInfo app;
        try {
            app = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
            mBundle = app.metaData;

            GoogleMapsKey = mBundle.getString("com.google.android.geo.API_KEY");

        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }


        // Initialize Places.
        Places.initialize(getActivity().getApplicationContext(), GoogleMapsKey);

        // Initialize the AutocompleteSupportFragment.
        autocompleteFragment = (AutocompleteSupportFragment) getChildFragmentManager().findFragmentById(R.id.autocomplete_fragment);

        // Specify the types of place data to return.
        // autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
        autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME,Place.Field.LAT_LNG));

        //set text hint
        autocompleteFragment.setHint("enter your home address");

        // to set a filter returning only results with a precise address
        //autocompleteFragment.setTypeFilter(TypeFilter.ADDRESS);

        // Set up a PlaceSelectionListener to handle the response.
        autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
            @Override
            public void onPlaceSelected(Place place) {
                // TODO: Get info about the selected place.
                Log.i("Placex", "Place: " + place.getName() + ", " + place.getLatLng().latitude);

                if(place.getLatLng() != null){
                    mLatitude = place.getLatLng().latitude;
                    mLongitude = place.getLatLng().longitude;
                    mAddress = place.getAddress();
                    adjustMarker(place.getLatLng(), false);
                    //update map with the new location
                    //updatePickupDropoffMarker(place.getLatLng());
                }

            }

            @Override
            public void onError(Status status) {
                // TODO: Handle the error.
                Log.i(TAG, "An error occurred: " + status);
            }
        });


    }


    @Override
    public void onClick(View view) {
        Log.i("xxx", "onClick: " + view.getId());
        //handle clicks on gui elements
        if(view.getId()== R.id.save_parent_address) {
            //if the gui element is the save location button
            if(mLongitude != 0 && mLatitude !=0){
                Log.i("xxx", "call: "+ mLongitude);
                LatLng l =new LatLng(mLatitude, mLongitude);

                ((MainActivity) getActivity()).showHideProgressBar(true);

                mViewModel.updateParentPosition(mParent.getSecretKey(),l);
                mViewModel.getParentLocationUpdateResp().removeObservers(this);
                mViewModel.getParentLocationUpdateResp().observe(this, new updateParentAddressResponseObserver());
            }

        }
    }




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

        // Add a marker in South africa and move the camera
        LatLng parentHomeLocation;

        if (mViewModel.getParent().getValue().getAddress_latitude() != null &&
                mViewModel.getParent().getValue().getAddress_longitude() != null)
        {
            parentHomeLocation = new LatLng(mViewModel.getParent().getValue().getAddress_latitude(),
                    mViewModel.getParent().getValue().getAddress_longitude());
        }
        else {
            parentHomeLocation = new LatLng(30.5595, 22.9375);
        }
        adjustMarker(parentHomeLocation, true);

        // Enable the zoom controls for the map
        //mGoogleMap.getUiSettings().setZoomControlsEnabled(true);

        googleMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() {
            @Override
            public void onMarkerDragStart(Marker marker) {
            }

            @Override
            public void onMarkerDrag(Marker marker) {
            }

            @Override
            public void onMarkerDragEnd(Marker marker) {
                LatLng latLng = marker.getPosition();
                adjustMarker(latLng, true);
            }
        });

        googleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
            @Override
            public void onCameraMoveStarted(int reason) {
                if(reason == REASON_DEVELOPER_ANIMATION)
                {
                    mEnableCameraMove = false;
                }
                else
                {
                    mEnableCameraMove = true;
                }
            }
        });
        googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng point) {

            }
        });

        googleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {

            @Override
            public void onCameraIdle() {

                if(!isMapZooming) {
                    LatLng point = googleMap.getCameraPosition().target;
                    //place the icon on the center of the map
                    Log.d("mLatitude", String.valueOf(point.latitude));
                    Log.d("mLongitude", String.valueOf(point.longitude));
                    adjustMarker(point, true);
                    mEnableCameraMove = true;
                }


            }
        });

        googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {

            @Override
            public void onCameraMove() {
                float current_zoom = mGoogleMap.getCameraPosition().zoom;
                if(current_zoom != mZoom)
                {
                    isMapZooming = true;
                }
                else
                {
                    isMapZooming = false;
                }
                mZoom = current_zoom;
                if(mEnableCameraMove && !isMapZooming) {
                    LatLng point = googleMap.getCameraPosition().target;
                    //place the icon on the center of the map
                    Log.d("mLatitude", String.valueOf(point.latitude));
                    Log.d("mLongitude", String.valueOf(point.longitude));
                    adjustMarker(point, false);
                }
            }
        });

        mViewModel.getParentLocation().removeObservers(ParentHomeFragment.this);
        mViewModel.getParentLocation().observe(ParentHomeFragment.this, new ParentLocationObserver());
    }

    private void adjustMarker(final LatLng latLng, final boolean adjustAddress) {

        final Handler handler = new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() {

                CameraPosition cameraPosition = new CameraPosition.Builder()
                        .target(latLng).zoom(mZoom == -1? DEFAULT_ZOOM: mZoom).build();

                mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

                mLatitude = latLng.latitude;
                mLongitude = latLng.longitude;

                updatePickupDropoffMarker(latLng);

                if(adjustAddress) {
                    if(getContext() == null)
                        return;
                    Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault());
                    try {
                        android.location.Address address = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1).get(0);
                        Log.d("sawer", "call: awert " + address.getAddressLine(0));
                        autocompleteFragment.setText(address.getAddressLine(0));

                        mAddress = address.getAddressLine(0);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }

    /*put the pickup/drop-off location of the parent on the map*/
    private void updatePickupDropoffMarker(LatLng parentHomeLocation) {
        if (mGoogleMap == null)
            return;

        if(mParentLocationMarker == null) {
            //define marker options with a home icon
            MarkerOptions marker_option = new MarkerOptions()
                    .position(parentHomeLocation)
                    .title("Your pickup/drop-off location")
                    .flat(true)
                    .draggable(true)
                    //.anchor(0.5f, 0.5f)
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.loc_map));

            mParentLocationMarker = mGoogleMap.addMarker(marker_option);
            mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(parentHomeLocation));
        }
        else
        {
            mParentLocationMarker.setPosition(parentHomeLocation);
        }


    }

    private class updateParentAddressResponseObserver implements Observer<Boolean> {

        @Override
        public void onChanged(Boolean resp) {
            if (resp == null) return;
            //if the process is running
            ((MainActivity) getActivity()).showHideProgressBar(false);

            if(resp==false)
            {
                Util.displayExitMessage("Error", "Address is not valid",
                        ParentHomeFragment.this.getActivity(), false);
            }
            else
            {
                Location location = new Location(LocationManager.GPS_PROVIDER);
                location.setLatitude(mLatitude);
                location.setLongitude(mLongitude);
                mViewModel.setParentLocation(location);

                //mViewModel.setParentAddress(mAddress);

                mParent.setAddress_latitude(location.getLatitude());
                mParent.setAddress_longitude(location.getLongitude());
                //mParent.setAddress(mAddress);

                mViewModel.setParent(mParent);

                Toast.makeText(getContext(), "Home address updated successfully", Toast.LENGTH_SHORT).show();
                getActivity().onBackPressed();

            }
            mViewModel.setParentLocationUpdateResp(null);
        }
    }


    void StartFragment(Fragment fragment) {
        getFragmentManager().beginTransaction().replace(R.id.nav_host_fragment_main,
                fragment).commit();
    }



    /*start listening for location change of the driver. Note that, this function is executed in
the main activity so that the app track the driver location regardless the fragment displayed*/
    public void startLocationUpdates() {
        //check if GPS is enabled
        if (!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            //if not, display an Alert message that may take the user to the settings to enable
            // the GPS
            buildAlertMessageNoGps();
        } else {
            try {
                //check if location access permission is granted
                if (mLocationPermissionGranted) {
                    //turn spinner on until receiving the location update
                    ((MainActivity)getActivity()).showHideProgressBar(true);
                    //if so, start listening for location change

                    mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, (float) 0, this);
                    mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, (float) 0, this);

                    //mLocationManager.requestLocationUpdates(android.location.LocationManager.GPS_PROVIDER, 0, 0, this);
                } else {
                    //otherwise, request a location access permission
                    getLocationPermission();
                }
            } catch (SecurityException e) {
                Log.e("Exception: %s", e.getMessage());
            }
        }
    }

    /*stop listening for location change*/
    public void stopLocationUpdates() {
        mLocationManager.removeUpdates(this);
    }

    /*display an alert that take the user to the settings of his device to turn the GPS on*/
    private void buildAlertMessageNoGps() {
        final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setMessage(R.string.gps_disabled)
                .setCancelable(false)
                .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(@SuppressWarnings("unused") final DialogInterface dialog, @SuppressWarnings("unused") final int id) {
                        startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
                    }
                })
                .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
                    public void onClick(final DialogInterface dialog, @SuppressWarnings("unused") final int id) {
                        dialog.cancel();
                    }
                });
        final AlertDialog alert = builder.create();
        alert.show();
    }

    /*Request location permission, so that we can get the location of the device. The result of
    the permission request is handled by a callback, onRequestPermissionsResult.*/
    private void getLocationPermission() {

        //check if the location access permission is granted
        if (ContextCompat.checkSelfPermission(getContext(),
                android.Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            //if so, start listening for location change
            mLocationPermissionGranted = true;
            startLocationUpdates();
        } else {
            //otherwise, request a permission for location access
            requestPermissions(new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                    Util.PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        //The result of the permission request is handled here
        mLocationPermissionGranted = false;
        //check the type of the request
        switch (requestCode) {
            //location access permission request
            case Util.PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //if permission is granted, start listening for location updates
                    mLocationPermissionGranted = true;
                    startLocationUpdates();
                } else {
                    //otherwise, display message to the user to turn on his location and finish the app
                    Util.displayExitMessage(getString(R.string.alert), getString(R.string.grant_access_to_location_access),
                            getActivity(), false);
                }
                break;
            }
        }
    }

    //this is the listener for location updates
    @Override
    public void onLocationChanged(Location location) {
        if(mShouldListenForLocationUpdate)
        {
            //stop spinner
            ((MainActivity)getActivity()).showHideProgressBar(false);

            if (location == null)
                return;

            mViewModel.setParentLocation(location);
            stopLocationUpdates();
            mShouldListenForLocationUpdate = false;
        }
    }

    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {

    }

    @Override
    public void onProviderEnabled(String s) {

    }

    @Override
    public void onProviderDisabled(String s) {

    }

    /*observe changes for the response of setting the parents alert zone distance*/
    private class ParentLocationObserver implements Observer<Location> {

        @Override
        public void onChanged(@Nullable Location location) {
            if (location == null) return;

            //otherwise, stop the spinner
            ((MainActivity)getActivity()).showHideProgressBar(false);

            LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
            CameraPosition cameraPosition = new CameraPosition.Builder()
                    .target(ll).zoom(DEFAULT_ZOOM).build();

            adjustMarker(ll, true);



            mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));



        }
    }

    /*observe changes for the response of setting the home location of the parent*/
    private class ParentLocationUpdateIsRunning implements Observer<Boolean> {

        @Override
        public void onChanged(@Nullable Boolean parentLocationUpdateIsRunning) {
            if (parentLocationUpdateIsRunning == null) return;

            if (parentLocationUpdateIsRunning) {
                //if setting the location is running, display the progress spinner
                ((MainActivity)getActivity()).showHideProgressBar(true);
            }
            else
            {
                //otherwise, stop the spinner
                ((MainActivity)getActivity()).showHideProgressBar(false);
                //check if the server returns OK status
                if (mViewModel.getParentLocationUpdateResp().getValue()) {
                    //if so, stop requesting location updates
                    stopLocationUpdates();
                    //update the parent's home location in the parent object
                    mParent.setAddress_latitude(mParentHomeLocation.latitude);
                    mParent.setAddress_longitude(mParentHomeLocation.longitude);
                    //save the updated parent object
                    Util.saveObjectToSharedPreference(getContext(),
                            "mPreference", "Parent", mParent);

                    //display a success message
                    Util.displayExitMessage(getString(R.string.success), getString(R.string.pickup_dropoff_ok), getActivity(), false);
                } else {
                    Util.displayExitMessage(getString(R.string.alert), getString(R.string.unexpected_error), getActivity(), false);
                }
                mViewModel.setParentLocationUpdateIsRunning(null);
            }
        }
    }

}