package jfxtras.scene.control.agenda.icalendar.editors.deleters;
import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javafx.util.Callback;
import javafx.util.Pair;
import jfxtras.icalendarfx.VCalendar;
import jfxtras.icalendarfx.components.VDisplayable;
import jfxtras.icalendarfx.components.VEvent;
import jfxtras.icalendarfx.components.VJournal;
import jfxtras.icalendarfx.components.VTodo;
import jfxtras.icalendarfx.parameters.Range.RangeType;
import jfxtras.icalendarfx.properties.component.descriptive.Status.StatusType;
import jfxtras.icalendarfx.properties.component.recurrence.RecurrenceRule;
import jfxtras.icalendarfx.properties.component.relationship.RecurrenceId;
import jfxtras.scene.control.agenda.icalendar.editors.ChangeDialogOption;
/**
* <p>Handles deleting recurrences of a {@link VDisplayable}
* (e.g. {@link VEvent}, {@link VTodo}, {@link VJournal})</p>
*
* @author David Bal
*
* @param <T> concrete implementation of this class
* @param <U> concrete {@link VDisplayable} class
*/
public abstract class DeleterDisplayable<T, U extends VDisplayable<?>> implements Deleter
{
public DeleterDisplayable(U vComponent)
{
this.vComponent = vComponent;
}
/*
* VCOMPONENT EDITED
*/
/** Gets the value of the {@link VDisplayable} to be deleted. Note: don't pass original or
* the changes will be instantaneous and cancel is not possible. */
public U getVComponentCopy() { return vComponent; }
private U vComponent;
/** Sets the value of the edited {@link VDisplayable} Note: don't pass original or
* the changes will be instantaneous and cancel is not possible. */
public void setVComponentCopy(U vComponentEdited) { this.vComponent = vComponentEdited; }
/**
* Sets the value of the edited {@link VDisplayable}. Note: don't pass original or
* the changes will be instantaneous and cancel is not possible.
*
* @return - this class for chaining
* @see VCalendar
*/
public T withVComponentCopy(U vComponentEdited) { setVComponentCopy(vComponentEdited); return (T) this; }
/*
* START ORIGINAL RECURRENCE
*/
/** Gets the value of the original recurrence date or date/time */
public Temporal getStartOriginalRecurrence() { return startOriginalRecurrence; }
private Temporal startOriginalRecurrence;
/** Sets the value of the original recurrence date or date/time */
public void setStartOriginalRecurrence(Temporal startOriginalRecurrence) { this.startOriginalRecurrence = startOriginalRecurrence; }
/**
* Sets the value of the original recurrence date or date/time and returns this class for chaining
*
* @return - this class for chaining
*/
public T withStartOriginalRecurrence(Temporal startOriginalRecurrence) { setStartOriginalRecurrence(startOriginalRecurrence); return (T) this; }
/*
* CHANGE DIALOG CALLBACK
*/
/** Gets the value of the dialog callback to prompt the user to select delete option */
public Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> getDialogCallback() { return dialogCallback; }
private Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> dialogCallback;
/** Sets the value of the dialog callback to prompt the user to select delete option */
public void setDialogCallback(Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> dialogCallback) { this.dialogCallback = dialogCallback; }
/**
* Sets the value of the dialog callback to prompt the user to select delete option and returns this class for chaining
*
* @return - this class for chaining
*/
public T withDialogCallback(Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> dialogCallback)
{
setDialogCallback(dialogCallback);
return (T) this;
}
/** Tests the object state is valid and revision can proceed. Returns true if valid, false otherwise */
private boolean isValid()
{
// if (getStartOriginalRecurrence() == null)
// {
// System.out.println("startOriginalRecurrence must not be null");
// return false;
// }
if (getDialogCallback() == null)
{
System.out.println("dialogCallback must not be null");
return false;
}
return true;
}
@Override
public List<VCalendar> delete()
{
if (! isValid())
{
throw new RuntimeException("Invalid parameters for component revision:");
}
// Copy VComponent to ensure original is unchanged
U vComponent = getVComponentCopy();
List<VCalendar> itipMessages = new ArrayList<>();
boolean hasRRule = vComponent.getRecurrenceRule() != null;
if (hasRRule)
{
Map<ChangeDialogOption, Pair<Temporal,Temporal>> choices = ChangeDialogOption.makeDialogChoices(
vComponent,
startOriginalRecurrence);
ChangeDialogOption changeResponse = dialogCallback.call(choices);
switch (changeResponse)
{
case ALL:
{
VCalendar cancelMessage = setUpCancelMessage(vComponent);
itipMessages.add(cancelMessage);
// NOTE: Orphaned children should be automatically removed when message is processed
// If not, then must be explicitly removed here
break;
}
case CANCEL:
break;
// return vComponent;
case ONE:
{
/* Note: A request method could be used to add an EXDATE instead of cancel.
* RFC 5546 indicates cancel should be used, but some clients such as Google
* calendar doesn't support the cancel, but it does support the request.
*/
VCalendar cancelMessage = setUpCancelMessage(vComponent);
// VCalendar cancelMessage = Deleter.defaultCancelVCalendar();
// vComponent.setStatus(StatusType.CANCELLED);
// vComponent.eraseDateTimeProperties();
// cancelMessage.addVComponent(vComponent);
vComponent.setRecurrenceId(getStartOriginalRecurrence());
vComponent.setRecurrenceRule((RecurrenceRule) null);
itipMessages.add(cancelMessage);
// REVISE APPROACH - accepted by Google
// // Add recurrence to EXDATE of parent
// Temporal recurrence = getStartOriginalRecurrence();
// if (vComponent.getExceptionDates() == null)
// {
// vComponent.withExceptionDates(recurrence);
// } else
// {
// vComponent.getExceptionDates().get(0).getValue().add(recurrence);
// }
// vComponent.incrementSequence();
//
// VCalendar requestMessage = Reviser.defaultRequestVCalendar();
// requestMessage.addVComponent(vComponent);
// itipMessages.add(requestMessage);
break;
}
case THIS_AND_FUTURE:
VCalendar cancelMessage = setUpCancelMessage(vComponent);
// VCalendar cancelMessage = Deleter.defaultCancelVCalendar();
// vComponent.setStatus(StatusType.CANCELLED);
// vComponent.eraseDateTimeProperties();
// cancelMessage.addVComponent(vComponent);
vComponent.setRecurrenceId(new RecurrenceId(getStartOriginalRecurrence()).withRange(RangeType.THIS_AND_FUTURE));
itipMessages.add(cancelMessage);
// REVISE APPROACH - accepted by Google
// // Add recurrence to EXDATE of parent
// Temporal recurrence = getStartOriginalRecurrence();
// if (vComponent.getExceptionDates() == null)
// {
// vComponent.withExceptionDates(recurrence);
// } else
// {
// vComponent.getExceptionDates().get(0).getValue().add(recurrence);
// }
// vComponent.incrementSequence();
//
// VCalendar requestMessage = Reviser.defaultRequestVCalendar();
// requestMessage.addVComponent(vComponent);
// itipMessages.add(requestMessage);
// // add UNTIL
// Temporal previous = vComponent.previousStreamValue(getStartOriginalRecurrence());
// final Temporal until;
// if (previous.isSupported(ChronoUnit.NANOS))
// {
// until = DateTimeUtilities.DateTimeType.DATE_WITH_UTC_TIME.from(previous);
// } else
// {
// until = LocalDate.from(previous);
// }
// vComponent.getRecurrenceRule().getValue().setUntil(until);
// List<VDisplayable<?>> recurrenceChildren = vComponent.recurrenceChildren()
// .stream()
// .filter(v -> DateTimeUtilities.isAfter(v.getRecurrenceId().getValue(), getStartOriginalRecurrence()))
// .collect(Collectors.toList());
break;
default:
throw new RuntimeException("Unsupprted response:" + changeResponse);
}
} else
{ // delete individual component
VCalendar cancelMessage = setUpCancelMessage(vComponent);
// VCalendar cancelMessage = Deleter.defaultCancelVCalendar();
// vComponent.setStatus(StatusType.CANCELLED);
// vComponent.eraseDateTimeProperties();
// cancelMessage.addVComponent(vComponent);
itipMessages.add(cancelMessage);
// NOTE: EXDATE should be added to parent when the iTIP message is processed.
// // does recurrence instance exist, then add EXDATE to parent
// if (vComponent.getRecurrenceId() != null)
// { // add EXDATE to recurrence parent
// // Copy parent component
// U parentCopy = null;
// try
// {
// parentCopy = (U) getVComponent().getClass().newInstance();
// VDisplayable<?> parent = getVComponent().recurrenceParent();
// parentCopy.copyFrom(parent);
// } catch (InstantiationException | IllegalAccessException e)
// {
// e.printStackTrace();
// }
//
// // Add recurrence to EXDATE of parent
// Temporal recurrence = vComponent.getRecurrenceId().getValue();
// if (parentCopy.getExceptionDates() == null)
// {
// parentCopy.withExceptionDates(recurrence);
// } else
// {
// parentCopy.getExceptionDates().get(0).getValue().add(recurrence);
// }
// parentCopy.incrementSequence();
// VCalendar requestMessage = Reviser.defaultRequestVCalendar();
// requestMessage.addVComponent(parentCopy);
// itipMessages.add(requestMessage);
// }
}
// if (! vComponent.isValid())
// {
// throw new RuntimeException("Invalid component:" + System.lineSeparator() +
// vComponent.errors().stream().collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator() +
// vComponent.toContent());
// }
// getVComponents().add(vComponent);
return itipMessages;
// return vComponent;
}
private VCalendar setUpCancelMessage(U vComponent)
{
VCalendar cancelMessage = Deleter.emptyCanceliTIPMessage();
vComponent.setStatus(StatusType.CANCELLED);
vComponent.setRecurrenceRule((RecurrenceRule) null);
vComponent.eraseDateTimeProperties();
cancelMessage.addChild(vComponent);
return cancelMessage;
}
}