package controllers;
import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.collect.Iterables.getFirst;
import helpers.FileStoreHelper;
import java.awt.Dimension;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jcr.Session;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;
import org.jcrom.Jcrom;
import play.libs.F;
import play.libs.Json;
import play.mvc.Call;
import play.mvc.Result;
import play.mvc.With;
import providers.CacheableUserProvider;
import service.JcrSessionFactory;
import service.filestore.FileStore;
import charts.ChartDescription;
import charts.ChartType;
import charts.Region;
import charts.builder.ChartBuilder;
import charts.representations.Format;
import charts.representations.Representation;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
@With(UncacheableAction.class)
public class Chart extends SessionAwareController {
private final FileStore fileStore;
private final ChartBuilder chartBuilder;
@Inject
public Chart(final JcrSessionFactory sessionFactory, final Jcrom jcrom,
final CacheableUserProvider sessionHandler, final FileStore fileStore,
final ChartBuilder chartBuilder) {
super(sessionFactory, jcrom, sessionHandler);
this.fileStore = fileStore;
this.chartBuilder = chartBuilder;
}
@SubjectPresent
public Result charts(final String format, final String id) throws Exception {
if (!fileIsAccessible(id)) {
return notFound();
}
final List<charts.Chart> charts =
chartBuilder.getCharts(id, null,
getRegions(request().queryString()), null);
final ObjectNode json = Json.newObject();
final ArrayNode aNode = json.putArray("charts");
for (charts.Chart chart : charts) {
ChartDescription desc = chart.getDescription();
final ObjectNode chartNode = Json.newObject();
chartNode.put("type", desc.getType().getLabel());
chartNode.put("region", desc.getRegion().getName());
chartNode.put("title", desc.getTitle());
for (String pname : desc.getParameterNames()) {
chartNode.put(pname, desc.getParameter(pname).toString());
}
try {
chartNode.put("url", buildUrl(chart, format, id));
} catch (UnsupportedEncodingException e) {
// Not going to happen
throw new RuntimeException(e);
}
aNode.add(chartNode);
}
return ok(json).as("application/json; charset=utf-8");
}
private Map<String, String> getParameters() {
Map<String, String> parameters = Maps.newHashMap();
for(Map.Entry<String, String[]> me : request().queryString().entrySet()) {
if(me.getValue() != null && me.getValue().length > 0) {
parameters.put(me.getKey(), me.getValue()[0]);
}
}
return parameters;
}
@SubjectPresent
public Result chart(final String chartType,
final String formatStr, final String id) throws Exception {
if (!fileIsAccessible(id)) {
return notFound();
}
final ChartType type;
final Format format;
try {
type = ChartType.getChartType(chartType);
} catch (IllegalArgumentException e) {
return notFound("unknown chart type " + chartType);
}
try {
format = Format.valueOf(formatStr.toUpperCase());
} catch (IllegalArgumentException e) {
return notFound("unknown chart format: " + formatStr);
}
final List<charts.Chart> charts = chartBuilder.getCharts(id,
type, getRegions(request().queryString()), getParameters());
for (charts.Chart chart : charts) {
try {
if(!modified(chart.created())) {
return status(NOT_MODIFIED);
}
} catch(Exception e) {}
try {
final Representation r = chart.outputAs(format,
getQueryDimensions(request().queryString()));
ctx().response().setHeader("Last-Modified", asHttpDate(chart.created()));
return ok(r.getContent()).as(r.getContentType());
} catch (charts.Chart.UnsupportedFormatException e) {
continue;
}
}
return notFound();
}
private boolean fileIsAccessible(final String id) {
return inUserSession(new F.Function<Session, Boolean>() {
@Override
public Boolean apply(Session session) throws Throwable {
final FileStore.FileOrFolder fof = fileStore.getManager(session)
.getByIdentifier(id);
return fof != null && fof instanceof FileStore.File;
}
});
}
private String buildUrl(charts.Chart chart, String format, String id)
throws UnsupportedEncodingException {
return buildUrl(controllers.routes.Chart.chart(
chart.getDescription().getType().toString().toLowerCase(),
format, id), chart);
}
private String buildUrl(Call call, charts.Chart chart)
throws UnsupportedEncodingException {
return call.url() + (call.url().contains("?") ? "&" : "?") +
URLEncodedUtils.format(getParameters(chart), "UTF-8");
}
private List<BasicNameValuePair> getParameters(charts.Chart chart) {
List<BasicNameValuePair> l = Lists.newArrayList();
l.add(new BasicNameValuePair("region",
chart.getDescription().getRegion().getName()));
for(String pname : chart.getDescription().getParameterNames()) {
l.add(new BasicNameValuePair(pname,
chart.getDescription().getParameter(pname).toString()));
}
return l;
}
public static List<FileStore.File> getFilesFromID(
FileStore fileStore, Session session, String id) throws Exception {
final FileStoreHelper fsh = new FileStoreHelper(session);
final List<FileStore.File> files = Lists.newLinkedList();
final FileStore.Manager fm = fileStore.getManager(session);
FileStore.FileOrFolder fof = fm.getByIdentifier(id);
if (fof instanceof FileStore.File) {
files.add((FileStore.File) fof);
} else if (fof instanceof FileStore.Folder) {
files.addAll(fsh.listFilesInFolder((FileStore.Folder) fof));
}
return files;
}
protected static Set<String> getValues(Map<String, String[]> m, String key) {
return ImmutableSet.copyOf(firstNonNull(m.get(key), new String[0]));
}
protected static List<Region> getRegions(Map<String, String[]> query) {
return Lists.newArrayList(Region.getRegions(getValues(query, "region")));
}
protected static Dimension getQueryDimensions(Map<String, String[]> query) {
final Dimension queryDimensions = new Dimension();
try {
double w = Double.parseDouble(getFirst(getValues(query, "width"), ""));
queryDimensions.setSize(w, queryDimensions.getHeight());
} catch (Exception e) {}
try {
double h = Double.parseDouble(getFirst(getValues(query, "height"), ""));
queryDimensions.setSize(queryDimensions.getWidth(), h);
} catch (Exception e) {}
return queryDimensions;
}
}