package io.github.project_travel_mate.travel;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import com.afollestad.materialdialogs.MaterialDialog;
import com.airbnb.lottie.LottieAnimationView;

import org.json.JSONArray;
import org.json.JSONException;
import org.osmdroid.api.IMapController;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.Marker;
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;

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

import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.project_travel_mate.R;
import objects.MapItem;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import utils.GPSTracker;
import utils.TravelmateSnackbars;

import static utils.Constants.API_LINK_V2;
import static utils.Constants.HERE_API_MODES;
import static utils.Constants.USER_TOKEN;

/**
 * Show markers on map around user's current location
 */
public class MapViewRealTimeActivity extends AppCompatActivity implements
        TravelmateSnackbars, Marker.OnMarkerClickListener {

    private final List<MapItem> mMapItems = new ArrayList<>();
    @BindView(R.id.data)
    ScrollView scrollView;
    @BindView(R.id.animation)
    LottieAnimationView animationView;
    @BindView(R.id.layout)
    LinearLayout layout;
    private int mIndex = 0;
    private Handler mHandler;
    private String mToken;
    private String mCurlat;
    private String mCurlon;
    private MapView mMap;
    private IMapController mController;
    private static final int REQUEST_LOCATION = 199;
    GPSTracker tracker;
    public ArrayList<Integer> mSelectedIndices = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map_realtime);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        ButterKnife.bind(this);

        mHandler = new Handler(Looper.getMainLooper());
        mMap = findViewById(R.id.map);
        scrollView.setVisibility(View.GONE);
        SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        mToken = mSharedPreferences.getString(USER_TOKEN, null);

        initMap();

        setTitle("Map View");

        // Get user's current location
        tracker = new GPSTracker(this);
        if (!tracker.canGetLocation()) {
            tracker.displayLocationRequest(this, mMap);
        } else {
            mCurlat = Double.toString(tracker.getLatitude());
            mCurlon = Double.toString(tracker.getLongitude());
            showDialogToSelectitems();
        }
        Objects.requireNonNull(getSupportActionBar()).setHomeButtonEnabled(true);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }

    /**
     * On open street map initialize
     */
    private void initMap() {
        mMap.setBuiltInZoomControls(false);
        mMap.setMultiTouchControls(true);
        mMap.setTilesScaledToDpi(true);
        mController = mMap.getController();

        MyLocationNewOverlay mLocationoverlay = new MyLocationNewOverlay(mMap);
        mLocationoverlay.enableFollowLocation();
        mLocationoverlay.enableMyLocation();
        mMap.getOverlays().add(mLocationoverlay);
        mController.setZoom(14.0);
    }

    /**
     * Calls API to get nearby places
     *
     * @param mode mode; type of places;
     * @param icon marker icon
     */
    private void getMarkers(String mode, final int icon) {

        String uri = API_LINK_V2 + "get-places/" + mCurlat + "/" + mCurlon
                + "/" + mode;

        Log.v("EXECUTING", uri);

        //Set up client
        OkHttpClient client = new OkHttpClient();
        //Execute request
        Request request = new Request.Builder()
                .header("Authorization", "Token " + mToken)
                .url(uri)
                .build();
        //Setup callback
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e("Request Failed", "Message : " + e.getMessage());
                mHandler.post(() -> networkError());
            }

            @Override
            public void onResponse(final Call call, final Response response) throws IOException {

                final String res = Objects.requireNonNull(response.body()).string();
                mHandler.post(() -> {
                    try {
                        JSONArray routeArray = new JSONArray(res);

                        for (int i = 0; i < routeArray.length(); i++) {
                            String name = routeArray.getJSONObject(i).getString("title");
                            String web = routeArray.getJSONObject(i).getString("icon");
                            String nums = routeArray.getJSONObject(i).getString("distance");
                            String addr = routeArray.getJSONObject(i).getString("address");

                            Double latitude =
                                    routeArray.getJSONObject(i).getDouble("latitude");
                            Double longitude =
                                    routeArray.getJSONObject(i).getDouble("longitude");

                            showMarker(latitude, longitude, name, icon);
                            mMapItems.add(new MapItem(name, nums, web, addr));
                        }
                        animationView.setVisibility(View.GONE);
                        layout.setVisibility(View.VISIBLE);
                    } catch (JSONException e) {
                        e.printStackTrace();
                        networkError();
                        Log.e("ERROR : ", e.getMessage() + " ");
                    }
                });
            }
        });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                return true;
            case R.id.action_sort:
                showDialogToSelectitems();
                return true;
            case R.id.action_list_view :
                finish();
                Intent intent = ListViewRealTimeActivity.getStartIntent(MapViewRealTimeActivity.this);
                startActivity(intent);
                return true;
            default :
                return super.onOptionsItemSelected(item);
        }
    }

    /**
     * Shows a dialog to allow user to select items to be displayed
     * on map
     */
    private void showDialogToSelectitems() {
        Integer[] selectedItems = new Integer[mSelectedIndices.size()];

        for (int i = 0; i < mSelectedIndices.size(); i++) {
            selectedItems[i] = mSelectedIndices.get(i);
        }
        mSelectedIndices.clear();
        new MaterialDialog.Builder(this)
                .title(R.string.title)
                .items(R.array.items)
                .itemsCallbackMultiChoice(selectedItems, (dialog, which, text) -> {

                //    mGoogleMap.clear();
                    mMapItems.clear();

                    for (int i = 0; i < which.length; i++) {
                        Log.v("selected", which[i] + " " + text[i]);
                        mSelectedIndices.add(which[i]);
                        Integer icon;
                        switch (which[i]) {
                            case 0:
                                icon = R.drawable.ic_local_pizza_black;
                                break;
                            case 1:
                                icon = R.drawable.ic_local_bar_black;
                                break;
                            case 2:
                                icon = R.drawable.ic_camera_alt_black;
                                break;
                            case 3:
                                icon = R.drawable.ic_directions_bus_black;
                                break;
                            case 4:
                                icon = R.drawable.ic_local_mall_black;
                                break;
                            case 5:
                                icon = R.drawable.ic_local_gas_station_black;
                                break;
                            case 6:
                                icon = R.drawable.ic_local_atm_black;
                                break;
                            case 7:
                                icon = R.drawable.ic_local_hospital_black;
                                break;
                            default:
                                icon = R.drawable.ic_attach_money_black;
                                break;
                        }
                        MapViewRealTimeActivity.this.getMarkers(HERE_API_MODES.get(which[i]), icon);
                    }
                    return true;
                })
                .positiveText(R.string.choose)
                .show();
    }

    /**
     * Sets marker at given location on map
     *
     * @param locationLat  latitude
     * @param locationLong longitude
     * @param locationName name of location
     * @param locationIcon icon
     */
    private void showMarker(Double locationLat, Double locationLong, String locationName, Integer locationIcon) {
        GeoPoint coord = new GeoPoint(locationLat, locationLong);
        Marker marker = new Marker(mMap);

        if (ContextCompat.checkSelfPermission(MapViewRealTimeActivity.this,
                Manifest.permission.ACCESS_COARSE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            marker.setPosition(coord);
            marker.setIcon(this.getDrawable(locationIcon));
            marker.setTitle(locationName);
            marker.setOnMarkerClickListener(this);

            mMap.getOverlays().add(marker);
            mMap.invalidate();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.map_menu, menu);
        return true;
    }

    public static Intent getStartIntent(Context context) {
        Intent intent = new Intent(context, MapViewRealTimeActivity.class);
        return intent;
    }

    /**
     * On marker selected
     *
     * @param marker  marker
     */
    private void onMarkerSelected(Marker marker) {
        // Zoom to current location
        GeoPoint coordinate = new GeoPoint(Double.parseDouble(mCurlat), Double.parseDouble(mCurlon));
        mController.setZoom(14.0);
        mController.animateTo(coordinate);

        marker.showInfoWindow();

        scrollView.setVisibility(View.VISIBLE);
        for (int i = 0; i < mMapItems.size(); i++) {
            if (mMapItems.get(i).getName().equals(marker.getTitle())) {
                mIndex = i;
                break;
            }
        }

        TextView title = MapViewRealTimeActivity.this.findViewById(R.id.item_title);
        TextView description = MapViewRealTimeActivity.this.findViewById(R.id.item_description);
        final Button calls, book;
        calls = MapViewRealTimeActivity.this.findViewById(R.id.call);
        book = MapViewRealTimeActivity.this.findViewById(R.id.book);

        title.setText(mMapItems.get(mIndex).getName());

        description.setText(android.text.Html.fromHtml(mMapItems.get(mIndex).getAddress()).toString());
        calls.setOnClickListener(view -> {
            Intent intent = new Intent(Intent.ACTION_DIAL);
            intent.setData(Uri.parse("tel:" + mMapItems.get(mIndex).getNumber()));
            MapViewRealTimeActivity.this.startActivity(intent);

        });
        book.setOnClickListener(view -> {
            Intent browserIntent;
            try {
                browserIntent = new Intent(
                        Intent.ACTION_VIEW, Uri.parse(mMapItems.get(mIndex).getAddress()));
                MapViewRealTimeActivity.this.startActivity(browserIntent);
            } catch (Exception e) {
                TravelmateSnackbars.createSnackBar(findViewById(R.id.map_real_time),
                        R.string.no_activity_for_browser, Snackbar.LENGTH_LONG).show();
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            // Check for the integer request code originally supplied to startResolutionForResult().
            case REQUEST_LOCATION:
                switch (resultCode) {
                    case Activity.RESULT_OK:
                        //User agreed to make required location settings changes
                        //startLocationUpdates();
                        TravelmateSnackbars.createSnackBar(findViewById(R.id.map_real_time),
                                R.string.location_enabled, Snackbar.LENGTH_LONG).show();
                        mCurlat = Double.toString(tracker.getLatitude());
                        mCurlon = Double.toString(tracker.getLongitude());
                        getMarkers("eat-drink", R.drawable.ic_local_pizza_black);

                        break;
                    case Activity.RESULT_CANCELED:
                        //User chose not to make required location settings changes
                        TravelmateSnackbars.createSnackBar(findViewById(R.id.map_real_time),
                                R.string.location_not_enabled, Snackbar.LENGTH_LONG).show();
                        break;
                }
                break;
        }
    }

    /**
     * On marker item click
     *
     * @param marker  marker
     * @param mapView mapView
     */
    @Override
    public boolean onMarkerClick(Marker marker, MapView mapView) {
        onMarkerSelected(marker);
        return false;
    }

    /**
     * Plays the network lost animation in the view
     */
    private void networkError() {
        layout.setVisibility(View.GONE);
        animationView.setVisibility(View.VISIBLE);
        animationView.setAnimation(R.raw.network_lost);
        animationView.playAnimation();
    }
}