package com.baselet.element;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import com.baselet.control.SharedUtils;
import com.baselet.control.basics.geom.Rectangle;
import com.baselet.control.constants.SharedConstants;
import com.baselet.element.sticking.PointChange;
import com.baselet.element.sticking.Stickable;
public class UndoInformation {
private final Rectangle diffRect;
private final Map<Stickable, List<PointChange>> stickableMoves;
private final String oldAdditionalAttributes;
private final String newAdditionalAttributes;
private UndoInformation(Rectangle diffRect, Map<Stickable, List<PointChange>> stickableMoves, String oldAdditionalAttributes, String newAdditionalAttributes) {
this.diffRect = diffRect;
this.stickableMoves = stickableMoves;
this.oldAdditionalAttributes = oldAdditionalAttributes;
this.newAdditionalAttributes = newAdditionalAttributes;
}
public UndoInformation(Rectangle newRect, Rectangle oldRect, Map<Stickable, List<PointChange>> stickableMoves, int gridSize, String oldAdditionalAttributes, String newAdditionalAttributes) {
this(toMinZoom(newRect.subtract(oldRect), gridSize), stickableMoves, oldAdditionalAttributes, newAdditionalAttributes);
}
private static Map<Stickable, List<PointChange>> invertStickableMoves(Map<Stickable, List<PointChange>> stickableMoves) {
Map<Stickable, List<PointChange>> invertedMap = new HashMap<Stickable, List<PointChange>>();
for (Entry<Stickable, List<PointChange>> entry : stickableMoves.entrySet()) {
List<PointChange> invList = new ArrayList<PointChange>();
for (PointChange p : entry.getValue()) {
invList.add(new PointChange(p.getIndex(), -p.getDiffX(), -p.getDiffY()));
}
invertedMap.put(entry.getKey(), invList);
}
return invertedMap;
}
public Rectangle getDiffRectangle(int gridSize, boolean undo) {
Rectangle returnRect = undo ? diffRect.copyInverted() : diffRect;
return toCurrentZoom(returnRect, gridSize);
}
public Map<Stickable, List<PointChange>> getStickableMoves(boolean undo) {
return undo ? invertStickableMoves(stickableMoves) : stickableMoves;
}
public String getAdditionalAttributes(boolean undo) {
if (undo) {
return oldAdditionalAttributes;
}
else {
return newAdditionalAttributes;
}
}
private static Rectangle toMinZoom(Rectangle rectangle, int gridSize) {
int xBefore = toMinZoom(rectangle.getX(), gridSize);
int yBefore = toMinZoom(rectangle.getY(), gridSize);
int wBefore = toMinZoom(rectangle.getWidth(), gridSize);
int hBefore = toMinZoom(rectangle.getHeight(), gridSize);
return new Rectangle(xBefore, yBefore, wBefore, hBefore);
}
private static int toMinZoom(int val, int gridSize) {
return val / gridSize;
}
private static Rectangle toCurrentZoom(Rectangle rectangle, int gridSize) {
int xBefore = toCurrentZoom(rectangle.getX(), gridSize);
int yBefore = toCurrentZoom(rectangle.getY(), gridSize);
int wBefore = toCurrentZoom(rectangle.getWidth(), gridSize);
int hBefore = toCurrentZoom(rectangle.getHeight(), gridSize);
return new Rectangle(xBefore, yBefore, wBefore, hBefore);
}
private static int toCurrentZoom(int val, int gridSize) {
return val * gridSize;
}
public UndoInformation merge(UndoInformation other) {
Rectangle mergedUndoDiffRect = diffRect.add(other.diffRect);
Map<Stickable, List<PointChange>> mergedMap = new HashMap<Stickable, List<PointChange>>();
mergeStickableMoves(mergedMap, stickableMoves);
mergeStickableMoves(mergedMap, other.stickableMoves);
return new UndoInformation(mergedUndoDiffRect, mergedMap, other.oldAdditionalAttributes, newAdditionalAttributes);
}
private void mergeStickableMoves(Map<Stickable, List<PointChange>> targetMap, Map<Stickable, List<PointChange>> sourceMap) {
for (Entry<Stickable, List<PointChange>> sourceEntry : sourceMap.entrySet()) {
Stickable sourceStickable = sourceEntry.getKey();
List<PointChange> sourceChangeList = sourceEntry.getValue();
List<PointChange> targetPointChanges = targetMap.get(sourceStickable);
if (targetPointChanges == null) { // stickable was not moved before in targetMap
targetMap.put(sourceStickable, sourceChangeList);
}
else { // stickable was already moved and these moves must be merged
mergeSourceToTarget(sourceChangeList, targetPointChanges);
}
}
}
private void mergeSourceToTarget(List<PointChange> sourceChangeList, List<PointChange> targetPointChanges) {
for (PointChange sourceChange : sourceChangeList) {
mergePoint(targetPointChanges, sourceChange);
}
}
private void mergePoint(List<PointChange> targetPointChanges, PointChange sourceChange) {
for (ListIterator<PointChange> iter = targetPointChanges.listIterator(); iter.hasNext();) {
PointChange targetChange = iter.next();
if (sourceChange.getIndex().equals(targetChange.getIndex())) {
iter.set(new PointChange(targetChange.getIndex(), sourceChange.getDiffX() + targetChange.getDiffX(), sourceChange.getDiffY() + targetChange.getDiffY()));
return; // index already in targetList and successfully updated
}
}
targetPointChanges.add(sourceChange); // index not in targetList, therefore added here
}
public String toString(boolean undo) {
return "UndoInformation [diffRect=" + getDiffRectangle(SharedConstants.DEFAULT_GRID_SIZE, undo) + ", stickableMoves=" + SharedUtils.mapToString(getStickableMoves(undo)) + ", additionalAttributes=" + getAdditionalAttributes(undo) + "]";
}
}