package com.example.tp8;

import android.Manifest;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.exifinterface.media.ExifInterface;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.DiffUtil;


import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class NearbyPhotosActivity extends AppCompatActivity {

    private static final int PERMISSIONS_REQUEST_LOCATION_AND_MEDIA = 101; // ID de la requête de permission

    private FusedLocationProviderClient fusedLocationClient; //https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient
    private Location currentUserLocation;
    private EditText distanceInput;
    private Button searchButton;
    private RecyclerView recyclerView;
    private NearbyPhotoAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nearby_photos);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.photosAroundBackground), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        distanceInput = findViewById(R.id.distance_input); // éléménts de l'interface
        searchButton = findViewById(R.id.search_button);
        recyclerView = findViewById(R.id.nearby_photos_recycler_view);

        // Fournisseur de localisation
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); // Permet de gérer les deux types de position sans devoir y prêter attention

        // RecyclerView qui gère l'affichage des éléments, mits en page selon GridLayoutManager
        recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); // 3 images par ligne dans la grille
        adapter = new NearbyPhotoAdapter(); // Adapteur local pour les photos
        recyclerView.setAdapter(adapter);

        searchButton.setOnClickListener(v -> { // Click bouton recherche
            String distanceStr = distanceInput.getText().toString(); // Je récupère la distance de recherche
            if (currentUserLocation == null) {
                Toast.makeText(this, getText(R.string.no_position_available), Toast.LENGTH_SHORT).show(); // Impossible d'avoir la position de l'utilisateur
                return;
            }
            if (distanceStr.isEmpty()) { // Pas de distance entrée pour le moment
                Toast.makeText(this, getText(R.string.input_distance), Toast.LENGTH_SHORT).show();
                return;
            }
            try { // Et j'essaie de convertir la distance en nombre0
                double maxDistance = Double.parseDouble(distanceStr); // Distance maximale de recherche
                loadNearbyPhotos(maxDistance); // Et je recherche les images dans ce rayon
            } catch (NumberFormatException e) {
                Toast.makeText(this, getText(R.string.invalid_distance), Toast.LENGTH_SHORT).show(); // Nombre invalide
            }
        }); // Fin listener bouton

        checkPermissionsAndGetLocation(); // Au lancement, je vérifie les permissions et obtiens la position
    }

    // Permission et position
    private void checkPermissionsAndGetLocation() {
        List<String> permissionsToRequest = new ArrayList<>(); // Liste des permissions qui seront demandées
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            permissionsToRequest.add(Manifest.permission.ACCESS_FINE_LOCATION); // Si je n'ai pas l'accès à la position précise, je demande
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Suggéré par l'IDE pour gérer tous les cas de versions Android
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
                permissionsToRequest.add(Manifest.permission.READ_MEDIA_IMAGES);
            }
        } else {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                permissionsToRequest.add(Manifest.permission.READ_EXTERNAL_STORAGE);
            }
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_MEDIA_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // Voir la position des images dans les métadonnées
            permissionsToRequest.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
        }

        if (!permissionsToRequest.isEmpty()) {
            // S'il y a des permissions à demander, j'envois la demande
            ActivityCompat.requestPermissions(this, permissionsToRequest.toArray(new String[0]), PERMISSIONS_REQUEST_LOCATION_AND_MEDIA);
        } else {
            fetchUserLocation(); // J'ai tout, je récupère la position de l'utilisateur
        }
    }

    @Override // Résultat de la demande de permission
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSIONS_REQUEST_LOCATION_AND_MEDIA) {
            boolean allGranted = true;
            for (int grantResult : grantResults) { // Vérifier que tous les accès ont été donnés
                if (grantResult != PackageManager.PERMISSION_GRANTED) {
                    allGranted = false;
                    break;
                }
            }
            if (allGranted) {
                fetchUserLocation(); // Tout y est, je récupère la position
            } else {
                Toast.makeText(this, getString(R.string.missing_permission), Toast.LENGTH_LONG).show(); // Il manque des accès
                finish();
            }
        }
    }

    private void fetchUserLocation() { // Récupération de la positon de l'utilisateur
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return; // Echec si je n'ai aucun accès à la position
        }
        fusedLocationClient.getLastLocation() // Récupérer la position
                .addOnSuccessListener(this, location -> {
                    if (location != null) {
                        currentUserLocation = location; // Position récupérée
                        Toast.makeText(this, getString(R.string.position_got), Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(this, getString(R.string.position_error), Toast.LENGTH_LONG).show(); // Erreur
                    }
                });
    }

    private void loadNearbyPhotos(double maxDistance) { // Chargement des photos proches du lieu
        new Thread(() -> { // Dans un nouveau thread
            List<Uri> nearbyPhotos = new ArrayList<>();
            ContentResolver contentResolver = getContentResolver(); // Je prépare de quoi chercher dans le stockage
            Uri queryUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            String[] projection = {MediaStore.Images.Media._ID};

            try (Cursor cursor = contentResolver.query(queryUri, projection, null, null, null)) {
                if (cursor != null) {
                    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID); // Récupérer l'ID de la colone qui gère les images
                    while (cursor.moveToNext()) { // Tant qu'il reste des images à récupérer
                        long id = cursor.getLong(idColumn);
                        Uri photoUri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Long.toString(id)); // Puis je récupère l'URI de l'image

                        try (InputStream inputStream = contentResolver.openInputStream(photoUri)) { // Je tente de récupérer l'ExifInterfacve
                            if (inputStream != null) {
                                ExifInterface exifInterface = new ExifInterface(inputStream);
                                double[] latLong = exifInterface.getLatLong(); // Récupérer les coords
                                if (latLong != null) { // Si elles existent
                                    double distance = distance(currentUserLocation.getLatitude(), currentUserLocation.getLongitude(), latLong[0], latLong[1]);// Calcul distance km
                                    //System.out.println(currentUserLocation.getLatitude()+"/"+currentUserLocation.getLongitude());
                                    //System.out.println(maxDistance);
                                    if (distance <= maxDistance) {
                                        // OK, dans le rayon de recherche
                                        nearbyPhotos.add(photoUri);
                                    }
                                }
                            }
                        } catch (Exception e) {
                            // Impossible de récupérer ExifInterface, je passe cette image
                        }
                    }
                }
            }
            runOnUiThread(() -> { // Une fois que j'ai finit de récupérer les images
                // Dans le thread principal
                adapter.submitList(nearbyPhotos); // J'envois la liste à l'adapter
                if(nearbyPhotos.isEmpty()){
                    Toast.makeText(this, getString(R.string.no_pictures), Toast.LENGTH_SHORT).show();
                }
            });
        }).start();
    }

    // J'aurai aussi pu passer par https://developer.android.com/reference/android/location/Location#distanceBetween(double,%20double,%20double,%20double,%20float[])
    private static double distance(double lat1, double lon1, double lat2, double lon2) {
        double R = 6371; // Rayon de la Terre, utilisé pour https://fr.wikipedia.org/wiki/Formule_de_haversine
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
                        Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
    }

    private static class NearbyPhotoAdapter extends ListAdapter<Uri, NearbyPhotoAdapter.ViewHolder> {
        //Adapteur pour l'affichage en grille des images
        protected NearbyPhotoAdapter() {
            super(DIFF_CALLBACK); // Implémentation du diff callback de https://developer.android.com/reference/androidx/leanback/widget/DiffCallback
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            // Créer l'afifchage en utilisant le layour en grille
             View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.grid_item_photo, parent, false);
             return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.imageView.setImageURI(getItem(position)); // Ajouter une image à une position dans la grille
        }

        public static class ViewHolder extends RecyclerView.ViewHolder {
            public ImageView imageView;
            public ViewHolder(View view) {
                super(view);
                imageView = view.findViewById(R.id.photo_grid_item);
            }
        }
    }

    private static final DiffUtil.ItemCallback<Uri> DIFF_CALLBACK = // Android se sert de cette partie pour comparer les éléments https://developer.android.com/reference/androidx/leanback/widget/DiffCallback
            new DiffUtil.ItemCallback<Uri>() {
                @Override
                public boolean areItemsTheSame(@NonNull Uri oldItem, @NonNull Uri newItem) {
                    return oldItem.equals(newItem);
                }

                @Override
                public boolean areContentsTheSame(@NonNull Uri oldItem, @NonNull Uri newItem) {
                    return oldItem.equals(newItem);
                }
            };
}
