package com.aplusinformatique.aplusmanager.Services;

import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;

import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;

import com.aplusinformatique.aplusmanager.Classes.Destinataire;
import com.aplusinformatique.aplusmanager.R;
import com.aplusinformatique.aplusmanager.Tools.Global;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.gson.JsonObject;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.ion.Ion;

public class DebutGeolocalisation extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    private static final String TAG = "distrimobilelog_Geoloc";

    private final int INTERVAL_DEFAULT = 10;
    private final int INTERVAL_GEOFENCING_DEFAULT = 20;
    private final String actionAccuseReception = "com.aplusinformatique.aplusmanager.accusereception";
    //Broadcast receiver Fin Service
    private final String actionFinService = "com.aplusinformatique.aplusmanager.fingeolocalisation";
    private final int ID_NOTIFICATION = 1001;
    Handler h = new Handler();
    Runnable r;
    Handler hDestinataires = new Handler();
    Runnable rDestinataires;
    private int interval;
    private int interval_geofencing;
    private int accuseReception = 0;
    private BroadcastReceiver receiverAccuseReception;
    private boolean firstLocation = true;
    private Location lastLocation = null;
    private boolean DeclencherFinService = false;
    private GoogleApiClient mGoogleApiClient;
    private PowerManager.WakeLock mWakeLock;
    private LocationRequest mLocationRequest;
    private boolean mInProgress;
    private Boolean servicesAvailable = false;
    private BroadcastReceiver receiverFinService;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
        Notification notification = notificationBuilder.setOngoing(true)
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle("Géolocalisation V1")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .build();

        startForeground(ID_NOTIFICATION, notification);

        IntentFilter filterFinService = new IntentFilter();
        filterFinService.addAction(actionFinService);

        receiverFinService = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                FinService();
            }
        };
        registerReceiver(receiverFinService, filterFinService);

        if (intent != null) {
            if (intent.getExtras() != null) {
                if (intent.getExtras().containsKey("interval")) {
                    this.interval = intent.getExtras().getInt("interval", INTERVAL_DEFAULT);
                } else {
                    this.interval = INTERVAL_DEFAULT;
                }

                if (intent.getExtras().containsKey("interval_geofencing")) {
                    this.interval_geofencing = intent.getExtras().getInt("interval_geofencing", INTERVAL_GEOFENCING_DEFAULT);
                } else {
                    this.interval_geofencing = INTERVAL_GEOFENCING_DEFAULT;
                }
            } else {
                this.interval = INTERVAL_DEFAULT;
                this.interval_geofencing = INTERVAL_GEOFENCING_DEFAULT;
            }
        } else {
            this.interval = INTERVAL_DEFAULT;
            this.interval_geofencing = INTERVAL_GEOFENCING_DEFAULT;
        }

        IntentFilter filter = new IntentFilter();
        filter.addAction(actionAccuseReception);

        receiverAccuseReception = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                accuseReception = 0;
            }
        };
        registerReceiver(receiverAccuseReception, filter);

        mInProgress = false;

        mLocationRequest = LocationRequest.create();

        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        mLocationRequest.setInterval(5000);
        mLocationRequest.setFastestInterval(1000);

        servicesAvailable = servicesConnected();

        setUpLocationClientIfNeeded();

        Log.d("x", "Début géolocalisation !");

        h.postDelayed(r = new Runnable() {
            public void run() {

                envoiPosition();
                Log.d("nombre de destinataires", "" + Global.destinaires.size());
                for (Destinataire d : Global.destinaires) {
                    Log.d("string destinataire", d.toString());
                }

                if (DeclencherFinService) {
                    FinService();
                } else {
                    h.postDelayed(this, interval * 1000);
                }
            }
        }, interval * 1000);

        hDestinataires.postDelayed(rDestinataires = new Runnable() {
            public void run() {
                Log.d("distrimobile", "Boucle detection geofencing !");

                if (lastLocation != null) {
                    for (Destinataire d : Global.destinaires) {
                        Log.d("analyse", d.toString());

                        if (d.getDistance() != 0) {
                            analyserDistance(d);
                        } else if (d.getTemps() != 0) {
                            analyserTemps(d);
                        }
                    }
                }

                if (DeclencherFinService == false) {
                    hDestinataires.postDelayed(this, interval_geofencing * 1000);
                }
            }
        }, interval_geofencing * 1000);
    }

    private void analyserDistance(final Destinataire d) {
        String s = "https://maps.googleapis.com/maps/api/directions/json?mode=driving&origin=" + lastLocation.getLatitude() + "," + lastLocation.getLongitude() + "&destination=" + d.getLatitude() + "," + d.getLongitude(); //+ "&key=AIzaSyBFJGdjukbpH0uK0NPYjxdKSag7EY_5P6M";

        Ion.with(getApplicationContext())
                .load(s)
                .asJsonObject()
                .setCallback(new FutureCallback<JsonObject>() {
                    @Override
                    public void onCompleted(Exception e, JsonObject result) {
                        if (result != null) {
                            Log.d("reponsegoogle", result.toString());

                            if (result.get("status").getAsString().equals("OK")) {
                                String distance = (((JsonObject) result.getAsJsonArray("routes").get(0)).getAsJsonArray("legs").get(0)).getAsJsonObject().getAsJsonObject("distance").get("value").getAsString();
                                //String tempssecondes = (((JsonObject) result.getAsJsonArray("routes").get(0)).getAsJsonArray("legs").get(0)).getAsJsonObject().getAsJsonObject("duration").get("value").getAsString();

                                Log.d("rep google", d.getId() + " , distance : " + distance);

                                if (Integer.valueOf(distance) <= d.getDistance()) {
                                    enApproche(d.getId(), "distance", distance);
                                }
                            }
                        }
                    }
                });
    }

    private void analyserTemps(final Destinataire d) {
        String s = "https://maps.googleapis.com/maps/api/directions/json?mode=driving&origin=" + lastLocation.getLatitude() + "," + lastLocation.getLongitude() + "&destination=" + d.getLatitude() + "," + d.getLongitude(); //+ "&key=AIzaSyBFJGdjukbpH0uK0NPYjxdKSag7EY_5P6M";

        Ion.with(getApplicationContext())
                .load(s)
                .asJsonObject()
                .setCallback(new FutureCallback<JsonObject>() {
                    @Override
                    public void onCompleted(Exception e, JsonObject result) {
                        if (result != null) {
                            Log.d("reponsegoogle", result.toString());

                            if (result.get("status").getAsString().equals("OK")) {
                                //String distance = (((JsonObject) result.getAsJsonArray("routes").get(0)).getAsJsonArray("legs").get(0)).getAsJsonObject().getAsJsonObject("distance").get("value").getAsString();
                                String tempssecondes = (((JsonObject) result.getAsJsonArray("routes").get(0)).getAsJsonArray("legs").get(0)).getAsJsonObject().getAsJsonObject("duration").get("value").getAsString();

                                Log.d("rep google", d.getId() + " , temps : " + tempssecondes);

                                if (Integer.valueOf(tempssecondes) <= d.getTemps()) {
                                    enApproche(d.getId(), "temps", tempssecondes);
                                }
                            }
                        }
                    }
                });
    }

    private void enApproche(String id, String mode, String valeur) {
        Log.d("EN APPROCHE", "id : " + id + " , mode : " + mode);

        Intent replyIntent = new Intent();
        replyIntent.setAction("com.aplusinformatique.aplusmanager.alertegeofencing");
        replyIntent.putExtra("id_destinataire", id);
        replyIntent.putExtra("mode", mode);
        replyIntent.putExtra("valeur", valeur);
        sendBroadcast(replyIntent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);

        if (this.mWakeLock == null) {
            this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
        }

        if (!this.mWakeLock.isHeld()) {
            this.mWakeLock.acquire();
        }

        if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
            return START_STICKY;

        setUpLocationClientIfNeeded();

        if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
            mInProgress = true;
            mGoogleApiClient.connect();
        }

        return START_STICKY;
    }

    private void setUpLocationClientIfNeeded() {
        if (mGoogleApiClient == null)
            buildGoogleApiClient();
    }

    protected synchronized void buildGoogleApiClient() {
        this.mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    private boolean servicesConnected() {
        GoogleApiAvailability api = GoogleApiAvailability.getInstance();
        int resultCode = api.isGooglePlayServicesAvailable(this);

        if (ConnectionResult.SUCCESS == resultCode) {
            return true;
        } else {
            return false;
        }
    }

    private void envoiPosition() {
        if (lastLocation != null && lastLocation.getLatitude() != 0) {
            Log.d(TAG, "position gps latitude : " + lastLocation.getLatitude() + " longitude : " + lastLocation.getLongitude() + " precision : " + lastLocation.getAccuracy() + " provider : " + lastLocation.getProvider());

            Intent replyIntent = new Intent();
            replyIntent.setAction("com.aplusinformatique.aplusmanager.geolocalisationreponse");
            replyIntent.putExtra("latitude", String.valueOf(lastLocation.getLatitude()));
            replyIntent.putExtra("longitude", String.valueOf(lastLocation.getLongitude()));
            replyIntent.putExtra("speed", String.valueOf(lastLocation.getSpeed()));
            replyIntent.putExtra("accuracy", String.valueOf(lastLocation.getAccuracy()));
            sendBroadcast(replyIntent);
        }

        Log.d(TAG, "EnvoiPosition DebutGeolocalisation");
        accuseReception += 1;
        Log.d(TAG, "DebutGeolocalisation compteur accuse réception : " + String.valueOf(accuseReception));
        if (accuseReception > 2) {
            Log.d(TAG, "DebutGeolocalisation accusés sans réponses");
            DeclencherFinService = true;
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location != null) {
            lastLocation = location;

            if (firstLocation) {
                firstLocation = false;

                envoiPosition();
            }

            Log.d("manager", "nouvelle location : " + location.toString());
        }
    }

    @Override
    public void onConnected(Bundle bundle) {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient, mLocationRequest, this);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        mInProgress = false;

        mGoogleApiClient = null;

        Log.d("xxx", "connection suspendue");
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        mInProgress = false;
    }

    @Override
    public void onDestroy() {
        this.mInProgress = false;

        Log.d(TAG, "onDestroy service DebutGeolocalisation");

        if (h != null) {
            h.removeCallbacks(r);
        }
        if (hDestinataires != null) {
            hDestinataires.removeCallbacks(rDestinataires);
        }

        if (this.servicesAvailable && this.mGoogleApiClient != null) {
            this.mGoogleApiClient.unregisterConnectionCallbacks(this);
            this.mGoogleApiClient.unregisterConnectionFailedListener(this);
            this.mGoogleApiClient.disconnect();

            this.mGoogleApiClient = null;
        }

        if (this.mWakeLock != null) {
            this.mWakeLock.release();
            this.mWakeLock = null;
        }

        if (receiverAccuseReception != null) {
            unregisterReceiver(receiverAccuseReception);
            receiverAccuseReception = null;
        }

        if (receiverFinService != null) {
            unregisterReceiver(receiverFinService);
            receiverFinService = null;
        }


        super.onDestroy();
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private String createNotificationChannel(NotificationManager notificationManager) {
        String channelId = TAG;
        String channelName = "Géolocalisation V1";
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
        // omitted the LED color
        channel.setImportance(NotificationManager.IMPORTANCE_LOW);
        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        notificationManager.createNotificationChannel(channel);
        return channelId;
    }

    private void FinService() {
        stopForeground(true);
        stopSelf();
    }
}