package edu.ualberta.med.biobank.dialogs.dispatch;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import edu.ualberta.med.biobank.BiobankPlugin;
import edu.ualberta.med.biobank.SessionManager;
import edu.ualberta.med.biobank.common.action.Action;
import edu.ualberta.med.biobank.common.action.scanprocess.CellInfo;
import edu.ualberta.med.biobank.common.action.scanprocess.CellInfoStatus;
import edu.ualberta.med.biobank.common.action.scanprocess.result.CellProcessResult;
import edu.ualberta.med.biobank.common.action.scanprocess.result.ProcessResult;
import edu.ualberta.med.biobank.common.action.scanprocess.result.ScanProcessResult;
import edu.ualberta.med.biobank.common.util.RowColPos;
import edu.ualberta.med.biobank.common.wrappers.CenterWrapper;
import edu.ualberta.med.biobank.common.wrappers.ContainerLabelingSchemeWrapper;
import edu.ualberta.med.biobank.common.wrappers.ModelWrapper;
import edu.ualberta.med.biobank.forms.utils.PalletScanManagement;
import edu.ualberta.med.biobank.gui.common.BgcPlugin;
import edu.ualberta.med.biobank.gui.common.dialogs.BgcBaseDialog;
import edu.ualberta.med.biobank.gui.common.widgets.BgcBaseText;
import edu.ualberta.med.biobank.validators.ScannerBarcodeValidator;
import edu.ualberta.med.biobank.widgets.grids.ScanPalletWidget;
import edu.ualberta.med.biobank.widgets.grids.cell.PalletCell;
import edu.ualberta.med.biobank.widgets.grids.cell.UICellStatus;
import edu.ualberta.med.scannerconfig.dmscanlib.ScanCell;
import edu.ualberta.med.scannerconfig.preferences.scanner.profiles.ProfileManager;
public abstract class AbstractScanDialog<T extends ModelWrapper<?>> extends
BgcBaseDialog {
private static final String TITLE = "Scanning specimens";
private BgcBaseText plateToScanText;
private String plateToScan;
private PalletScanManagement palletScanManagement;
protected ScanPalletWidget spw;
protected T currentShipment;
private IObservableValue scanHasBeenLaunchedValue = new WritableValue(
Boolean.FALSE, Boolean.class);
private IObservableValue scanOkValue = new WritableValue(Boolean.TRUE,
Boolean.class);
private IObservableValue hasValues = new WritableValue(Boolean.FALSE,
Boolean.class);
private Button scanButton;
private Button scanTubeAloneSwitch;
private boolean rescanMode = false;
protected CenterWrapper<?> currentSite;
public AbstractScanDialog(Shell parentShell, final T currentShipment,
CenterWrapper<?> currentSite) {
super(parentShell);
this.currentShipment = currentShipment;
this.currentSite = currentSite;
palletScanManagement = new PalletScanManagement() {
@Override
public boolean isScanTubeAloneMode() {
// FIXME: see issue #1230. always activate this mode
return true;
}
@Override
protected void beforeThreadStart() {
AbstractScanDialog.this.beforeScanThreadStart();
}
@Override
protected void processScanResult(IProgressMonitor monitor)
throws Exception {
setScanHasBeenLaunched(true);
AbstractScanDialog.this.processScanResult(monitor,
AbstractScanDialog.this.currentSite);
setHasValues();
}
@Override
protected Map<RowColPos, PalletCell> getFakeScanCells()
throws Exception {
return AbstractScanDialog.this.getFakeScanCells();
}
@Override
protected void afterScanAndProcess() {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
spw.setCells(getCells());
setRescanMode(true);
}
});
}
@Override
protected void postprocessScanTubeAlone(PalletCell cell)
throws Exception {
AbstractScanDialog.this.postprocessScanTubeAlone(cell);
setHasValues();
}
@Override
protected boolean canScanTubeAlone(PalletCell cell) {
return AbstractScanDialog.this.canScanTubeAlone(cell);
}
};
}
@Override
protected String getTitleAreaTitle() {
return TITLE;
}
@Override
protected String getDialogShellTitle() {
return TITLE;
}
protected void setRescanMode(boolean isOn) {
if (isOn) {
scanButton.setText("Retry scan");
} else {
String scanButtonText =
"Launch Scan";
if (!BiobankPlugin.isRealScanEnabled()) {
scanButtonText = "Fake scan";
}
scanButton.setText(scanButtonText);
}
rescanMode = isOn;
}
protected void beforeScanThreadStart() {
// default does nothing
}
protected boolean isRescanMode() {
return rescanMode;
}
protected abstract Map<RowColPos, PalletCell> getFakeScanCells()
throws Exception;
protected void processScanResult(IProgressMonitor monitor,
CenterWrapper<?> currentCenter) throws Exception {
Assert.isNotNull(SessionManager.getUser().getCurrentWorkingCenter());
if (checkBeforeProcessing(currentCenter)) {
Map<RowColPos, PalletCell> cells = getCells();
// conversion for server side call
Map<RowColPos, edu.ualberta.med.biobank.common.action.scanprocess.CellInfo> serverCells =
null;
if (cells != null) {
serverCells =
new HashMap<RowColPos, edu.ualberta.med.biobank.common.action.scanprocess.CellInfo>();
for (Entry<RowColPos, PalletCell> entry : cells.entrySet()) {
serverCells.put(entry.getKey(), entry.getValue()
.transformIntoServerCell());
}
}
// server side call
ScanProcessResult res = (ScanProcessResult) SessionManager
.getAppService().doAction(
getPalletProcessAction(SessionManager.getUser()
.getCurrentWorkingCenter().getId(),
serverCells, isRescanMode(),
Locale.getDefault()));
if (cells != null) {
// for each cell, convert into a client side cell
for (Entry<RowColPos, edu.ualberta.med.biobank.common.action.scanprocess.CellInfo> entry : res
.getCells().entrySet()) {
RowColPos rcp = entry.getKey();
monitor.subTask(NLS.bind(
"Processing position {0} ",
ContainerLabelingSchemeWrapper.rowColToSbs(rcp)));
PalletCell palletCell = cells.get(entry.getKey());
CellInfo servercell = entry.getValue();
if (palletCell == null) { // can happened if missing
palletCell = new PalletCell(new ScanCell(
servercell.getRow(), servercell.getCol(),
servercell.getValue()));
cells.put(rcp, palletCell);
}
palletCell
.merge(SessionManager.getAppService(), servercell);
specificScanPosProcess(palletCell);
}
}
setScanOkValue(res.getProcessStatus() != CellInfoStatus.ERROR);
} else {
setScanOkValue(false);
}
}
@SuppressWarnings("unused")
protected void specificScanPosProcess(PalletCell palletCell) {
// default do nothing
}
@SuppressWarnings("unused")
protected boolean checkBeforeProcessing(CenterWrapper<?> currentCenter)
throws Exception {
return true;
}
@Override
protected void createDialogAreaInternal(Composite parent) throws Exception {
Composite contents = new Composite(parent, SWT.NONE);
contents.setLayout(new GridLayout(2, false));
contents.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
createCustomDialogPreContents(contents);
plateToScanText =
(BgcBaseText) createBoundWidgetWithLabel(contents,
BgcBaseText.class, SWT.NONE,
"Plate to scan", new String[0],
this, "plateToScan", new ScannerBarcodeValidator(
"Enter a valid plate barcode"));
plateToScanText.addListener(SWT.DefaultSelection, new Listener() {
@Override
public void handleEvent(Event e) {
if (scanButton.isEnabled()) {
launchScan();
}
}
});
String scanButtonText = "Launch Scan";
if (!BiobankPlugin.isRealScanEnabled()) {
scanButtonText = "Fake scan";
}
scanButton = new Button(contents, SWT.PUSH);
scanButton.setText(scanButtonText);
scanButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
launchScan();
}
});
scanButton.setEnabled(false);
createScanTubeAloneButton(contents);
spw = new ScanPalletWidget(contents, getPalletCellStatus());
GridData gd = new GridData();
gd.horizontalSpan = 2;
spw.setLayoutData(gd);
spw.addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(MouseEvent e) {
if (isScanHasBeenLaunched())
palletScanManagement.scanTubeAlone(e);
}
});
widgetCreator.addBooleanBinding(new WritableValue(Boolean.FALSE,
Boolean.class), scanOkValue,
"Error in scan result. Please keep only specimens with no errors.",
IStatus.ERROR);
widgetCreator.addBooleanBinding(new WritableValue(Boolean.FALSE,
Boolean.class), scanHasBeenLaunchedValue,
"Scan should be launched", IStatus.ERROR);
widgetCreator.addBooleanBinding(new WritableValue(Boolean.FALSE,
Boolean.class), hasValues, "No values scanned",
IStatus.ERROR);
}
@SuppressWarnings("unused")
protected void createCustomDialogPreContents(Composite parent) {
// default does nothing
}
protected abstract List<UICellStatus> getPalletCellStatus();
private void launchScan() {
setScanOkValue(false);
palletScanManagement.launchScanAndProcessResult(plateToScan,
ProfileManager.ALL_PROFILE_NAME, isRescanMode());
}
protected void startNewPallet() {
spw.setCells(null);
scanHasBeenLaunchedValue.setValue(false);
setRescanMode(false);
}
protected void setScanOkValue(final boolean resOk) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
scanOkValue.setValue(resOk);
}
});
}
protected void setHasValues() {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
hasValues.setValue(getCells().size() > 0);
}
});
}
private void setScanHasBeenLaunched(final boolean launched) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
scanHasBeenLaunchedValue.setValue(launched);
}
});
}
private boolean isScanHasBeenLaunched() {
return scanHasBeenLaunchedValue.getValue().equals(true);
}
protected Map<RowColPos, PalletCell> getCells() {
return palletScanManagement.getCells();
}
protected void redrawPallet() {
spw.redraw();
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.CANCEL_ID,
"Cancel current pallet", false);
createButton(parent, IDialogConstants.PROCEED_ID,
getProceedButtonlabel(), false);
createButton(parent, IDialogConstants.NEXT_ID,
"Start next Pallet", false);
createButton(parent, IDialogConstants.FINISH_ID,
IDialogConstants.FINISH_LABEL, false);
}
protected abstract String getProceedButtonlabel();
@Override
protected void setOkButtonEnabled(boolean enabled) {
Button proceedButton = getButton(IDialogConstants.PROCEED_ID);
Button finishButton = getButton(IDialogConstants.FINISH_ID);
Button nextButton = getButton(IDialogConstants.NEXT_ID);
if (finishButton != null && !finishButton.isDisposed()) {
if (canActivateProceedButton())
proceedButton.setEnabled(enabled);
else
proceedButton.setEnabled(false);
if (canActivateNextAndFinishButton()) {
finishButton.setEnabled(enabled);
nextButton.setEnabled(enabled);
} else {
finishButton.setEnabled(false);
nextButton.setEnabled(false);
}
} else {
okButtonEnabled = enabled;
}
}
protected boolean canActivateNextAndFinishButton() {
return true;
}
protected boolean canActivateProceedButton() {
return true;
}
@Override
protected void handleStatusChanged(IStatus status) {
super.handleStatusChanged(status);
scanButton.setEnabled(fieldsValid());
}
protected boolean fieldsValid() {
return isPlateValid();
}
private boolean isPlateValid() {
return BiobankPlugin.getDefault().isValidPlateBarcode(
plateToScanText.getText());
};
@Override
protected void buttonPressed(int buttonId) {
if (IDialogConstants.CANCEL_ID == buttonId)
super.buttonPressed(buttonId);
else if (IDialogConstants.PROCEED_ID == buttonId) {
try {
doProceed();
} catch (Exception e) {
BgcPlugin.openAsyncError(
"Error", e);
}
} else if (IDialogConstants.FINISH_ID == buttonId) {
setReturnCode(OK);
close();
} else if (IDialogConstants.NEXT_ID == buttonId) {
startNewPallet();
}
}
public String getPlateToScan() {
return plateToScan;
}
public void setPlateToScan(String plateToScan) {
this.plateToScan = plateToScan;
}
protected abstract void doProceed() throws Exception;
protected void createScanTubeAloneButton(Composite parent) {
scanTubeAloneSwitch = new Button(parent, SWT.NONE);
GridData gd = new GridData();
gd.verticalAlignment = SWT.TOP;
gd.horizontalSpan = 2;
gd.horizontalAlignment = SWT.RIGHT;
scanTubeAloneSwitch.setLayoutData(gd);
scanTubeAloneSwitch.setText("");
scanTubeAloneSwitch.setImage(BgcPlugin.getDefault().getImageRegistry()
.get(BgcPlugin.IMG_SCAN_EDIT));
scanTubeAloneSwitch.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
if (isScanHasBeenLaunched()) {
palletScanManagement.toggleScanTubeAloneMode();
if (palletScanManagement.isScanTubeAloneMode()) {
scanTubeAloneSwitch.setImage(BgcPlugin.getDefault()
.getImageRegistry()
.get(BgcPlugin.IMG_SCAN_CLOSE_EDIT));
} else {
scanTubeAloneSwitch.setImage(BgcPlugin.getDefault()
.getImageRegistry().get(BgcPlugin.IMG_SCAN_EDIT));
}
}
}
});
scanTubeAloneSwitch.setVisible(false);
}
protected boolean canScanTubeAlone(PalletCell cell) {
return cell == null || cell.getStatus() == UICellStatus.EMPTY
|| cell.getStatus() == UICellStatus.MISSING;
}
protected void postprocessScanTubeAlone(PalletCell cell) throws Exception {
Assert.isNotNull(SessionManager.getUser().getCurrentWorkingCenter());
CellProcessResult res = (CellProcessResult) SessionManager
.getAppService().doAction(
getCellProcessAction(SessionManager.getUser()
.getCurrentWorkingCenter().getId(),
cell.transformIntoServerCell(),
Locale.getDefault()));
cell.merge(SessionManager.getAppService(), res.getCell());
if (res.getProcessStatus() == CellInfoStatus.ERROR) {
Button okButton = getButton(IDialogConstants.PROCEED_ID);
okButton.setEnabled(false);
}
specificScanPosProcess(cell);
spw.redraw();
}
protected abstract Action<ProcessResult> getCellProcessAction(
Integer centerId, CellInfo cell, Locale locale);
protected abstract Action<ProcessResult> getPalletProcessAction(
Integer centerId, Map<RowColPos, CellInfo> cells, boolean isRescanMode,
Locale locale);
protected void resetScan() {
if (spw != null)
spw.setCells(null);
palletScanManagement.onReset();
}
}