package org.keyscience.fragments;

import android.Manifest;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Point;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.widget.NestedScrollView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.SettingsClient;
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.LatLng;
import com.google.android.gms.maps.model.MapStyleOptions;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.squareup.picasso.Picasso;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.keyscience.BackgroundTask.EcoQuestJSONLoaderTask;
import org.keyscience.BackgroundTask.LeaderBoardLoaderTask;
import org.keyscience.Database.DatabaseHelper;
import org.keyscience.Gravatar.MD5Util;
import org.keyscience.R;
import org.keyscience.Services.GeofenceTransitionsIntentService;
import org.keyscience.SingletonPattern.LeaderBoardList;
import org.keyscience.SingletonPattern.SingletonEcoQuests;
import org.keyscience.SingletonPattern.SingletonFragmentList;
import org.keyscience.SingletonPattern.User;
import org.keyscience.adapters.EcoQuestInfoWindowAdapter;
import org.keyscience.adapters.LeaderBoardAdapter;
import org.keyscience.app.Constants;
import org.keyscience.app.HomeActivity;
import org.keyscience.app.ProfileUpdateActivity;
import org.keyscience.picasso.CircleTransform;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;

public class ProfileFragment extends Fragment {


    private static String leaderBoardUrl = "http://keyscience.org/wp-content/app/userprofile/leaderboardJson.php";
    private static String phpUrl = "http://www.keyscience.org/wp-content/app/EcoQuest/retrieveEcoQuestJson.php";
    private static String jsonUrl = "http://www.keyscience.org/wp-content/app/EcoQuest/ecoquest.json";
    private PendingIntent geofencePendingIntent;
    private GeofencingClient geofencingClient;
    private ArrayList<Geofence> geofenceList = new ArrayList<>();
    private EcoQuestJSONLoaderTask task;
    private BroadcastReceiver geoFenceBroadcast;

    private TextView logoutButton;
    private ImageView profileImage;
    private Context context;

    private GoogleMap mMap;
    private NestedScrollView mScrollView;

    private TextView userName;
    private TextView userBio;
    private TextView userRank;
    private TextView userTotalPoints;
    private TextView totalHunts;
    private TextView huntsCompleted;
    private View view;

    private RecyclerView leaderBoardRecycler;
    private LeaderBoardLoaderTask leaderBoardLoaderTask;


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


        view = inflater.inflate(R.layout.profile_layout, container, false);

        context = getContext();
        userName = view.findViewById(R.id.userName);
        userBio = view.findViewById(R.id.userBio);
        userRank = view.findViewById(R.id.userRank);
        userTotalPoints = view.findViewById(R.id.totalPoints);
        totalHunts = view.findViewById(R.id.totalHunts);
        huntsCompleted = view.findViewById(R.id.huntsCompleted);

        leaderBoardRecycler = view.findViewById(R.id.leaderBoardRecyclerView);
        leaderBoardRecycler.setFocusable(false);
        leaderBoardRecycler.setLayoutManager(new LinearLayoutManager(getContext()));

        LinearLayout linearLayout = view.findViewById(R.id.profileLayout);

        Display display = (getActivity().getWindowManager().getDefaultDisplay());
        Point size = new Point();
        display.getSize(size);
        int height = size.y;
        linearLayout.setMinimumHeight(height - 180);

        profileImage = view.findViewById(R.id.profile_image);
        profileImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(context, ProfileUpdateActivity.class);
                context.startActivity(intent);
            }
        });

        logoutButton = view.findViewById(R.id.logout_button);
        logoutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // remove sessions
                SharedPreferences sharedPreferences = getContext().getSharedPreferences(Constants.SHARE_PRE_FILE, Context.MODE_PRIVATE);
                SharedPreferences.Editor preferencesEditor = sharedPreferences.edit();

                // remove user ID from share preferences
                preferencesEditor.clear();
                preferencesEditor.commit();

                //remove current user
                User.removeUserInstance();

                //remove from local DB
                context.deleteDatabase(DatabaseHelper.DATABASE_NAME);

                ((HomeActivity) getActivity()).setViewPager(SingletonFragmentList.getInstance().getFragmentPosition(Constants.LOGIN));
            }
        });

        geoFenceBroadcast = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction().equals(Constants.LOCATION_ACTION)) {
                    enableMyLocation();
                    task = new EcoQuestJSONLoaderTask(mMap, geofenceList, getContext());
                    task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, phpUrl, jsonUrl);
                } else if (intent.getAction().equals(Constants.GEOFENCE_ACTION)) {
                    totalHunts.setText(Integer.toString(SingletonEcoQuests.getInstance().size()));
                    enableGeoFence();
                } else if (intent.getAction().equals(Constants.REFRESH_USER_POINTS)) {
                    huntsCompleted.setText(Integer.toString(User.getInstance().getTotalCompletedQuests()));
                    userTotalPoints.setText(Integer.toString(User.getInstance().getPoints()));
                } else if (intent.getAction().equals(Constants.PROFILE_CHANGED)) {
                    userBio.setText(User.getInstance().getBio());
                    userName.setText(User.getInstance().getName());
                }
            }
        };
        IntentFilter filter = new IntentFilter();
        filter.addAction(Constants.LOCATION_ACTION);
        filter.addAction(Constants.GEOFENCE_ACTION);
        filter.addAction(Constants.REFRESH_USER_POINTS);
        filter.addAction(Constants.PROFILE_CHANGED);
        LocalBroadcastManager.getInstance(getContext()).registerReceiver(geoFenceBroadcast, filter);

        geofencingClient = LocationServices.getGeofencingClient(getActivity());

        ((ScrollGoogleMap) getChildFragmentManager()
                .findFragmentById(R.id.map))
                .setListener(new ScrollGoogleMap.OnTouchListener() {
                    @Override
                    public void onTouch() {
                        mScrollView.requestDisallowInterceptTouchEvent(true);
                    }
                });

        if (getUserVisibleHint()) {
            loadUserData();
        }

        return view;
    }

    /**
     * Enable Google Location Services and then request access to device location
     */
    protected void createLocationRequest() {
        LocationRequest locationRequest = LocationRequest.create();
        locationRequest.setInterval(1000);
        locationRequest.setFastestInterval(500);
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(locationRequest);

        SettingsClient client = LocationServices.getSettingsClient(getActivity());
        Task<LocationSettingsResponse> task = client.checkLocationSettings(builder.build());

        task.addOnSuccessListener(getActivity(), new OnSuccessListener<LocationSettingsResponse>() {
            @Override
            public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
                // All location settings are satisfied. The client can initialize
                // location requests here.
                enableMyLocation();
            }
        });

        task.addOnFailureListener(getActivity(), new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                if (e instanceof ResolvableApiException) {
                    // Location settings are not satisfied, but this can be fixed
                    // by showing the user a dialog.
                    try {
                        // Show the dialog by calling startResolutionForResult(),
                        // and check the result in onActivityResult().
                        ResolvableApiException resolvable = (ResolvableApiException) e;
                        resolvable.startResolutionForResult(getActivity(),
                                Constants.REQUEST_CHECK_SETTINGS);
                    } catch (IntentSender.SendIntentException sendEx) {
                        // Ignore the error.
                    }
                }
            }
        });
    }


    //uses the GeofencingRequest class and its nested GeofencingRequestBuilder class to specify
    // the geofences to monitor and to set how related geofence events are triggered
    private GeofencingRequest getGeofencingRequest() {
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        //use INITIAL_TRIGGER_DWELL, which triggers events only when the user stops for a
        // defined duration within a geofence. This approach can help reduce "alert spam" resulting
        // from large numbers notifications when a device briefly enters and exits geofences
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL | GeofencingRequest.INITIAL_TRIGGER_ENTER);
        builder.addGeofences(geofenceList);
        return builder.build();
    }

    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (geofencePendingIntent != null) {
            return geofencePendingIntent;
        }
        Intent intent = new Intent(getContext(), GeofenceTransitionsIntentService.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        geofencePendingIntent = PendingIntent.getService(getContext(), 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        return geofencePendingIntent;
    }

    public void enableGeoFence() {
        if (ContextCompat.checkSelfPermission(getContext(),
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            geofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
                    .addOnSuccessListener(getActivity(), new OnSuccessListener<Void>() {
                        @Override
                        public void onSuccess(Void aVoid) {
                            // Geofences added
                            // ...
                            Toast.makeText(getContext(), "EcoQuest Ready", Toast.LENGTH_LONG).show();
                        }
                    })
                    .addOnFailureListener(getActivity(), new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Failed to add geofences
                            // This message is for debugging, please remove
                            // Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
                        }
                    });
        } else {
            ActivityCompat.requestPermissions(getActivity(), new String[]
                    {Manifest.permission.ACCESS_FINE_LOCATION}, Constants.REQUEST_LOCATION_PERMISSION);
        }
    }

    private void enableMyLocation() {
        if (ContextCompat.checkSelfPermission(getContext(),
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            mMap.setMyLocationEnabled(true);
        } else {
            ActivityCompat.requestPermissions(getActivity(), new String[]
                    {Manifest.permission.ACCESS_FINE_LOCATION}, Constants.REQUEST_LOCATION_PERMISSION);
        }
    }

    private void updateProfileImage() {
        String userEmail = User.getInstance().getEmail();
        String hash = MD5Util.md5Hex(userEmail);
        String gravatarUrl = "http://www.gravatar.com/avatar/" + hash + "?s=204&d=404";

        Picasso.get()
                .load(gravatarUrl)
                .transform(new CircleTransform())
                .placeholder(R.mipmap.ic_launcher_round)
                .resize(100, 100)
                .centerInside()
                .noFade()
                .into(profileImage);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser && isResumed()) {
            loadUserData();
        }
    }

    private void loadMap() {

        SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                mMap = googleMap;
                createLocationRequest();
                mMap.setInfoWindowAdapter(new EcoQuestInfoWindowAdapter(getContext()));
                mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                mMap.getUiSettings().setMapToolbarEnabled(false);

                // Add a marker in KeyBiscayne and move the camera
                LatLng KeyBiscayne = new LatLng(25.693203, -80.162944);
                float zoom = 13;
                mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(KeyBiscayne, zoom));

                mScrollView = view.findViewById(R.id.scrollMap); //parent scrollview in xml, give your scrollview id value

                try {
                    // Customise the styling of the base map using a JSON object defined
                    // in a raw resource file.
                    boolean success = googleMap.setMapStyle(
                            MapStyleOptions.loadRawResourceStyle(
                                    context, R.raw.geohunt_mapstyle));

                    if (!success) {
                    }
                } catch (Resources.NotFoundException e) {
                    e.printStackTrace();
                }
                if (task == null) {
                    task = new EcoQuestJSONLoaderTask(mMap, geofenceList, getContext());
                    task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, phpUrl, jsonUrl);
                }
            }
        });
    }

    private void loadUserData() {
        User user = User.getInstance();
        userName.setText(user.getName());
        userBio.setText(user.getBio());
        userRank.setText(user.getRank());
        userTotalPoints.setText(Integer.toString(user.getPoints()));
        int integer = user.getTotalCompletedQuests();
        huntsCompleted.setText(Integer.toString(integer));

        new RankAsyncTask().execute(Integer.toString(user.getPoints()));

        updateProfileImage();
        // after user data has been loaded, then we can load the map
        loadMap();
        loadLeaderBoard();
    }

    private void loadLeaderBoard() {

        if (LeaderBoardList.getInstance().getList().size() == 0) {
            leaderBoardLoaderTask = new LeaderBoardLoaderTask(leaderBoardRecycler, context, LeaderBoardList.getInstance().getList());
            leaderBoardLoaderTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, leaderBoardUrl);
        } else {
            leaderBoardRecycler.setAdapter(new LeaderBoardAdapter(LeaderBoardList.getInstance().getList(),
                    context));
        }

    }

    @Override
    public void onPause() {
        if (task != null) {
            task.cancel(true);
            try {
                //Waits if necessary to complete
                task.get();
            } catch (ExecutionException e) {
                //  ignore
            } catch (InterruptedException e) {
                //  ignore
            } catch (CancellationException e) {
                //  ignore
            }
            task = null;
        }
        if (leaderBoardLoaderTask != null) {
            leaderBoardLoaderTask.cancel(true);
            try {
                //Waits if necessary to complete
                leaderBoardLoaderTask.get();
            } catch (ExecutionException e) {
                //  ignore
            } catch (InterruptedException e) {
                //  ignore
            } catch (CancellationException e) {
                //  ignore
            }
            leaderBoardLoaderTask = null;
        }
        super.onPause();
    }

    @Override
    public void onDestroy() {
        LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(geoFenceBroadcast);
        super.onDestroy();
    }

    /*Threw this together quickly in case we run out of time.*/
    private class RankAsyncTask extends AsyncTask<String, String, String> {

        public String ranksJsonDir = "http://keyscience.org/wp-content/app/userprofile/userRanks.json";
        public HashMap<String, String> map = new HashMap<>();
        public String strUserRank;

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);

            userRank.setText(strUserRank);
        }

        protected String doInBackground(String... params) {
            HttpURLConnection connection = null;
            BufferedReader reader = null;

            try {
                URL url = new URL(ranksJsonDir);
                connection = (HttpURLConnection) url.openConnection();
                connection.connect();

                InputStream stream = connection.getInputStream();

                reader = new BufferedReader(new InputStreamReader(stream));

                StringBuffer buffer = new StringBuffer();
                String line = "";

                while ((line = reader.readLine()) != null) {
                    buffer.append(line + "\n");
                }

                String jsonInput = buffer.toString();

                readRankJSON(Integer.valueOf(params[0]), jsonInput);

            } catch (Exception e) {

            }

            return null;
        }

        private void readRankJSON(int userPoints, String inputLine) {
            List<HashMap<String, String>> results = new ArrayList<>();
            JSONArray jArray;
            try {
                jArray = new JSONArray(inputLine);

                for (int i = 0; i < jArray.length(); i++) {
                    map = new HashMap<>();

                    JSONObject json_data = jArray.getJSONObject(i);

                    map.put("Rank", json_data.getString("Rank"));
                    map.put("Points", json_data.getString("Points"));

                    results.add(map);
                }

                HashMap.Entry<String, String> maxEntry = null;

                for (HashMap.Entry<String, String> entry : map.entrySet())
                    if (maxEntry == null || entry.getValue().compareTo(maxEntry.getValue()) > 0)
                        if ( userPoints > Integer.valueOf(entry.getValue()) )
                            strUserRank = map.get("Rank");
                        else
                            for (HashMap<String, String> model : results)
                                if ( userPoints <= Integer.valueOf(model.get("Points")) ) {
                                    strUserRank = model.get("Rank");
                                    break;
                                }

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