package org.openlca.app.results;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.openlca.app.App;
import org.openlca.app.M;
import org.openlca.app.components.ContributionImage;
import org.openlca.app.db.Cache;
import org.openlca.app.db.Database;
import org.openlca.app.rcp.images.Images;
import org.openlca.app.util.Actions;
import org.openlca.app.util.DQUI;
import org.openlca.app.util.Labels;
import org.openlca.app.util.Numbers;
import org.openlca.app.util.UI;
import org.openlca.app.util.tables.TableClipboard;
import org.openlca.app.util.tables.Tables;
import org.openlca.app.util.viewers.Viewers;
import org.openlca.core.database.CurrencyDao;
import org.openlca.core.database.EntityCache;
import org.openlca.core.math.data_quality.DQResult;
import org.openlca.core.matrix.LongPair;
import org.openlca.core.matrix.TechIndex;
import org.openlca.core.model.Currency;
import org.openlca.core.model.FlowType;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.descriptors.FlowDescriptor;
import org.openlca.core.model.descriptors.ProcessDescriptor;
import org.openlca.core.results.ContributionResultProvider;
import org.openlca.core.results.SimpleResultProvider;
/**
* The total requirements section that is shown on the TotalFlowResultPage.
*/
class TotalRequirementsSection {
private EntityCache cache = Cache.getEntityCache();
private SimpleResultProvider<?> result;
private DQResult dqResult;
private Costs costs;
private String currencySymbol;
private Map<Long, ProcessDescriptor> processDescriptors = new HashMap<>();
private TableViewer table;
TotalRequirementsSection(SimpleResultProvider<?> result, DQResult dqResult) {
this.result = result;
for (ProcessDescriptor desc : result.getProcessDescriptors())
processDescriptors.put(desc.getId(), desc);
if (!result.hasCostResults())
costs = Costs.NONE;
else
costs = result.getTotalCostResult() >= 0 ? Costs.NET_COSTS : Costs.ADDED_VALUE;
this.dqResult = dqResult;
}
void create(Composite body, FormToolkit tk) {
Section section = UI.section(body, tk, M.TotalRequirements);
UI.gridData(section, true, true);
Composite comp = UI.sectionClient(section, tk);
UI.gridLayout(comp, 1);
Label label = new Label();
table = Tables.createViewer(comp, columnLabels(), label);
Tables.bindColumnWidths(table, DQUI.MIN_COL_WIDTH, columnWidths());
Viewers.sortByLabels(table, label, 0, 1, 3);
Viewers.sortByDouble(table, (Item i) -> i.amount, 2);
if (costs != Costs.NONE)
Viewers.sortByDouble(table, (Item i) -> i.costValue, 4);
if (DQUI.displayProcessQuality(dqResult)) {
int startCol = costs == Costs.NONE ? 4 : 5;
for (int i = 0; i < dqResult.setup.processDqSystem.indicators.size(); i++) {
Viewers.sortByDouble(table, label, i + startCol);
}
}
for (int col : numberColumns()) {
table.getTable().getColumns()[col].setAlignment(SWT.RIGHT);
}
Actions.bind(table, TableClipboard.onCopy(table));
Tables.onDoubleClick(table, e -> {
Item item = Viewers.getFirstSelected(table);
if (item != null) {
App.openEditor(item.process);
}
});
createCostSum(comp, tk);
}
private void createCostSum(Composite comp, FormToolkit tk) {
if (costs == Costs.NONE)
return;
double v = result.getTotalCostResult();
String s;
String c;
if (costs == Costs.NET_COSTS) {
s = M.TotalNetcosts;
c = asCosts(v);
} else {
s = M.TotalAddedValue;
c = asCosts(v == 0 ? 0 : -v);
}
Control label = tk.createLabel(comp, s + ": " + c);
label.setFont(UI.boldFont());
}
void fill() {
if (table == null)
return;
table.setInput(createItems());
}
private String[] columnLabels() {
List<String> b = new ArrayList<>();
b.add(M.Process);
b.add(M.Product);
b.add(M.Amount);
b.add(M.Unit);
if (costs == Costs.ADDED_VALUE)
b.add(M.AddedValue);
else if (costs == Costs.NET_COSTS)
b.add(M.NetCosts);
String[] columnLabels = b.toArray(new String[b.size()]);
if (!DQUI.displayProcessQuality(dqResult))
return columnLabels;
return DQUI.appendTableHeaders(columnLabels, dqResult.setup.processDqSystem);
}
private int[] numberColumns() {
if (costs == Costs.NONE || costs == null)
return new int[] { 2 };
return new int[] { 2, 4 };
}
private double[] columnWidths() {
double[] widths = null;
if (costs == Costs.NONE)
widths = new double[] { .4, .2, .2, .2 };
else
widths = new double[] { .4, .2, .2, .1, .1 };
if (!DQUI.displayProcessQuality(dqResult))
return widths;
return DQUI.adjustTableWidths(widths, dqResult.setup.processDqSystem);
}
private List<Item> createItems() {
if (result == null || result.result == null)
return Collections.emptyList();
double[] tr = result.result.totalRequirements;
if (tr == null)
return Collections.emptyList();
List<Item> items = new ArrayList<>();
for (int i = 0; i < tr.length; i++) {
items.add(new Item(i, tr[i]));
}
calculateCostChares(items);
Collections.sort(items, (a, b) -> -Double.compare(a.amount, b.amount));
return items;
}
private void calculateCostChares(List<Item> items) {
if (costs == Costs.NONE)
return;
double max = 0;
for (Item item : items) {
max = Math.max(max, item.costValue);
}
if (max == 0)
return;
for (Item item : items) {
item.costShare = item.costValue / max;
}
}
private String asCosts(double value) {
if (currencySymbol == null) {
try {
CurrencyDao dao = new CurrencyDao(Database.get());
Currency ref = dao.getReferenceCurrency();
currencySymbol = ref.code != null ? ref.code : ref.getName();
} catch (Exception e) {
currencySymbol = "?";
}
}
return Numbers.decimalFormat(value, 2) + " " + currencySymbol;
}
private enum Costs {
ADDED_VALUE, NET_COSTS, NONE
}
private class Item {
ProcessDescriptor process;
String product;
double amount;
String unit;
double costValue;
double costShare;
Item(int idx, double amount) {
this.amount = amount;
init(idx);
}
private void init(int idx) {
TechIndex index = result.result.productIndex;
if (index == null)
return;
setProcessProduct(index, idx);
setCostValue(idx);
}
private void setProcessProduct(TechIndex techIdx, int idx) {
LongPair lp = techIdx.getProviderAt(idx);
if (lp == null)
return;
ProcessDescriptor process = processDescriptors.get(lp.getFirst());
if (process != null) {
this.process = process;
}
FlowDescriptor flow = cache.get(FlowDescriptor.class, lp.getSecond());
if (flow != null) {
this.product = Labels.getDisplayName(flow);
this.unit = Labels.getRefUnit(flow, cache);
}
}
private void setCostValue(int idx) {
if (costs == Costs.NONE)
return;
if (!(result instanceof ContributionResultProvider))
return;
ContributionResultProvider<?> crp = (ContributionResultProvider<?>) result;
double[] vals = crp.result.singleCostResults;
if (vals.length > idx && idx >= 0) {
double v = vals[idx];
costValue = costs == Costs.NET_COSTS ? v : v != 0 ? -v : 0;
}
}
}
private class Label extends DQLabelProvider {
private ContributionImage costImage = new ContributionImage(
UI.shell().getDisplay());
public Label() {
super(dqResult, dqResult != null ? dqResult.setup.processDqSystem : null, costs == Costs.NONE ? 4 : 5);
}
@Override
public void dispose() {
costImage.dispose();
super.dispose();
}
@Override
public Image getImage(Object obj, int col) {
if (!(obj instanceof Item))
return null;
switch (col) {
case 0:
return Images.get(ModelType.PROCESS);
case 1:
return Images.get(FlowType.PRODUCT_FLOW);
case 4:
if (costs == Costs.NONE)
return null;
return costImage.getForTable(((Item) obj).costShare);
default:
return null;
}
}
@Override
public String getText(Object obj, int col) {
if (!(obj instanceof Item))
return null;
Item item = (Item) obj;
switch (col) {
case 0:
return Labels.getDisplayName(item.process);
case 1:
return item.product;
case 2:
return Numbers.format(item.amount);
case 3:
return item.unit;
case 4:
return asCosts(item.costValue);
default:
return null;
}
}
@Override
protected double[] getQuality(Object obj) {
if (!(obj instanceof Item))
return null;
Item item = (Item) obj;
return dqResult.get(item.process);
}
}
}