/* Copyright 2016 Esri
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * For additional information, contact:
 * Environmental Systems Research Institute, Inc.
 * Attn: Contracts Dept
 * 380 New York Street
 * Redlands, California, USA 92373
 *
 * email: [email protected]
 *
 */

package com.esri.arcgisruntime.opensourceapps.mapsapp;


import java.util.ArrayList;
import java.util.List;

import android.Manifest;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.provider.Settings;
import androidx.annotation.NonNull;

import com.google.android.material.snackbar.Snackbar;
import androidx.core.app.ActivityCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import butterknife.BindView;
import butterknife.ButterKnife;

import com.esri.arcgisruntime.opensourceapps.mapsapp.account.AccountManager;
import com.esri.arcgisruntime.opensourceapps.mapsapp.account.SignInActivity;
import com.esri.arcgisruntime.opensourceapps.mapsapp.basemaps.BasemapsDialogFragment;

/**
 * Entry point into the Maps App.
 */
public class MapsAppActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {


	private static final int PERMISSION_REQUEST_LOCATION = 0;
	private static final int REQUEST_LOCATION_SETTINGS = 1;
	private static final int REQUEST_WIFI_SETTINGS = 2;
	private static final int REQUEST_ARCGIS_CRED = 3;
	private static final String TAG = MapsAppActivity.class.getSimpleName();
	public  DrawerLayout mDrawerLayout;
	private final List<DrawerItem> mDrawerItems = new ArrayList<>();
	/**
	 * The list of menu items in the navigation drawer
	 */
	@BindView(R.id.maps_app_activity_left_drawer) ListView mDrawerList;
	private View mLayout;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.maps_app_activity);
		mLayout = findViewById(R.id.maps_app_activity_content_frame);

		// We need a reference to the containing widget when
		// managing the Snackbar (used for notifying users about app
		// permissions)
		mLayout = findViewById(R.id.maps_app_activity_content_frame);


		ButterKnife.bind(this);

		setupDrawer();

		// All devices running N and above require explicit permissions
		// checking when the app is first run.

		requestLocationPermission();
	}

	/*
	 * Prompt user to turn on location tracking and wireless if needed
	 */
	private void checkSettings() {
		// Is GPS enabled?
		boolean gpsEnabled = locationTrackingEnabled();
		// Is there internet connectivity?
		boolean internetConnected = internetConnectivity();

		if (gpsEnabled && internetConnected) {
			setView();
		}else if (!gpsEnabled) {
			Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
			showDialog(gpsIntent, REQUEST_LOCATION_SETTINGS, getString(R.string.location_tracking_off));
		}else {
			Intent internetIntent = new Intent(Settings.ACTION_WIFI_SETTINGS);
			showDialog(internetIntent, REQUEST_WIFI_SETTINGS, getString(R.string.wireless_off));
		}
	}

	/**
	 * Prompt user to enable location tracking
	 */
	private void showDialog(final Intent intent, final int requestCode, String message) {

		final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
		alertDialog.setMessage(message);
		alertDialog.setPositiveButton(getString(R.string.open_location_options), new DialogInterface.OnClickListener() {

			@Override
			public void onClick(DialogInterface dialog, int which) {
				//
				startActivityForResult(intent, requestCode);
			}
		});
		alertDialog.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {

			@Override
			public void onClick(DialogInterface dialog, int which) {

			}
		});
		alertDialog.create().show();
	}

	/**
	 * When returning from activities concerning airplane mode and location
	 * settings, check them again.
	 *
	 * @param requestCode
	 * @param resultCode
	 * @param data
	 */
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == REQUEST_WIFI_SETTINGS || requestCode == REQUEST_LOCATION_SETTINGS) {
			checkSettings();
		}else if (requestCode == REQUEST_ARCGIS_CRED){
			Log.i(TAG, "Browser returned...");
		}

	}

	/**
	 * Requests the {@link Manifest.permission#ACCESS_COARSE_LOCATION}
	 * permission. If an additional rationale should be displayed, the user has
	 * to launch the request from a SnackBar that includes additional
	 * information.
	 */

	private void requestLocationPermission() {
		// Permission has not been granted and must be requested.
		if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {

			// Provide an additional rationale to the user if the permission was
			// not granted
			// and the user would benefit from additional context for the use of
			// the permission.
			// Display a SnackBar with a button to request the missing
			// permission.
			Snackbar.make(mLayout, "Location access is required to display the map.", Snackbar.LENGTH_INDEFINITE)
					.setAction("OK", new View.OnClickListener() {
						@Override
						public void onClick(View view) {
							// Request the permission
							ActivityCompat.requestPermissions(MapsAppActivity.this,
									new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
									PERMISSION_REQUEST_LOCATION);
						}
					}).show();

		} else {
			// Request the permission. The result will be received in
			// onRequestPermissionResult().
			ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
					PERMISSION_REQUEST_LOCATION);
		}
	}

	/**
	 * Once the app has prompted for permission to access location, the response
	 * from the user is handled here. If permission exists to access location
	 * check if GPS is available and device is not in airplane mode.
	 *
	 * @param requestCode
	 *            int: The request code passed into requestPermissions
	 * @param permissions
	 *            String: The requested permission(s).
	 * @param grantResults
	 *            int: The grant results for the permission(s). This will be
	 *            either PERMISSION_GRANTED or PERMISSION_DENIED
	 */
	@Override
	public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

		if (requestCode == PERMISSION_REQUEST_LOCATION) {
			// Request for camera permission.
			if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
				// Permission has been granted, do we have the right phone
				// settings?
				// Check for GPS and wireless
				checkSettings();

			} else {
				// Permission request was denied.
				Snackbar.make(mLayout, "Location permission request was denied.", Snackbar.LENGTH_SHORT).show();
			}
		}
	}


	@Override
	protected void onResume() {
		super.onResume();
		if (getIntent().getData()== null){
			updateDrawer();
		}

	}

	/**
	 * Initializes the navigation drawer.
	 */
	private void setupDrawer() {
		mDrawerLayout = findViewById(R.id.maps_app_activity_drawer_layout);

		// Set the list's click listener
		mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

		// set a custom shadow that overlays the main content when the drawer
		// opens
		mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);

		updateDrawer();
	}

	private void setView() {
		if (AccountManager.getInstance().isSignedIn()) {
			// we are signed in to a portal - show the content browser to choose
			// a map
			showContentBrowser();
		} else {
			// show the default map
			showMap(null, null);
		}
	}

	/**
	 * Opens the content browser that shows the user's maps.
	 */
	private void showContentBrowser() {
		FragmentManager fragmentManager = getFragmentManager();
		Fragment browseFragment = fragmentManager.findFragmentByTag(ContentBrowserFragment.TAG);
		if (browseFragment == null) {
			browseFragment = new ContentBrowserFragment();
		}

		if (!browseFragment.isVisible()) {
			FragmentTransaction transaction = fragmentManager.beginTransaction();
			transaction.add(R.id.maps_app_activity_content_frame, browseFragment, ContentBrowserFragment.TAG);
			transaction.addToBackStack(null);
			transaction.commit();

			invalidateOptionsMenu(); // reload the options menu
		}

		mDrawerLayout.closeDrawers();
	}

	/**
	 * Opens the map represented by the specified portal item or if null, opens
	 * a default map.
	 *
	 * @param portalItemId
	 *            - String representing a portal resource
	 * @param basemapPortalItemId
	 *            - String representing a basemap portal item
	 */
	public void showMap(String portalItemId, String basemapPortalItemId) {
		// remove existing MapFragment explicitly, simply replacing it can cause
		// the app to freeze when switching basemaps
		FragmentTransaction transaction;
		FragmentManager fragmentManager = getFragmentManager();
		Fragment currentMapFragment = fragmentManager.findFragmentByTag(MapFragment.TAG);
		if (currentMapFragment != null) {
			transaction = fragmentManager.beginTransaction();
			transaction.remove(currentMapFragment);
			transaction.commit();
		}

		MapFragment mapFragment = MapFragment.newInstance(portalItemId, basemapPortalItemId);

		transaction = fragmentManager.beginTransaction();
		transaction.replace(R.id.maps_app_activity_content_frame, mapFragment, MapFragment.TAG);
		transaction.addToBackStack(null);
		transaction.commit();

		invalidateOptionsMenu(); // reload the options menu
	}

	private void showSignInActivity() {
		Intent intent = new Intent(this, SignInActivity.class);
		startActivity(intent);

		mDrawerLayout.closeDrawers();
	}

	/**
	 * Resets the app to "signed out" state.
	 */
	private void signOut() {
		AccountManager.getInstance().setPortal(null);

		setView();

		updateDrawer();
		mDrawerLayout.closeDrawers();
	}

	/**
	 * Updates the navigation drawer items.
	 */
	private void updateDrawer() {
		mDrawerItems.clear();

		DrawerItem item;
		if (AccountManager.getInstance().isSignedIn()) {

			// user info
			LinearLayout userInfoView = (LinearLayout) getLayoutInflater().inflate(R.layout.drawer_item_user_layout,
					null);
			TextView textView = userInfoView.findViewById(R.id.drawer_item_fullname_textview);
			textView.setText(AccountManager.getInstance().getPortalUser().getFullName());

			textView = userInfoView.findViewById(R.id.drawer_item_username_textview);
			textView.setText(AccountManager.getInstance().getPortalUser().getUsername());

			item = new DrawerItem(userInfoView, null);
			mDrawerItems.add(item);

			// Sign Out

			LinearLayout view_signOut = (LinearLayout) getLayoutInflater().inflate(R.layout.drawer_item_layout, null);
			TextView text_drawer_signOut = view_signOut.findViewById(R.id.drawer_item_textview);
			ImageView icon_drawer_signOut = view_signOut.findViewById(R.id.drawer_item_icon);

			text_drawer_signOut.setText(getString(R.string.sign_out));
			icon_drawer_signOut.setImageResource(R.drawable.ic_profile);
			item = new DrawerItem(view_signOut, new DrawerItem.OnClickListener() {

				@Override
				public void onClick() {
					signOut();
				}
			});
			mDrawerItems.add(item);

			// My Maps
			LinearLayout view_myMaps = (LinearLayout) getLayoutInflater().inflate(R.layout.drawer_item_layout, null);
			TextView text_drawer_myMaps = view_myMaps.findViewById(R.id.drawer_item_textview);
			ImageView icon_drawer_myMaps = view_myMaps.findViewById(R.id.drawer_item_icon);

			text_drawer_myMaps.setText(getString(R.string.my_maps));
			icon_drawer_myMaps.setImageResource(R.drawable.ic_map32);
			item = new DrawerItem(view_myMaps, new DrawerItem.OnClickListener() {

				@Override
				public void onClick() {
					showContentBrowser();
				}
			});
			mDrawerItems.add(item);
		} else {

			// Adding the Sign In item in the drawer
			LinearLayout view_signIn = (LinearLayout) getLayoutInflater().inflate(R.layout.drawer_item_layout, null);
			TextView text_drawer_signIn = view_signIn.findViewById(R.id.drawer_item_textview);
			ImageView icon_drawer_signIn = view_signIn.findViewById(R.id.drawer_item_icon);

			text_drawer_signIn.setText(getString(R.string.sign_in));
			icon_drawer_signIn.setImageResource(R.drawable.ic_profile);
			item = new DrawerItem(view_signIn, new DrawerItem.OnClickListener() {

				@Override
				public void onClick() {
					showSignInActivity();
				}
			});
			mDrawerItems.add(item);
		}

		// Adding the basemap item in the drawer
		LinearLayout view_basemap = (LinearLayout) getLayoutInflater().inflate(R.layout.drawer_item_layout, null);
		TextView text_drawer_basemap = view_basemap.findViewById(R.id.drawer_item_textview);
		ImageView icon_drawer_basemap = view_basemap.findViewById(R.id.drawer_item_icon);
		text_drawer_basemap.setText(getString(R.string.menu_basemaps));
		icon_drawer_basemap.setImageResource(R.drawable.action_basemaps);
		item = new DrawerItem(view_basemap, new DrawerItem.OnClickListener() {

			@Override
			public void onClick() {
				// Show BasemapsDialogFragment to offer a choice if basemaps.
				// This calls back to onBasemapChanged() if one is selected.
				BasemapsDialogFragment basemapsFrag = new BasemapsDialogFragment();
				basemapsFrag.setBasemapsDialogListener(new BasemapsDialogFragment.BasemapsDialogListener() {

					@Override
					public void onBasemapChanged(String itemId) {
						showMap(null, itemId);
					}
				});
				basemapsFrag.show(getFragmentManager(), null);
				mDrawerLayout.closeDrawers();
			}

		});

		mDrawerItems.add(item);

		BaseAdapter adapter = (BaseAdapter) mDrawerList.getAdapter();
		if (adapter == null) {
			adapter = new DrawerItemListAdapter();
			mDrawerList.setAdapter(adapter);
		} else {
			adapter.notifyDataSetChanged();
		}
	}

	private boolean locationTrackingEnabled() {
		LocationManager locationManager = (LocationManager) getApplicationContext()
				.getSystemService(Context.LOCATION_SERVICE);
		return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
	}

	private boolean internetConnectivity(){
		ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo wifi = connManager.getActiveNetworkInfo();
		if (wifi == null){
			return false;
		}else {
			return wifi.isConnected();
		}
	}

	/**
	 * Handles selection of items in the navigation drawer.
	 */
	private class DrawerItemClickListener implements OnItemClickListener {

		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			mDrawerItems.get(position).onClicked();
		}
	}

	/**
	 * Populates the navigation drawer list with items.
	 */
	private class DrawerItemListAdapter extends BaseAdapter {

		@Override
		public int getCount() {
			return mDrawerItems.size();
		}

		@Override
		public Object getItem(int position) {
			return mDrawerItems.get(position);
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			DrawerItem drawerItem = (DrawerItem) getItem(position);
			return drawerItem.getView();
		}
	}

}