package regexgolf2.controllers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import regexgolf2.model.ObjectChangedListener;
import regexgolf2.model.Requirement;
import regexgolf2.model.SolvableChallenge;
import regexgolf2.ui.subcomponents.requirementlisting.RequirementListUI;
import regexgolf2.ui.subcomponents.requirementlisting.requirementcell.RequirementItem;
/**
* The RequirementListingController is responsible for displaying
* Requirements from a Challenge.
* It uses a ListView to display the Requirements and reacts instantly
* to changes in the Challenge.
*/
public class RequirementListingController
{
private final RequirementListUI _ui;
private ObjectChangedListener _challengeListener;
private final boolean _expectedMatchResult;
private final ObjectProperty<SolvableChallenge> _challenge = new SimpleObjectProperty<>();
private final BooleanProperty _editableProperty = new SimpleBooleanProperty();
/**
* @param challengeProperty a Property that is used for binding if it is not null.
* @param expectedMatchResult the expectedMatchResult of the Requirements that should be displayed.
* @param editable If the Requirements should be editable or not.
*/
public RequirementListingController(ObjectProperty<SolvableChallenge> challengeProperty,
boolean expectedMatchResult) throws IOException
{
_expectedMatchResult = expectedMatchResult;
_ui = new RequirementListUI();
_ui.editableProperty().bind(_editableProperty);
initUITitle();
initButtonHandlers();
initChallengeListener(); //Listener for Changes INSIDE the Challenge
initChallengeChangedReaction(); //Change of the Challenge Property
if (challengeProperty != null)
_challenge.bind(challengeProperty);
}
private void initUITitle()
{
String title = _expectedMatchResult ? "Do Match" : "Don't Match";
_ui.getTitleLabel().setText(title);
}
private void initButtonHandlers()
{
_ui.getAddButton().setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent ae)
{
getChallenge().getChallenge().addRequirement(new Requirement(_expectedMatchResult));
//TODO preselect the newly created requirement and also set editMode = true on cell
}
});
_ui.getRemoveButton().setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent arg0)
{
Requirement r = _ui.getSelectedItem().getRequirement();
getChallenge().getChallenge().removeRequirement(r);
}
});
}
/**
* Initializes the ChallengeListener that refreshes the UI
* if the Challenge changed.
*/
private void initChallengeListener()
{
_challengeListener = new ObjectChangedListener()
{
@Override
public void objectChanged(EventObject event)
{
if (!(getChallenge() != null && getChallenge().equals(event.getSource())))
throw new IllegalStateException("Challenge: " + _challenge + "\n" +
"Event-Source: " + event.getSource());
refreshRequirementListUI();
}
};
}
private void initChallengeChangedReaction()
{
_challenge.addListener(new ChangeListener<SolvableChallenge>()
{
@Override
public void changed(
ObservableValue<? extends SolvableChallenge> observable,
SolvableChallenge oldValue, SolvableChallenge newValue)
{
if (oldValue != null)
oldValue.removeObjectChangedListener(_challengeListener);
if (newValue != null)
newValue.addObjectChangedListener(_challengeListener);
refreshRequirementListUI();
}
});
}
private void refreshRequirementListUI()
{
List<Requirement> newRequirementList;
if (getChallenge() != null)
newRequirementList = getChallenge().getRequirements(_expectedMatchResult);
else
newRequirementList = new ArrayList<>();
List<RequirementItem> toRemove = new ArrayList<>();
for (RequirementItem item : _ui.getItems())
{
if (!newRequirementList.contains(item.getRequirement()))
toRemove.add(item);
}
_ui.getItems().removeAll(toRemove);
for (Requirement r : newRequirementList)
{
boolean itemExits = false;
for (RequirementItem item : _ui.getItems())
{
if (item.getRequirement().equals(r))
{
itemExits = true;
break;
}
}
if (!itemExits)
addRequirementToUI(r);
}
refreshListViewItems();
}
/**
* Creates a new RequirementItem with the given Requirement and adds
* the Item to the UI-List. The created Item is also returned.
*/
private RequirementItem addRequirementToUI(Requirement r)
{
final RequirementItem item = new RequirementItem(r, getChallenge().isComplied(r));
item.wordProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue)
{
item.getRequirement().setWord(newValue);
}
});
_ui.addRequirementItem(item);
return item;
}
private void refreshListViewItems()
{
for (RequirementItem item : _ui.getItems())
{
item.setWord(item.getRequirement().getWord());
item.setComplied(getChallenge().isComplied(item.getRequirement()));
}
}
public SolvableChallenge getChallenge()
{
return _challenge.get();
}
public void setChallenge(SolvableChallenge challenge)
{
_challenge.set(challenge);
}
public ObjectProperty<SolvableChallenge> challengeProperty()
{
return _challenge;
}
public BooleanProperty editableProperty()
{
return _editableProperty;
}
public Node getUINode()
{
return _ui.getUINode();
}
}