package com.opendoorlogistics.core.scripts.formulae.image;
import com.opendoorlogistics.api.ExecutionReport;
import com.opendoorlogistics.api.tables.ODLDatastore;
import com.opendoorlogistics.api.tables.ODLTable;
import com.opendoorlogistics.api.tables.ODLTableDefinition;
import com.opendoorlogistics.api.tables.ODLTableReadOnly;
import com.opendoorlogistics.core.formulae.Function;
import com.opendoorlogistics.core.formulae.FunctionFactory;
import com.opendoorlogistics.core.formulae.FunctionUtils;
import com.opendoorlogistics.core.formulae.definitions.FunctionDefinition;
import com.opendoorlogistics.core.formulae.definitions.FunctionDefinitionLibrary;
import com.opendoorlogistics.core.formulae.definitions.FunctionDefinition.ArgumentType;
import com.opendoorlogistics.core.formulae.definitions.FunctionDefinition.FunctionType;
import com.opendoorlogistics.core.gis.map.RenderProperties;
import com.opendoorlogistics.core.gis.map.annotations.ImageFormulaKey;
import com.opendoorlogistics.core.gis.map.data.DrawableObjectImpl;
import com.opendoorlogistics.core.scripts.TableReference;
import com.opendoorlogistics.core.scripts.elements.AdapterConfig;
import com.opendoorlogistics.core.scripts.execution.adapters.AdapterBuilderUtils;
import com.opendoorlogistics.core.scripts.execution.adapters.IndexedDatastores;
import com.opendoorlogistics.core.scripts.formulae.image.FmImageWithView.IWVMode;
import com.opendoorlogistics.core.tables.beans.BeanMapping.BeanDatastoreMapping;
import com.opendoorlogistics.core.utils.strings.Strings;
public class ImageFormulaeCreator {
private static class ArgIndices {
int lookupVal = -1;
int tableToDrawRef = -1;
int tableForZoomDrawRef = -1;
int mode = -1;
int height = -1;
int width = -1;
int dpCM = -1;
int renderProp = -1;
}
public static void buildImageFormulae(FunctionDefinitionLibrary library, final IndexedDatastores<? extends ODLTable> datastores, final ExecutionReport result) {
for (final boolean withView : new boolean[] { false, true }) {
for (final boolean printable : new boolean[] { false, true }) {
for (final boolean includeProperties : new boolean[] { false, true }) {
// construct the definition
final ArgIndices argIndices = new ArgIndices();
FunctionDefinition dfn = new FunctionDefinition(FunctionType.FUNCTION, getFormulaName(withView, printable));
if (withView) {
dfn.setDescription("Draw an image using all objects in the to-draw-table, taking the zoom and view centre from the objects in the to-zoom-table whose image formula key matches to-zoom-lookup-value. "
+ "Both tables must contain the standard drawable fields.");
argIndices.tableToDrawRef = dfn.addArg("to-draw-table-reference", ArgumentType.TABLE_REFERENCE_CONSTANT,
"Reference to the drawable table including the datastore name - e.g. \"external, drawabletable\".");
argIndices.lookupVal = dfn.addArg("to-zoom-lookup-value", "Filter objects in the to-zoom table whose image formula key field has this value.");
argIndices.tableForZoomDrawRef = dfn.addArg("to-zoom-table-table-reference", ArgumentType.TABLE_REFERENCE_CONSTANT,
"Reference to the to-zoom-table including the datastore name - e.g. \"external, drawabletable\".");
} else {
dfn.setDescription("Draw an image using the referenced drawable-table, filtering the objects based on the row in current table.");
argIndices.lookupVal = dfn.addArg("lookup_value", "Filter objects in the drawable table whose image formula key field has this value.");
argIndices.tableToDrawRef = dfn.addArg("drawable-table-reference", ArgumentType.TABLE_REFERENCE_CONSTANT,
"Reference to the drawable table including the datastore name - e.g. \"external, drawabletable\".");
}
addStandardArguments(withView,printable, includeProperties, argIndices, dfn);
dfn.setFactory(new FunctionFactory() {
@Override
public Function createFunction(Function... children) {
// parse the table reference for the drawables table
ODLTableReadOnly pointsTable = getDrawablesTable(children[argIndices.tableToDrawRef], datastores, result);
// get the to-zoom table if we have one
ODLTableReadOnly zoomTable= null;
if(argIndices.tableForZoomDrawRef!=-1){
zoomTable = getDrawablesTable(children[argIndices.tableForZoomDrawRef], datastores, result);
}
// get properties
RenderProperties properties = getRenderProperties(argIndices, children);
if (withView) {
return createFmImageWithViewFormula(argIndices, pointsTable,zoomTable, properties, children);
} else {
return createFmImageFormula(argIndices, pointsTable, properties, children);
}
}
});
library.add(dfn);
}
}
}
}
private static String getFormulaName(boolean withView, boolean printable) {
return (printable ? "printableImage" : "image") + (withView ? "WithView" : "");
}
private static void addStandardArguments(final boolean withView,final boolean isPrintable, final boolean includeProperties, final ArgIndices argIndices, FunctionDefinition dfn) {
StringBuilder builder = new StringBuilder();
if(withView){
for (FmImageWithView.IWVMode mode : FmImageWithView.IWVMode.values()) {
builder.append(" " + mode.getKeyword() + " = " + mode.getDescription());
}
}else{
for (FmImage.Mode mode : FmImage.Mode.values()) {
builder.append(" " + mode.getKeyword() + " = " + mode.getDescription());
}
}
argIndices.mode = dfn.addArg("Mode", ArgumentType.STRING_CONSTANT, "Create image mode." + builder.toString());
if (isPrintable) {
argIndices.width = dfn.addArg("width", "Image width in centimeters.");
argIndices.height = dfn.addArg("height", "Image height in centimeters.");
argIndices.dpCM = dfn.addArg("dots_per_cm", "Dots per centimeter");
} else {
argIndices.width = dfn.addArg("width", "Image width in pixels.");
argIndices.height = dfn.addArg("height", "Image height in pixels.");
}
if (includeProperties) {
argIndices.renderProp = dfn.addArg("render-properties", ArgumentType.STRING_CONSTANT, "A string containing key value pairs, for example \"legend=topleft\".");
}
}
private static ODLTableReadOnly getDrawablesTable(Function oTableRef, final IndexedDatastores<? extends ODLTable> datastores, final ExecutionReport result) {
String sTableRef = FunctionUtils.getConstantString(oTableRef);
TableReference tableRef = TableReference.create(sTableRef, result);
if (tableRef == null) {
throw new RuntimeException("Error reading table reference in an image formula.");
}
// find the drawable table ...
int dsIndx = datastores.getIndex(tableRef.getDatastoreName());
if (dsIndx == -1) {
throw new RuntimeException("Error getting datastore " + tableRef.getDatastoreName() + " used in an image formula.");
}
ODLDatastore<? extends ODLTable> ds = datastores.getDatastore(dsIndx);
if (ds == null || result.isFailed()) {
throw new RuntimeException("Error getting datastore " + tableRef + " used in an image formula.");
}
// do simple adaption of table to drawable datastore definition to ensure table format is exact
ODLDatastore<? extends ODLTableDefinition> definition = DrawableObjectImpl.getBeanMapping().getDefinition();
AdapterConfig adapterConfig = AdapterConfig.createSameNameMapper(definition);
adapterConfig.getTables().get(0).setFromTable(tableRef.getTableName());
ODLDatastore<ODLTable> simpleAdapted = AdapterBuilderUtils.createSimpleAdapter(ds, adapterConfig, result);
if (simpleAdapted == null || result.isFailed()) {
throw new RuntimeException("Error matching table " + tableRef + " used in formula image to the table expected by the map renderer.");
}
ODLTableReadOnly pointsTable = simpleAdapted.getTableAt(0);
return pointsTable;
}
private static RenderProperties getRenderProperties(final ArgIndices argIndices, Function... children) {
// get flags
RenderProperties properties = new RenderProperties();
if (argIndices.renderProp != -1) {
String s = FunctionUtils.getConstantString(children[argIndices.renderProp]);
properties = new RenderProperties(s);
}
// add default flags
properties.addFlags(RenderProperties.SHOW_ALL);
return properties;
}
private static Function createFmImageWithViewFormula(final ArgIndices argIndices, ODLTableReadOnly pointsTable,ODLTableReadOnly zoomTable, RenderProperties properties,
Function... children) {
// find the group key field
int groupKeyColumnIndex = getImageFilterColumnIndex();
// get the mode
FmImageWithView.IWVMode mode = null;
String sMode = FunctionUtils.getConstantString(children[argIndices.mode]);
for (FmImageWithView.IWVMode m : FmImageWithView.IWVMode.values()) {
if (Strings.equalsStd(m.getKeyword(), sMode)) {
mode = m;
}
}
if (mode == null) {
throw new RuntimeException("Unknown image function mode: " + sMode);
}
return new FmImageWithView(pointsTable,zoomTable, groupKeyColumnIndex,children[argIndices.lookupVal], mode, children[argIndices.width],
children[argIndices.height], argIndices.dpCM!=-1? children[argIndices.dpCM]:null,properties);
}
private static Function createFmImageFormula(final ArgIndices argIndices, ODLTableReadOnly pointsTable, RenderProperties properties,
Function... children) {
// find the group key field
int groupKeyColumnIndex = getImageFilterColumnIndex();
// get the mode
FmImage.Mode mode = null;
String sMode = FunctionUtils.getConstantString(children[argIndices.mode]);
for (FmImage.Mode m : FmImage.Mode.values()) {
if (Strings.equalsStd(m.getKeyword(), sMode)) {
mode = m;
}
}
if (mode == null) {
throw new RuntimeException("Unknown image function mode: " + sMode);
}
if (argIndices.dpCM != -1) {
return FmImage.createFixedPhysicalSize(children[argIndices.lookupVal], pointsTable, groupKeyColumnIndex, mode, children[argIndices.width], children[argIndices.height],
children[argIndices.dpCM], properties);
} else {
return FmImage.createFixedPixelSize(children[argIndices.lookupVal], pointsTable, groupKeyColumnIndex, mode, children[argIndices.width], children[argIndices.height],
properties);
}
}
private static int getImageFilterColumnIndex() {
BeanDatastoreMapping beanMap = DrawableObjectImpl.getBeanMapping();
int groupKeyColumnIndex = beanMap.getTableMapping(0).indexOfAnnotation(ImageFormulaKey.class);
if (groupKeyColumnIndex == -1) {
throw new RuntimeException("Could not find group key field in drawable lat long table, used in image formula.");
}
return groupKeyColumnIndex;
}
}