package org.diretto.web.richwebclient.view.widgets.googlemap.server;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import org.diretto.api.client.base.data.ResultSet;
import org.diretto.api.client.base.data.ResultSetImpl;
import org.diretto.api.client.external.processing.ProcessingService;
import org.diretto.api.client.main.core.CoreService;
import org.diretto.api.client.main.core.entities.Attachment;
import org.diretto.api.client.main.core.entities.CoreServiceEntityIDFactory;
import org.diretto.api.client.main.core.entities.Document;
import org.diretto.api.client.main.core.entities.DocumentID;
import org.diretto.api.client.main.core.entities.Position;
import org.diretto.api.client.main.core.entities.Time;
import org.diretto.api.client.user.UserID;
import org.diretto.api.client.user.UserInfo;
import org.diretto.web.richwebclient.RichWebClientApplication;
import org.diretto.web.richwebclient.management.AuthenticationRegistry;
import org.diretto.web.richwebclient.view.util.MediaTypeUtils;
import org.diretto.web.richwebclient.view.widgets.googlemap.client.VExploreGoogleMap;
import org.diretto.web.richwebclient.view.widgets.googlemap.client.base.MapType;
import org.diretto.web.richwebclient.view.widgets.googlemap.server.base.AbstractGoogleMap;
import org.diretto.web.richwebclient.view.widgets.googlemap.server.event.GoogleMapListener;
import org.diretto.web.richwebclient.view.widgets.googlemap.server.event.LoadingProcessListener;
import org.diretto.web.richwebclient.view.windows.DocumentViewWindow;
import org.diretto.web.richwebclient.view.windows.MainWindow;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.ui.ClientWidget;
/**
* The server side component of the {@code ExploreGoogleMap}.
*
* @author Tobias Schlecht
*/
@ClientWidget(VExploreGoogleMap.class)
public final class ExploreGoogleMap extends AbstractGoogleMap
{
private static final long serialVersionUID = -5921741171570925121L;
private final List<LoadingProcessListener> loadingProcessListeners = new Vector<LoadingProcessListener>();
private static final int THUMBNAIL_SIZE = 60;
private final CoreService coreService;
private final ProcessingService processingService;
private final MainWindow mainWindow;
private final AuthenticationRegistry authenticationRegistry;
private final int paginationSize;
private final Map<DocumentID, Document> documents = new ConcurrentHashMap<DocumentID, Document>();
private final Map<DocumentID, String> userNames = new ConcurrentHashMap<DocumentID, String>();
private final Map<DocumentID, String> thumbnailURLs = new ConcurrentHashMap<DocumentID, String>();
private volatile Thread currentThread = null;
private boolean polling = true;
private boolean clearMap = false;
/**
* Constructs an {@link ExploreGoogleMap}.
*
* @param application The corresponding {@code RichWebClientApplication}
* @param zoomLevel The initial zoom level
* @param mapType The initial {@code MapType}
*/
public ExploreGoogleMap(RichWebClientApplication application, int zoomLevel, MapType mapType)
{
this(application, zoomLevel, 0.0d, 0.0d, mapType);
}
/**
* Constructs an {@link ExploreGoogleMap}.
*
* @param application The corresponding {@code RichWebClientApplication}
* @param zoomLevel The initial zoom level
* @param centerLatitude The initial center latitude in degrees
* @param centerLongitude The initial center longitude in degrees
* @param mapType The initial {@code MapType}
*/
public ExploreGoogleMap(RichWebClientApplication application, int zoomLevel, double centerLatitude, double centerLongitude, MapType mapType)
{
this(application, zoomLevel, centerLatitude, centerLongitude, mapType, true);
}
/**
* Constructs an {@link ExploreGoogleMap}.
*
* @param application The corresponding {@code RichWebClientApplication}
* @param zoomLevel The initial zoom level
* @param centerLatitude The initial center latitude in degrees
* @param centerLongitude The initial center longitude in degrees
* @param mapType The initial {@code MapType}
* @param scrollWheelZoomingEnabled {@code true} if scroll wheel zooming is
* enabled; otherwise {@code false}
*/
public ExploreGoogleMap(RichWebClientApplication application, int zoomLevel, double centerLatitude, double centerLongitude, MapType mapType, boolean scrollWheelZoomingEnabled)
{
super(zoomLevel, centerLatitude, centerLongitude, mapType, scrollWheelZoomingEnabled, true);
coreService = application.getCoreService();
processingService = application.getProcessingService();
mainWindow = (MainWindow) application.getMainWindow();
authenticationRegistry = application.getAuthenticationRegistry();
paginationSize = coreService.getPaginationSize();
}
@Override
public void paintContent(PaintTarget target) throws PaintException
{
super.paintContent(target);
target.startTag("polling");
target.addAttribute("value", polling);
target.endTag("polling");
target.startTag("clearMap");
target.addAttribute("value", clearMap);
target.endTag("clearMap");
target.startTag("documents");
for(DocumentID documentID : documents.keySet())
{
Document document = documents.remove(documentID);
String publisher = userNames.remove(documentID);
String thumbnailURL = thumbnailURLs.remove(documentID);
if(document != null && publisher != null && thumbnailURL != null)
{
Attachment attachment = document.getInitialAttachment();
Position position = document.getBestVotedPosition();
Time time = document.getBestVotedTime();
target.startTag("document");
target.addAttribute("id", documentID.toString());
target.addAttribute("title", document.getTitle());
target.addAttribute("publisher", publisher);
target.addAttribute("position-latitude", position.getLatitude());
target.addAttribute("position-longitude", position.getLongitude());
target.addAttribute("average-time", time.getAverageTime().getMillis());
target.addAttribute("media-type", MediaTypeUtils.getMediaType(document.getMediaMainType()).toString());
target.addAttribute("votes-up", attachment.getVotes().UP);
target.addAttribute("votes-down", attachment.getVotes().DOWN);
target.addAttribute("thumbnail-url", thumbnailURL);
target.endTag("document");
}
}
target.endTag("documents");
}
@Override
public void changeVariables(Object source, Map<String, Object> variables)
{
super.changeVariables(source, variables);
if(variables.containsKey("cleared") && (Boolean) variables.get("cleared"))
{
clearMap = false;
}
if(variables.containsKey("documentView"))
{
String documentIDString = (String) variables.get("documentView");
DocumentID documentID = CoreServiceEntityIDFactory.getDocumentIDInstance(documentIDString);
DocumentViewWindow documentViewWindow = new DocumentViewWindow(mainWindow, coreService, processingService, authenticationRegistry, documentID);
mainWindow.addWindow(documentViewWindow);
documentViewWindow.focus();
}
}
/**
* Adds the given {@link Document} to this {@link ExploreGoogleMap}.
*
* @param document A {@code Document}
*/
public void addDocument(Document document)
{
DocumentID documentID = document.getID();
userNames.put(documentID, coreService.getUserInfo(document.getPublisher()).getUserName());
thumbnailURLs.put(documentID, processingService.getThumbnailURL(documentID, THUMBNAIL_SIZE).toExternalForm());
documents.put(documentID, document);
requestRepaint();
}
/**
* Updates this {@link ExploreGoogleMap} with the given {@link ResultSet} of
* {@link Document}s.
*
* @param documents A {@code ResultSet} of {@code Document}
*/
public void updateDocuments(final ResultSet<DocumentID, Document> documents)
{
Thread thread = new Thread(new Runnable()
{
@Override
public void run()
{
boolean loading = false;
if(((ResultSetImpl<DocumentID, Document>) documents).getLoadedDataSize() >= paginationSize)
{
loading = true;
for(LoadingProcessListener loadingProcessListener : loadingProcessListeners)
{
loadingProcessListener.onLoadingProcessStarted();
}
requestRepaint();
}
clearMap = true;
Map<DocumentID, Document> documentsBatch = new HashMap<DocumentID, Document>(paginationSize);
Map<DocumentID, UserID> userIDsBatch = new HashMap<DocumentID, UserID>(paginationSize);
int count = 0;
for(Iterator<Document> iterator = documents.iterator(); iterator.hasNext();)
{
synchronized(ExploreGoogleMap.this)
{
if(!Thread.currentThread().equals(currentThread))
{
ExploreGoogleMap.this.documents.clear();
userNames.clear();
thumbnailURLs.clear();
if(loading)
{
for(LoadingProcessListener loadingProcessListener : loadingProcessListeners)
{
loadingProcessListener.onLoadingProcessFinished();
}
}
requestRepaint();
currentThread.start();
return;
}
}
Document document = iterator.next();
DocumentID documentID = document.getID();
documentsBatch.put(documentID, document);
userIDsBatch.put(documentID, document.getPublisher());
thumbnailURLs.put(documentID, processingService.getThumbnailURL(documentID, THUMBNAIL_SIZE).toExternalForm());
count++;
if(count == paginationSize || !iterator.hasNext())
{
ResultSet<UserID, UserInfo> userInfos = coreService.getUserInfosByIDs(new Vector<UserID>(userIDsBatch.values()));
for(DocumentID id : userIDsBatch.keySet())
{
String userName = userInfos.get(userIDsBatch.get(id)).getUserName();
userNames.put(id, userName);
ExploreGoogleMap.this.documents.put(id, documentsBatch.get(id));
}
documentsBatch.clear();
userIDsBatch.clear();
count = 0;
requestRepaint();
}
}
if(loading)
{
for(LoadingProcessListener loadingProcessListener : loadingProcessListeners)
{
loadingProcessListener.onLoadingProcessFinished();
}
}
requestRepaint();
synchronized(ExploreGoogleMap.this)
{
if(Thread.currentThread().equals(currentThread))
{
currentThread = null;
}
else
{
currentThread.start();
}
}
}
});
synchronized(this)
{
if(currentThread == null)
{
currentThread = thread;
currentThread.start();
}
else
{
currentThread = thread;
}
}
}
/**
* Activates polling of the client side component.
*/
public void activatePolling()
{
polling = true;
requestRepaint();
}
/**
* Deactivates polling of the client side component.
*/
public void deactivatePolling()
{
polling = false;
requestRepaint();
}
/**
* Adds the given {@link LoadingProcessListener}.
*
* @param loadingProcessListener A {@code LoadingProcessListener}
*/
public synchronized void addLoadingProcessListener(LoadingProcessListener loadingProcessListener)
{
if(!loadingProcessListeners.contains(loadingProcessListener))
{
loadingProcessListeners.add(loadingProcessListener);
}
}
/**
* Removes the given {@link GoogleMapListener}.
*
* @param googleMapListener A {@code GoogleMapListener}
*/
public synchronized void removeListener(GoogleMapListener googleMapListener)
{
if(googleMapListener instanceof LoadingProcessListener)
{
loadingProcessListeners.remove(googleMapListener);
}
}
}