package com.cc.infosur.routing;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.osmdroid.util.GeoPoint;

import com.cc.infosur.greendao.Alert;
import com.cc.infosur.greendao.AlertDao;
import com.cc.infosur.greendao.DaoMaster;
import com.cc.infosur.greendao.DaoSession;
import com.cc.infosur.greendao.DayDao;
import com.cc.infosur.greendao.DayServiceRelDao;
import com.cc.infosur.greendao.DbPoint;
import com.cc.infosur.greendao.DbPolygon;
import com.cc.infosur.greendao.LinkDao;
import com.cc.infosur.greendao.MonthOpeningTime;
import com.cc.infosur.greendao.MonthOpeningTimeDao;
import com.cc.infosur.greendao.Node;
import com.cc.infosur.greendao.NodeDao;
import com.cc.infosur.greendao.ParkInfo;
import com.cc.infosur.greendao.ParkInfoDao;
import com.cc.infosur.greendao.Question;
import com.cc.infosur.greendao.Questionary;
import com.cc.infosur.greendao.QuestionaryDao;
import com.cc.infosur.greendao.Request;
import com.cc.infosur.greendao.RequestDao;
import com.cc.infosur.greendao.DaoMaster.DevOpenHelper;
import com.cc.infosur.main.MainActivity;
import com.cc.infosur.main.MockLocationProvider;

import com.cc.infosur.map.InfoTabFragment;
import com.cc.infosur.map.MapFragment;
import com.cc.infosur.polygon.Point;
import com.cc.infosur.polygon.Polygon;

import com.cc.infosur.R;

import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.sqlite.SQLiteDatabase;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat.Builder;
import android.util.Log;
import android.net.ConnectivityManager;



public class GPSLoggerService extends Service {

    private final DecimalFormat sevenSigDigits = new DecimalFormat("0.#######");
    private final DateFormat timestampFormat = new SimpleDateFormat("yyyyMMddHHmmss");

    private LocationManager lm;
    //private LocationListener locationListener;

    ///private static long minTimeMillis = 20000;  // 20 SECONDS FOR TESTING
    private static long minTimeMillis = 30000;  // 20 SECONDS FOR TESTING
    private static long minDistanceMeters = 0;
    private static float minAccuracyMeters = 50;

    private int lastStatus = 0;
    private static boolean showingDebugToast = true;
    private String imageRepository = "http://sensa-demo.tripalacarte.com/upload/uplodedImages/";

    private static final String tag = "GPSLoggerService";

    private Network network;

    private SQLiteDatabase db;
    private DevOpenHelper helper;
    private DaoSession daoSession;
    private DaoMaster daoMaster;
    private ParkInfoDao parkInfoDao;
    private QuestionaryDao questionaryDao;
    private AlertDao alertDao;
    private MonthOpeningTimeDao monthOpeningTimeDao;
    private RequestDao requestDao;
    private NodeDao nodeDao;
    private LinkDao linkDao;

    private String provider;

    DbPolygon parkLimits;
    List<DbPolygon> restrictedAreas;
    List<DbPolygon> accommodationAreas;
    List<Questionary> questionaries;

    Polygon parkLimitsPolygon;
    List<Polygon> restrictedAreasPolygons;
    List<Polygon> accommodationAreasPolygons;

    MainActivity mainActivity;

    Date restrictedAreaTravellerTime = null;
    Date restrictedAreaParkManagerTime = null;

    Date timeOutTravellerTime = null;
    Date timeOutParkManagerTime = null;

    int timeout_geofencing_traveller;
    int timeout_geofencing_park_manager;
    int timeout_timefencing_traveller;
    int timeout_timefencing_park_manager;

    long month_opening_time;
    Date open_from;
    Date open_to;

    long minutesremaining = 0;
    long minutesremainingPark = 0;

    SimpleDateFormat dateFormat = new SimpleDateFormat("y-MM-d HH:mm");

    Boolean timeout_timefencing_trav = false;
    Boolean timeout_timefencing_park = false;
    Boolean timeout_timefencing_trav_still = false;
    boolean timeout_geofencing_trav_still = false;
    boolean parkManagerWarned = false;

    // Worker Execution Code Time Specified
    private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();


    /** Called when the activity is first created. */
    private void startLoggerService() {

        Log.i("LOC", "Creating network");

        network = new Network(getBaseContext());

        // ---use the LocationManager class to obtain GPS locations---
        lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        Criteria criteria = new Criteria();
        provider = lm.getBestProvider(criteria, false);

        // InfoTabFragment will be used to know if the Fake Location has been asked or the Current Location
        InfoTabFragment infoTabFragment = new InfoTabFragment();
        if( infoTabFragment.getFakeLocation() ) {
            lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTimeMillis, minDistanceMeters, locationListener);
        } else {
            lm.requestLocationUpdates(provider, minTimeMillis, minDistanceMeters, locationListener);
        }

        //lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTimeMillis, minDistanceMeters, locationListener);

        // Prepare db
        helper = new DevOpenHelper(this, "tripdb", null);
        db = helper.getWritableDatabase();
        daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
        parkInfoDao = daoSession.getParkInfoDao();
        questionaryDao = daoSession.getQuestionaryDao();
        monthOpeningTimeDao = daoSession.getMonthOpeningTimeDao();

        ParkInfo parkInfo = parkInfoDao.loadAll().get(0);
        // OUT OF TRACK - RESTRICTED AREAS
           timeout_geofencing_traveller = parkInfo.getTimeoutGeofencingTraveller();              // The traveler will be warn 5 minutes after he has been localised to be on a restricted area
           timeout_geofencing_park_manager = parkInfo.getTimeoutGeofencingParkmanager();         // The park manager will be warn 10 minutes after a traveler has been localised to be on a restricted area

        // OUT OF TIME - PARK CLOSED
           timeout_timefencing_traveller = parkInfo.getTimeoutTimefencingTraveller();             // The traveler will be warn 1 hour before the closing time of the park
           timeout_timefencing_park_manager = parkInfo.getTimeoutTimefencingParkmanager();        // The park manager will be warn 10 minutes after the closing time if there is/are still traveler(s) outside the park

        // For testing
            // OUT OF TRACK - RESTRICTED AREAS
                 // timeout_geofencing_traveller = 5;              // The traveler will be warn 5 minutes after he has been localised to be on a restricted area
                 // timeout_geofencing_park_manager = 10;           // The park manager will be warn 10 minutes after a traveler has been localised to be on a restricted area
            // OUT OF TIME - PARK CLOSED
                 // timeout_timefencing_traveller = 60;             // The traveler will be warn 1 hour before the closing time of the park
                 // timeout_timefencing_park_manager = 20;          // The park manager will be warn 10 minutes after the closing time if there is/are still traveler(s) outside the park

        Polygon.Builder limitsBuilder = new Polygon.Builder();
        parkLimits = parkInfo.getBoundary();
        for (DbPoint p : parkLimits.getPoints()) {
            limitsBuilder
                    .addVertex(new Point(p.getLongitude(), p.getLatitude()));
        }
        parkLimitsPolygon = limitsBuilder.build();

        accommodationAreasPolygons = new ArrayList<Polygon>();
        accommodationAreas = parkInfo.getAccommodationAreas();
        for (DbPolygon pol : accommodationAreas) {
            Polygon.Builder accBuilder = new Polygon.Builder();
            for (DbPoint p : pol.getPoints()) {
                accBuilder.addVertex(new Point(p.getLongitude(), p
                        .getLatitude()));
            }
            accommodationAreasPolygons.add(accBuilder.build());
        }

        restrictedAreasPolygons = new ArrayList<Polygon>();
        restrictedAreas = parkInfo.getRestrictedAreas();
        for (DbPolygon pol : restrictedAreas) {
            Polygon.Builder restBuilder = new Polygon.Builder();
            for (DbPoint p : pol.getPoints()) {
                restBuilder.addVertex(new Point(p.getLongitude(), p
                        .getLatitude()));
                //System.out.println(" 1111111--------------------------getLatitude = "+p.getLatitude());
                //System.out.println(" 22222--------------------------getLatitude = "+p.getLatitude());
        }


            restrictedAreasPolygons.add(restBuilder.build());
        }

        questionaries = questionaryDao.loadAll();
    }



    /**
     * Function shutdownLoggerService()
     */
    private void shutdownLoggerService() {
        lm.removeUpdates(locationListener);
    }



    /**
     * Class MyLocationListener
     */
    //public class MyLocationListener implements LocationListener {
    private final LocationListener locationListener = new LocationListener() {

        public void onLocationChanged(Location loc) {


            System.out.println("LOCATION CHANGED TEST");
            // In case if the fake location has been asked - Get those coordinates
            InfoTabFragment infoTabFragment = new InfoTabFragment();
            try {
            if( infoTabFragment.getFakeLocation() ) {
                loc.setLatitude(MockLocationProvider.getLatitude());
                loc.setLongitude(MockLocationProvider.getLongitude());
            }

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

            ConnectivityManager manager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
            boolean is3g = false;
            // Mobile
            try {
                is3g = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnectedOrConnecting();

            } catch (Exception e) {
                is3g = false;
                e.printStackTrace();
            }
            // Wifi
            boolean isWifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();

//loc.setLatitude(41.78821622506477);
//loc.setLongitude(-8.07561000663754);


//loc.setLatitude(49.502663299999995);
//loc.setLongitude(5.9408389);

//loc.setLatitude(49.500896999999995);
//loc.setLongitude(5.946425);


            //49.500897,5.946425,0

            //49.500896999999995
            //5.946425

            //49.500921,5.946393,0



            if (loc != null) {

 //showNotification("Lat-Long", Double.toString(loc.getLatitude())+"3#"+Double.toString(loc.getLongitude()));

 //System.out.println("555555555555555555 Lat-Long ="+Double.toString(loc.getLatitude())+"3#"+Double.toString(loc.getLongitude()));

                boolean pointIsRecorded = false;
                try {
                    System.out.println(" ====================================== ");
                    Log.i("LOC", "Receive location: " + loc.getAccuracy());
                    System.out.println("latitude :: " + loc.getLatitude());
                    System.out.println("longitude :: " + loc.getLongitude());

                    // Steven : In case, if we are not inside a park, stop the process/loop of this function
                    /*if (!parkLimitsPolygon.contains(new Point(loc
                            .getLongitude(), loc.getLatitude()))) {
                        return;
                    }*/

                    boolean inAccommodationArea = false;
                    for (Polygon pol : accommodationAreasPolygons) {
                        if (pol.contains(new Point(loc.getLongitude(), loc
                                .getLatitude()))) {
                            inAccommodationArea = true;
                            break;
                        }
                    }

                    // ======================================== //
                    // 				== REQUESTS == 				//
                    // ======================================== //
                    // If there No 3G && No WiFi
                    if(!is3g && !isWifi) {
                        // Do Nothing
                    } else {
                        checkRequest();
                        // checkNewQuestions();
                    }


                    // Check if is in a restricted area
                    boolean inRestrictedArea = false;
                    if (!inAccommodationArea) {


    System.out.println(" 6666666666666666666666666 inRestrictedArea :: " + inRestrictedArea);

                        for (Polygon pol : restrictedAreasPolygons) {

                            if (pol.contains(new Point(loc.getLongitude(), loc
                                    .getLatitude()))) {

                                inRestrictedArea = true;
                                break;
                            }
                        }

  System.out.println(" 777777777777777777777 oc.getLongitude() :: " + loc.getLongitude());
  System.out.println(" 8888888888888888888 loc.getLongitude() :: " + loc.getLatitude());

  System.out.println(" 99999999999999999 inRestrictedArea :: " + inRestrictedArea);

                        // ============================================ //
                        // 				== TIME FENCING == 				//
                        // ============================================ //

                        // FORMAT
                            DateFormat formatTime = new SimpleDateFormat("HH:mm:ss");
                        // CURRENT TIME
                            Calendar currentTime = Calendar.getInstance();
                        // INTEGER - CURRENT MONTH
                            int monthcurrent = currentTime.get(Calendar.MONTH);

                        // Get data from "MonthOpeningTime" table of the current month (monthcurrent)
                            MonthOpeningTime monthOpening = monthOpeningTimeDao.loadAll().get(monthcurrent);
                            open_from = monthOpening.getOpenFrom();
                            open_to = monthOpening.getOpenTo();

                        // OPENING TIME
                            Calendar opening = Calendar.getInstance();
                            opening.setTime(open_from);
                            int hoursOpen = opening.getTime().getHours();
                            int minutesOpen = opening.getTime().getMinutes();
                            int secondsOpen = opening.getTime().getSeconds();

                            Calendar openingTime = Calendar.getInstance();
                            openingTime.set(Calendar.HOUR_OF_DAY, hoursOpen);
                            openingTime.set(Calendar.MINUTE, minutesOpen);
                            openingTime.set(Calendar.SECOND, secondsOpen);

                        // CLOSING TIME
                            Calendar closing = Calendar.getInstance();
                            closing.setTime(open_to);
                            int hoursClose = closing.getTime().getHours();
                            int minutesClose = closing.getTime().getMinutes();
                            int secondsClose = closing.getTime().getSeconds();

                            Calendar closingTime = Calendar.getInstance();
                            closingTime.set(Calendar.HOUR_OF_DAY, hoursClose);
                            closingTime.set(Calendar.MINUTE, minutesClose);
                            closingTime.set(Calendar.SECOND, secondsClose);

                            // For testing
                                //closingTime.set(Calendar.HOUR_OF_DAY, 11);
                                //closingTime.set(Calendar.MINUTE, 15);
                                // closingTime.set(Calendar.SECOND, secondsClose);

                            System.out.println(" |||||||| currentTime :: " + currentTime.getTime());
                            System.out.println(" |||||||| openingTime :: " + openingTime.getTime());
                            System.out.println(" |||||||| closingTime :: " + closingTime.getTime());

                            System.out.println(" #################################### isTheParkOpened :: " + isTheParkOpened(currentTime, openingTime, closingTime) );

                        // == [BEFORE CLOSING PARK] ==
                        if(isTheParkOpened(currentTime, openingTime, closingTime)) {

                            System.out.println(" °°°°°°°°° The Park Opened °°°°°°°°° " + isTheParkOpened(currentTime, openingTime, closingTime));


                            // == WARN THE TRAVELER ==
                            if (!timeout_timefencing_trav) {

                                // TRAVELER
                                long diff = closingTime.getTimeInMillis() - currentTime.getTimeInMillis();
                                Boolean positiveTraveler = diff > 0;

                                if(positiveTraveler) {
                                    minutesremaining = TimeUnit.MILLISECONDS.toMinutes(diff);
                                    minutesremaining = (minutesremaining < 0 ? -minutesremaining : minutesremaining);
                                    minutesremaining = minutesremaining - timeout_timefencing_traveller;
                                } else {
                                    minutesremaining = 0;
                                }

                                System.out.println("°°°°°°° TRAVELLER °°°°°°°");
                                System.out.println("======= minutesremaining :: " + minutesremaining);
                                System.out.println("******* timeout_timefencing_traveller :: " + timeout_timefencing_traveller);

                                // Time reached : send Notification to the traveler that the park is nearly going to to close
                                if (minutesremaining == 0 && !timeout_timefencing_trav) {

                                    System.out.println("°°°°°°° Time Fencing reached : TRAVELER !");
                                    System.out.println("°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°");

                                    timeout_timefencing_trav = true;

                                    // Warn the user
                                    showNotification("Caution - Park Closing",
                                            "The park is nearly going to close. Please go to your next accommodation.");


                                    if (mainActivity != null && MainActivity.isActivityVisible()) {

                                        // MapFragment mapFragment = (MapFragment) mainActivity.mStacks.get("MAP_TAB").firstElement();
                                        // mapFragment.showTimeFencingDialog();

                                        infoTabFragment.showTimeFencingDialog();

                                    }

                                }

                            }

                            // == WARN THE PARK MANAGER ==
                            if (!timeout_timefencing_park) {

                                // PARK MANAGER
                                long diffPark = closingTime.getTimeInMillis() - currentTime.getTimeInMillis();
                                Boolean positiveManager = diffPark > 0;

                                if(positiveManager) {
                                    minutesremainingPark = TimeUnit.MILLISECONDS.toMinutes(diffPark);
                                    minutesremainingPark = (minutesremainingPark < 0 ? -minutesremainingPark : minutesremainingPark);
                                    minutesremainingPark = minutesremainingPark - timeout_timefencing_park_manager;
                                } else {
                                    minutesremainingPark = 0;
                                }

                                System.out.println("°°°°°°° PARK MANAGER °°°°°°°");
                                System.out.println("======= minutesremainingPark :: " + minutesremainingPark);
                                System.out.println("******* timeout_timefencing_park_manager :: " + timeout_timefencing_park_manager);

                                // Time reached : send to EPP
                                if (minutesremainingPark == 0 && !timeout_timefencing_park) {

                                    System.out.println("°°°°°°° Time Fencing reached :: PARK MANAGER !");
                                    System.out.println("°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°");

                                    timeout_timefencing_park = true;

                                    // Warn the park manager
                                    if(!is3g && !isWifi) {

                                        Log.v("############################################################# TIMEFENCING TO PARK MANAGER", "no wifi");


                                    // There is WiFi or 3G/4G
                                    } else {

                                        Log.v("############################################################# TIMEFENCING TO PARK MANAGER", "with wifi");

                                        // Warn the park manager
                                        String request = "http://144.76.175.87/sensa/etp/sendTravellerOutOfTime.php";
                                        request += "?travellerid=" + MainActivity.getTravellerId();
                                        request += "&parkid=" + MainActivity.getEppParkId();
                                        request += "&time="
                                                + URLEncoder
                                                .encode("'"
                                                        + dateFormat.format(Calendar
                                                        .getInstance()
                                                        .getTime())
                                                        + "'", "UTF-8")
                                                .replace("+", "%20");

                                        //new SendAckTask().execute(request);

                                    }

                                }

                            }


                        // == [AFTER CLOSING PARK] ==
                        } else {

                            // RE-INITIALIZATION
                            if(timeout_timefencing_trav && timeout_timefencing_park) {

                                System.out.println(" ::::::::  RE-INITIALIZATION :::::::: " );

                                timeout_timefencing_trav = false;
                                timeout_timefencing_park = false;

                            }

                            System.out.println(" °°°°°°°°° :: timeout_timefencing_trav :: " + timeout_timefencing_trav);
                            System.out.println(" °°°°°°°°° :: timeout_timefencing_park :: " + timeout_timefencing_park);

                            if(!timeout_timefencing_trav_still) {

                                // Warn the user one more time if he is still in the park whereas the park is closed
                                showNotification("Warning - Park Closed",
                                        "Be careful, the park is closed. Please go right now to your next accommodation.");

                                timeout_timefencing_trav_still = true;

                                if(!timeout_timefencing_park) {

                                    timeout_timefencing_trav = true;
                                    timeout_timefencing_park = true;

                                    // Warn the park manager
                                    if(!is3g && !isWifi) {

                                        Log.v("############################################################# TIMEFENCING TO PARK MANAGER", "no wifi");


                                    // There is WiFi or 3G/4G
                                    } else {

                                        Log.v("############################################################# TIMEFENCING TO PARK MANAGER", "with wifi");

                                        // Warn the park manager
                                        String request = "http://144.76.175.87/sensa/etp/sendTravellerOutOfTime.php";
                                        request += "?travellerid=" + MainActivity.getTravellerId();
                                        request += "&parkid=" + MainActivity.getEppParkId();
                                        request += "&time="
                                                + URLEncoder
                                                .encode("'"
                                                        + dateFormat.format(Calendar
                                                        .getInstance()
                                                        .getTime())
                                                        + "'", "UTF-8")
                                                .replace("+", "%20");

                                        //new SendAckTask().execute(request);

                                    }



                                }

                            }



                        }


                        // 				== END TIME FENCING == 			//
                        // ============================================ //

                    // In accommodation area
                    } else {

                        System.out.println(" == IN ACCOMMODATION AREA");

                    }


                    checkGeolocalisedQuestions(loc);
                    checkAlerts(loc);

                    // ============================================ //
                    // 			  == RESTRICTED AREAS == 	        //
                    // ============================================ //
                    if (inRestrictedArea) {

                        Log.i("LOC", "Move!");

                        // 1 / 2 - "Load Routing" to download the roads - Need to know the roads to get the closest way

                        Node node = network.getClosest(loc.getLatitude(), loc.getLongitude());


                        if (restrictedAreaTravellerTime == null) {


                            restrictedAreaTravellerTime = new Date(System.currentTimeMillis());
                            restrictedAreaParkManagerTime = new Date(System.currentTimeMillis());

                        } else {

                            Date actualTime = new Date(System.currentTimeMillis());
                            long miliseconds = actualTime.getTime() - restrictedAreaTravellerTime.getTime();
                            Long minutes = miliseconds / 60000;

                            System.out.println("°°°°°°° TRAVELER °°°°°°°");
                            System.out.println(" SENSA :: RESTRICTED AREA :: minutes :: " + minutes );
                            System.out.println(" SENSA :: RESTRICTED AREA :: timeout_geofencing_traveller :: " + timeout_geofencing_traveller );

                            if (minutes.intValue() > timeout_geofencing_traveller) {

                                System.out.println(" SENSA :: RESTRICTED AREA :: " + minutes.intValue() + " :: "+ timeout_geofencing_traveller);
                                restrictedAreaTravellerTime = new Date(System.currentTimeMillis());


                                // Warn the user
                                showNotification("Restricted area",
                                        "You are in a restricted area, please go back");

                                // 2 / 2 - "Load Routing" to download the roads - Need to know the roads to get the closest way
                                // Set the marker on the closest path in which the traveler has to go
                                if(node.getLatitude() != null && node.getLongitude() != null) {

                                    System.out.println(" **** NODE :: Latitude :: " + node.getLatitude() );
                                    System.out.println(" **** NODE :: Longitude :: " + node.getLongitude() );

                                    // MapFragment.setMapMarker(new GeoPoint(node.getLatitude(), node.getLongitude()));
                                    InfoTabFragment.setMapMarker(new GeoPoint(node.getLatitude(), node.getLongitude()));

                                }


                            }

                            miliseconds = actualTime.getTime() - restrictedAreaParkManagerTime.getTime();
                            minutes = miliseconds / 60000;

                            System.out.println("°°°°°°° PARK MANAGER °°°°°°°");
                            System.out.println(" SENSA :: RESTRICTED AREA :: minutes :: " + minutes );
                            System.out.println(" SENSA :: RESTRICTED AREA :: timeout_geofencing_park_manager :: " + timeout_geofencing_park_manager );



                              if (minutes.intValue() > timeout_geofencing_park_manager) {


                                  restrictedAreaParkManagerTime = new Date(System.currentTimeMillis());
                                  parkManagerWarned = true;


                              if (!timeout_geofencing_trav_still) {


                                  timeout_geofencing_trav_still = true;


                                // Warn the park manager

                                if(!is3g && !isWifi) {

                                    Log.v("################################################## GEOFENCING OutOfTrack 1 TO PARK MANAGER", "no wifi");

                                // There is WiFi or 3G/4G
                                } else {

                                // Warn the park manager
                                    Log.v("##################################################### GEOFENCING OutOfTrack  TO PARK MANAGER", "with wifi");



                                }

                            }
                         }
                        }

                    } else {

                        // The traveler is back on good path (ONLY when the traveler was in forbidden area)
                        if (parkManagerWarned) {

                            parkManagerWarned = false;

                            timeout_geofencing_trav_still = false;

                            // Warn the park manager
                            // No WiFi && 3G/4G
                            if(!is3g && !isWifi) {

                                Log.v("############################################################# GEOFENCING ON TRACK AGAIN 2 TO PARK MANAGER", "no wifi");




                            // There is WiFi or 3G/4G
                            } else {

                                // Warn the park manager
                                Log.v("############################################################# GEOFENCING ON TRACK AGAIN TO PARK MANAGER", "with wifi");

                                String request = "http://144.76.175.87/sensa/etp/sendTravellerOnTrackAgain.php";
                                request += "?travellerid="
                                        + MainActivity.getTravellerId();
                                request += "&parkid="
                                        + MainActivity.getEppParkId();
                                request += "&geoloclat=" + loc.getLatitude();
                                request += "&geoloclon=" + loc.getLongitude();
                                request += "&time="
                                        + URLEncoder
                                        .encode("'"
                                                + dateFormat.format(Calendar
                                                .getInstance()
                                                .getTime())
                                                + "'", "UTF-8")
                                        .replace("+", "%20");
                                //new SendAckTask().execute(request);

                            }

                        }
                        restrictedAreaTravellerTime = null;
                        restrictedAreaParkManagerTime = null;
                        Log.i("LOC", "Don't move");
                        // MapFragment.removeMapMarker();
                        InfoTabFragment.removeMapMarker();

                    }

                } catch (Exception e) {
                    Log.e(tag, " CATCH :: " + e.toString());
                } finally {

                }
            }
        }



        /**
         * Function DateToCalendar()
         * @param date
         * @return
         */
        private Calendar DateToCalendar(Date date) {
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            return cal;
        }



        /**
         * Function checkGeolocalisedQuestions()
         * @param loc
         *
         * take all the questions // check location question
         */
        private void checkGeolocalisedQuestions(Location loc) {
            float[] results = null;
            Questionary questionary = null;
            Location qLoc = new Location("");

            for (Questionary q : questionaries) {
                qLoc.setLatitude(q.getLatitude());
                qLoc.setLongitude(q.getLongitude());
                Float dist = loc.distanceTo(qLoc);
                if (dist.intValue() < q.getRadius()) {
                    if (q.getActivated())
                        // was activated;
                        q.setActivated(true);
                    questionary = q;
                } else {
                    q.setActivated(false);
                }
                questionaryDao.update(q);
            }

            mainActivity = MainActivity.getInstance();
        }



        /**
         * Function isTheParkOpened()
         * @param currentTime
         * @param openingTime
         * @param closingTime
         * @return
         *
         * Return a boolean if the Current Time is between the Opening Time and the Closing Time
         */
        private boolean isTheParkOpened(Calendar currentTime, Calendar openingTime, Calendar closingTime) {

            // Current Time
            Date cur = currentTime.getTime();

            // Opening Time
            Date opn = openingTime.getTime();

            // Closing Time
            Date cls = closingTime.getTime();


            return (cur.after(opn) && cur.before(cls));
        }



        /**
         * Function checkAlerts()
         * @param loc
         */
        private void checkAlerts(Location loc) {

            alertDao = daoSession.getAlertDao();
            List<Alert> alerts = alertDao.loadAll();

            Location aLoc = new Location("");
            for (Alert a : alerts) {
                aLoc.setLatitude(a.getLatitude());
                aLoc.setLongitude(a.getLongitude());
                Float dist = loc.distanceTo(aLoc);
                if (dist.intValue() < a.getRadius()) {
                    showNotification("Alert", "You are in a dangerous place");
                    break;
                }
            }
        }

        /**
         * Function checkNewQuestions()
         */
        private void checkNewQuestions() {

            String jsonStr = null;
            JSONObject questionsObject = null;
            JSONArray arrQuestions = null;
            JSONArray questionsArray = null;
            questionaryDao = daoSession.getQuestionaryDao();
            List<Questionary> questions = questionaryDao.loadAll();

            ParkInfo parkInfo = new ParkInfo();
            int eppParkId = parkInfo.getEppParkId();

            System.out.println(" ** DATABASE :: QUESTIONS :: SIZE :: " + questions.size());

            // Questions
            try {
                //String http = "http://10.112.10.132/maps/questions.json";

                HttpGet httpget = new HttpGet("http://144.76.175.87/sensa/etp/getFieldReportQuestion.php?parkid="+eppParkId);


                HttpClient httpclient = new DefaultHttpClient();
                HttpResponse response = httpclient.execute(httpget);

                // Get the string
                HttpEntity entity = response.getEntity();

                if (entity != null) {
                    // A Simple JSON Response Read
                    InputStream instream = entity.getContent();
                    jsonStr = convertStreamToString(instream);
                    instream.close();
                }

            } catch (ClientProtocolException e) {
                //return null;
            } catch (IOException e) {
                //return null;
            }

            try {
                questionsObject = new JSONObject(jsonStr);
                arrQuestions = questionsObject.optJSONArray("questions");

                if(arrQuestions != null) {
                    questionsArray = questionsObject.getJSONArray("questions");

                    System.out.println(" ** EPP URL :: QUESTIONS :: SIZE :: " + questionsArray.length());

                }

            } catch (JSONException e) {
                //
            }



        }





        /**
         * Function checkRequest()
         *
         * Check if there are request which is/are on stand by (it calls only when the traveler is back on area with Wifi)
         */
        private void checkRequest() {




            System.out.println( " 111111111111111111111111111111 dans checkRequest");


            requestDao = daoSession.getRequestDao();
            List<Request> requests = requestDao.loadAll();

            if(requests.size() > 0) {


                System.out.println( " 222222222222222222222222 dans checkRequest");

                // Listing all request(s)
                for (Request r : requests) {

                    Request myRequest = requestDao.load(r.getId());


                    if (mainActivity != null && MainActivity.isActivityVisible()) {


                        // If false, it doesn't have been inserted on the database yet.
                        // Insert it into the database and send to EPP
                        if (!r.getIsrequested()) {
                            r.setIsrequested(true);
                            //new SendAckTask().execute(r.getRequest());


                            System.out.println( " 3333333333333333333 dans checkRequest");

                        // It's true, it means, the request have already been
                        // inserted into the database and sent to EPP
                        } else {
                            requestDao.delete(myRequest);
                        }

                    } // __ END If{ }

                } // __ E?D For{ }

            } // __END If{ }

        }



        /**
         * Function convertStreamToString()
         * @param is
         * @return
         */
        private String convertStreamToString(InputStream is) {

            StringBuilder sb = new StringBuilder();
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                        is, "UTF8"));

                // System.gc();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return sb.toString();
        }



        /**
         * Function isInsidePolygon()
         * @param lat
         * @param lon
         * @param polygon
         * @return
         */
        private boolean isInsidePolygon(double lat, double lon,
                                        DbPolygon polygon) {
            int count = 0;

            DbPoint p;
            for (int i = 0; i < polygon.getPoints().size(); i++) {
                p = polygon.getPoints().get(i);
                if (p.getLongitude() > lon && p.getLatitude() > lat) {
                    if (i > 0) {
                        if (polygon.getPoints().get(i - 1).getLatitude() < lat)
                            count++;
                    } else {
                        if (polygon.getPoints()
                                .get(polygon.getPoints().size() - 2)
                                .getLatitude() < lat)
                            count++;
                    }

                    if (i < polygon.getPoints().size() - 1) {
                        if (polygon.getPoints().get(i + 1).getLatitude() < lat)
                            count++;
                    } else {
                        if (polygon.getPoints().get(1).getLatitude() < lat)
                            count++;
                    }
                }
            }

            if (count % 2 == 0)
                return false;
            else
                return true;
        }


        /**
         * Function onProviderDisabled()
         * @param provider
         */
        public void onProviderDisabled(String provider) {
            /*
            if (showingDebugToast)
                Toast.makeText(getBaseContext(),
                        "onProviderDisabled: " + provider, Toast.LENGTH_SHORT)
                        .show();
            */
        }


        /**
         * Function onProviderEnabled()
         * @param provider
         */
        public void onProviderEnabled(String provider) {
            /*
            if (showingDebugToast)
                Toast.makeText(getBaseContext(),
                        "onProviderEnabled: " + provider, Toast.LENGTH_SHORT)
                        .show();
            */
        }



        /**
         * Function onStatusChanged()
         * @param provider
         * @param status
         * @param extras
         */
        public void onStatusChanged(String provider, int status, Bundle extras) {
            /*
            String showStatus = null;
            if (status == LocationProvider.AVAILABLE) {
                System.out.println(" SENSA :: GPS :: onStatusChanged :: AVAILABLE ");
                showStatus = "Available";
            }
            if (status == LocationProvider.TEMPORARILY_UNAVAILABLE) {
                System.out.println(" SENSA :: GPS :: onStatusChanged :: TEMPORARILY UNAVAILABLE ");
                showStatus = "Temporarily Unavailable";
            }
            if (status == LocationProvider.OUT_OF_SERVICE) {
                System.out.println(" SENSA :: GPS :: onStatusChanged :: OUT OF SERVICE ");
                showStatus = "Out of Service";
            }
            if (status != lastStatus && showingDebugToast) {
                Toast.makeText(getBaseContext(), "new status: " + showStatus,
                        Toast.LENGTH_SHORT).show();
            }
            lastStatus = status;
            */
        }

    };



    /**
     *
     * Below is the service framework methods
     *
     */


    private NotificationManager mNM;

    /**
     * Function onCreate()
     */
    @Override
    public void onCreate() {
        super.onCreate();
        // mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Log.i("LOC", "onCreate");

        startLoggerService();

        // Display a notification about us starting. We put an icon in the
        // status bar.
        // showNotification();
    }



    /**
     * Function onDestroy()
     */
    @Override
    public void onDestroy() {
        super.onDestroy();

        shutdownLoggerService();

        // Cancel the persistent notification.
        // // mNM.cancel(R.string.local_service_started);
        // //
        // // // Tell the user we stopped.
        // // Toast.makeText(this, R.string.local_service_stopped,
        // Toast.LENGTH_SHORT).show();
    }



    /**
     * Function showNotifcation()
     * @param title
     * @param message
     *
     * Show a notification while this service is running
     */
    private void showNotification(String title, String message) {

        Intent intent = new Intent(this, MainActivity.class);

        intent.setFlags(intent.FLAG_ACTIVITY_SINGLE_TOP);
        // intent.putExtra("type", type);
        // intent.setAction(Intent.ACTION_MAIN);
        // intent.addCategory(Intent.CATEGORY_LAUNCHER);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                intent, PendingIntent.FLAG_UPDATE_CURRENT
                        | PendingIntent.FLAG_ONE_SHOT);

        Builder mBuilder = new Builder(
                this).setSmallIcon(R.drawable.sensa_logo)
                .setContentTitle(title).setContentText(message);
        mBuilder.setContentIntent(contentIntent);
        mBuilder.setDefaults(Notification.DEFAULT_SOUND);
        mBuilder.setAutoCancel(true);
        NotificationManager mNotificationManager = (NotificationManager) this
                .getSystemService(Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(2, mBuilder.build());
    }

    // This is the object that receives interactions from clients. See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();



    /**
     * Function onBind()
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }



    /**
     * Function setMinTimeMillis()
     * @param _minTimeMillis
     */
    public static void setMinTimeMillis(long _minTimeMillis) {
        minTimeMillis = _minTimeMillis;
    }



    /**
     * Function getMinTimeMillis()
     * @return
     */
    public static long getMinTimeMillis() {
        return minTimeMillis;
    }



    /**
     * Function setMinDistanceMeters()
     * @param _minDistanceMeters
     */
    public static void setMinDistanceMeters(long _minDistanceMeters) {
        minDistanceMeters = _minDistanceMeters;
    }



    /**
     * Function getMinDistanceMeters()
     * @return
     */
    public static long getMinDistanceMeters() {
        return minDistanceMeters;
    }



    /**
     * Function getMinAccuracyMeters()
     * @return
     */
    public static float getMinAccuracyMeters() {
        return minAccuracyMeters;
    }



    /**
     * Function setMinAccuracyMeters()
     * @param minAccuracyMeters
     */
    public static void setMinAccuracyMeters(float minAccuracyMeters) {
        GPSLoggerService.minAccuracyMeters = minAccuracyMeters;
    }



    /**
     * Function setShowingDebugToast()
     * @param showingDebugToast
     */
    public static void setShowingDebugToast(boolean showingDebugToast) {
        GPSLoggerService.showingDebugToast = showingDebugToast;
    }



    /**
     * isShowingDebugToast()
     * @return
     */
    public static boolean isShowingDebugToast() {
        return showingDebugToast;
    }



    /**
     * Class for clients to access. Because we know this service always runs in
     * the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        GPSLoggerService getService() {
            return GPSLoggerService.this;
        }
    }

}