package edu.vanderbilt.vm.guide.ui;
import java.util.ArrayList;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleWeightedGraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnCameraChangeListener;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import edu.vanderbilt.vm.guide.R;
import edu.vanderbilt.vm.guide.container.Agenda;
import edu.vanderbilt.vm.guide.container.Graph;
import edu.vanderbilt.vm.guide.container.MapVertex;
import edu.vanderbilt.vm.guide.container.Node;
import edu.vanderbilt.vm.guide.container.Place;
import edu.vanderbilt.vm.guide.db.GuideDBConstants;
import edu.vanderbilt.vm.guide.db.GuideDBOpenHelper;
import edu.vanderbilt.vm.guide.util.DBUtils;
import edu.vanderbilt.vm.guide.util.Geomancer;
import edu.vanderbilt.vm.guide.util.GlobalState;
import edu.vanderbilt.vm.guide.util.GuideConstants;
public class AgendaMapFrag extends SupportMapFragment
implements OnMapLongClickListener, OnMarkerClickListener, IGraphMapper {
// @SuppressWarnings("unused")
private static final Logger logger = LoggerFactory.getLogger("ui.AgendaMapFrag");
private Agenda mAgenda;
private int mPlaceIdFocused = -1;
private Menu mMenu;
private boolean showSelf = true;
/**
* Instantiate a Map Fragment and puts markers on all the places on the
* Agenda
*
* @param ctx
* @param agenda
* @return
*/
public static AgendaMapFrag newInstance(Context ctx, Agenda agenda) {
AgendaMapFrag frag = (AgendaMapFrag)Fragment.instantiate(ctx,
"edu.vanderbilt.vm.guide.ui.AgendaMapFrag");
frag.mAgenda = agenda;
return frag;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
}
@Override
public void onResume() {
super.onResume();
if (mAgenda == null) {
mAgenda = GlobalState.getUserAgenda();
}
GoogleMap map = getMap();
MapViewer.resetCamera(map);
map.setOnMarkerClickListener(this); // What happens when a marker is tapped
map.setMyLocationEnabled(showSelf);
map.setOnMapLongClickListener(this);
map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
//logger.info("PlaceIdFocused: " + mPlaceIdFocused);
PlaceDetailer.open(getActivity(), mPlaceIdFocused);
}
});
ArrayList<LatLng> geopointList = new ArrayList<LatLng>();
for (Place plc : mAgenda) {
geopointList.add(MapViewer.toLatLng(plc));
Location plcLoc = new Location("temp");
plcLoc.setLatitude(plc.getLatitude());
plcLoc.setLongitude(plc.getLongitude());
int dist = (int)Geomancer.getDeviceLocation().distanceTo(plcLoc);
// Set the marker for each Place
// Title must be exactly as the PlaceName, in order to match
// them later on
map.addMarker(new MarkerOptions().position(MapViewer.toLatLng(plc))
.title(plc.getName()).draggable(false).snippet(dist + " yards away"));
}
// Calculate the bounds that cover all places in Agenda
if (geopointList.size() == 0) {
} else if (geopointList.size() == 1) {
map.moveCamera(CameraUpdateFactory.newLatLng(geopointList.get(0)));
} else {
map.setOnCameraChangeListener(new OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition arg0) {
GoogleMap map = getMap();
LatLngBounds.Builder builder = LatLngBounds.builder();
for (Place plc : mAgenda) {
builder.include(MapViewer.toLatLng(plc));
}
map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 20));
map.setOnCameraChangeListener(null);
}
});
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.map_menu_add_agenda:
MapViewer.addToAgenda(getActivity(), mPlaceIdFocused);
mMenu.findItem(R.id.map_menu_add_agenda).setVisible(false);
mMenu.findItem(R.id.map_menu_remove_agenda).setVisible(true);
return true;
case R.id.map_menu_remove_agenda:
MapViewer.removeFromAgenda(getActivity(), mPlaceIdFocused);
mMenu.findItem(R.id.map_menu_add_agenda).setVisible(true);
mMenu.findItem(R.id.map_menu_remove_agenda).setVisible(false);
return true;
default:
return false;
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.map_viewer, menu);
mMenu = menu;
}
/**
* Set whether the map has a current location marker. Default is true.
*
* @param show
*/
void setShowCurrentLocation(boolean show) {
showSelf = show;
}
/**
* Set whether the map will show the adjacency connection between Points in
* the Agenda. This method should only be called inside or after onResume().
*/
void drawNetwork() {
GoogleMap map = this.getMap();
map.setMyLocationEnabled(showSelf);
map.setOnMapLongClickListener(this);
Graph g = Graph.createGraph(mAgenda);
logger.debug("Graph creation done");
g.buildNetwork();
logger.debug("Network building done");
for (Node nn : g) {
for (Integer i : nn.getNeighbours()) {
drawPath(nn, g.findNodeById(i));
}
}
logger.debug("Path drawing done");
}
void setAgenda(Agenda a) {
this.mAgenda = a;
}
@Override
public void onPause() {
super.onPause();
// Stops the map from requesting location data when this page is
// inactive
this.getMap().setMyLocationEnabled(false);
}
private void drawPath(Node n1, Node n2) {
PolylineOptions option = new PolylineOptions();
option.add(new LatLng(n1.getLat(), n1.getLng()));
option.add(new LatLng(n2.getLat(), n2.getLng()));
this.getMap().addPolyline(option);
}
public void drawPath(Graph g) {
PolylineOptions option = new PolylineOptions();
for (Node n : g) {
option.add(new LatLng(n.getLat(), n.getLng()));
}
this.getMap().addPolyline(option);
}
public void clearMap() {
getMap().clear();
}
public void redrawMarker() {
for (Place plc : mAgenda) {
Location plcLoc = new Location("temp");
plcLoc.setLatitude(plc.getLatitude());
plcLoc.setLongitude(plc.getLongitude());
// Set the marker for each Place
// Title must be exactly as the PlaceName, in order to match
// them later on
getMap().addMarker(
new MarkerOptions()
.position(MapViewer.toLatLng(plc))
.title(plc.getName())
.draggable(false)
.snippet(
Geomancer.getDeviceLocation().distanceTo(plcLoc)
+ " yards away"));
}
}
@Override
public void onMapLongClick(LatLng point) {
Location clicked = new Location("temp");
clicked.setLatitude(point.latitude);
clicked.setLongitude(point.longitude);
GuideDBOpenHelper helper = new GuideDBOpenHelper(getActivity());
String[] columns = {
GuideDBConstants.PlaceTable.LATITUDE_COL,
GuideDBConstants.PlaceTable.LONGITUDE_COL, GuideDBConstants.PlaceTable.ID_COL
};
Cursor cursor = DBUtils.getAllPlaces(columns, helper.getReadableDatabase());
int position = Geomancer.findClosestPlace(clicked, cursor);
cursor.moveToPosition(position);
int idColIx = cursor.getColumnIndex(GuideDBConstants.PlaceTable.ID_COL);
PlaceDetailer.open(getActivity(), (int)cursor.getLong(idColIx));
helper.close();
}
@Override
public boolean onMarkerClick(Marker marker) {
if (marker.isInfoWindowShown()) {
marker.hideInfoWindow();
mPlaceIdFocused = -1;
} else {
marker.showInfoWindow();
Place plc = null;
// Had to match marker by title, because there is no
// marker id
for (Place agndPlc : mAgenda) {
if (marker.getTitle().equals(agndPlc.getName())) {
plc = agndPlc;
break;
}
}
if (plc != null) {
mPlaceIdFocused = plc.getUniqueId();
}
if (mMenu != null) {
if (mAgenda.isOnAgenda(plc)) {
// Option to remove
MenuItem item = mMenu.findItem(R.id.map_menu_remove_agenda);
item.setVisible(true);
// item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
// TODO
item = mMenu.findItem(R.id.map_menu_add_agenda);
item.setVisible(false);
item = null;
} else {
// Option to add
mMenu.findItem(R.id.map_menu_add_agenda).setVisible(true);
mMenu.findItem(R.id.map_menu_remove_agenda).setVisible(false);
}
}
}
return true;
}
@Override
public void mapGraph(SimpleWeightedGraph<MapVertex, DefaultWeightedEdge> graph) {
GoogleMap map = getMap();
map.clear();
for (MapVertex mv : graph.vertexSet()) {
if (mv.id <= GuideConstants.MAX_PLACE_ID) {
map.addMarker(new MarkerOptions().
position(new LatLng(mv.lat, mv.lon)).
title(DBUtils.getPlaceNameById(
mv.id,
GlobalState.getReadableDatabase(getActivity()))).
icon(BitmapDescriptorFactory.
defaultMarker(BitmapDescriptorFactory.HUE_AZURE))); }
else {
map.addMarker(new MarkerOptions().
position(new LatLng(mv.lat, mv.lon)).
icon(BitmapDescriptorFactory.
fromResource(R.drawable.nodemarker)).
anchor(0.5f, 0.5f));
}
}
mapEdges(map, graph);
}
private static void mapEdges(GoogleMap map, SimpleWeightedGraph<MapVertex, DefaultWeightedEdge> graph) {
for (DefaultWeightedEdge e : graph.edgeSet()) {
MapVertex mv1 = graph.getEdgeSource(e);
MapVertex mv2 = graph.getEdgeTarget(e);
map.addPolyline(new PolylineOptions().
add(new LatLng(mv1.lat, mv1.lon)).
add(new LatLng(mv2.lat, mv2.lon)).
width(5.0f));
}
}
}