package com.baselet.element.sequence_aio.facet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import com.baselet.element.sequence_aio.facet.LifelineSpanningTickSpanningOccurrence.ContainerPadding;
import com.baselet.element.sequence_aio.facet.SequenceDiagram.DoubleConverter;
public class HorizontalDrawingInfoImpl implements HorizontalDrawingInfo {
private final double diagramStart;
private final double diagramWidth;
private final LifelineHorizontalDrawingInfo[] horizontalDrawingInfos;
private final Map<Container, Double> containerLeftPadding;
private final Map<Container, Double> containerRightPadding;
/**
* Calculates the actual lifeline and diagram width.
* @param diagramStart left most point were the diagram can draw
* @param diagramMinWidth
* @param widthConverter used to adjust the calculated width (should only increase the width!)
* @param lifelineWidth width of a single lifeline without any paddings
* @param lifelineXPadding padding between two lifelines or a lifeline and the diagram border
* @param lifelineCount
* @param lastTick
* @param paddings
*/
public HorizontalDrawingInfoImpl(double diagramStart, double diagramMinWidth, DoubleConverter widthConverter,
double lifelineWidth, double lifelineXPadding, int lifelineCount, int lastTick, Collection<ContainerPadding> paddings) {
containerLeftPadding = new HashMap<Container, Double>((int) (paddings.size() / 0.7));
containerRightPadding = new HashMap<Container, Double>((int) (paddings.size() / 0.7));
horizontalDrawingInfos = new LifelineHorizontalDrawingInfo[lifelineCount];
this.diagramStart = diagramStart;
// calculate the padding information and the maximum padding
double maxPadding = 0;
double[][] leftPaddings = new double[lifelineCount][];
double[][] rightPaddings = new double[lifelineCount][];
for (int i = 0; i < lifelineCount; i++) {
PaddingInfo paddingInfo = getPaddings(i, true, lastTick, paddings);
leftPaddings[i] = paddingInfo.paddings;
maxPadding = Math.max(maxPadding, paddingInfo.maxPadding);
paddingInfo = getPaddings(i, false, lastTick, paddings);
rightPaddings[i] = paddingInfo.paddings;
maxPadding = Math.max(maxPadding, paddingInfo.maxPadding);
}
lifelineWidth += maxPadding * 2; // add the padding because it is not included in the lifeline width
double diagramWidth = lifelineWidth * lifelineCount + lifelineXPadding * (lifelineCount + 1);
if (diagramWidth < diagramMinWidth) {
diagramWidth = diagramMinWidth;
if (lifelineCount > 0) {
lifelineWidth = (diagramWidth - lifelineXPadding * (lifelineCount + 1)) / lifelineCount;
}
}
// adjust the width with the width converter
diagramWidth = widthConverter.convert(diagramWidth);
double lifelineHeadLeftStart = (diagramWidth
- (lifelineWidth * lifelineCount + lifelineXPadding * (lifelineCount - 1))
) / 2.0;
this.diagramWidth = diagramWidth;
for (int i = 0; i < horizontalDrawingInfos.length; i++) {
horizontalDrawingInfos[i] = new LifelineHorizontalDrawingInfoImpl(
leftPaddings[i],
rightPaddings[i],
lifelineHeadLeftStart + (lifelineWidth + lifelineXPadding) * i,
lifelineHeadLeftStart + (lifelineWidth + lifelineXPadding) * i + lifelineWidth);
}
}
private PaddingInfo getPaddings(int lifelineId, boolean left, int lastTick, Collection<ContainerPadding> allPaddings) {
double[] lifelinePaddings = new double[lastTick + 2];
// define 1 queue for starting of padding intervals, and the other for the ending of the intervals
Queue<ContainerPadding> paddingQueueStart = new PriorityQueue<ContainerPadding>(5,
ContainerPadding.getContainerStartTickLifelineAscComparator());
LinkedList<ContainerPadding> paddingListEnd = new LinkedList<ContainerPadding>();
for (ContainerPadding cp : allPaddings) {
if (left && cp.getContainer().getFirstLifeline().getIndex() == lifelineId && cp.getLeftPadding() > 0) {
paddingQueueStart.add(cp);
}
else if (!left && cp.getContainer().getLastLifeline().getIndex() == lifelineId && cp.getRightPadding() > 0) {
paddingQueueStart.add(cp);
}
}
double maxPadding = 0;
for (int tick = 0; tick < lifelinePaddings.length; tick++) {
// insert paddings that start at the current tick at the right place (endTick asc) and remove them.
while (paddingQueueStart.peek() != null && paddingQueueStart.peek().getContainer().getStartTick() == tick) {
paddingListEnd.addLast(paddingQueueStart.poll());
}
Iterator<ContainerPadding> endIter = paddingListEnd.iterator();
while (endIter.hasNext()) {
ContainerPadding paddingInterval = endIter.next();
if (left) {
if (!containerLeftPadding.containsKey(paddingInterval.getContainer())) {
containerLeftPadding.put(paddingInterval.getContainer(), lifelinePaddings[tick]);
}
lifelinePaddings[tick] += paddingInterval.getLeftPadding();
}
else {
if (!containerRightPadding.containsKey(paddingInterval.getContainer())) {
containerRightPadding.put(paddingInterval.getContainer(), lifelinePaddings[tick]);
}
lifelinePaddings[tick] += paddingInterval.getRightPadding();
}
if (paddingInterval.getContainer().getEndTick() == tick) {
endIter.remove();
}
}
maxPadding = Math.max(maxPadding, lifelinePaddings[tick]);
}
return new PaddingInfo(lifelinePaddings, maxPadding);
}
@Override
public LifelineHorizontalDrawingInfo getHDrawingInfo(Lifeline lifeline) {
return horizontalDrawingInfos[lifeline.getIndex()];
}
@Override
public double getSymmetricWidth(Lifeline ll1, Lifeline ll2, int tick) {
if (ll1.getIndex() <= ll2.getIndex()) {
return getHDrawingInfo(ll2).getSymmetricHorizontalEnd(tick)
- getHDrawingInfo(ll1).getSymmetricHorizontalStart(tick);
}
else {
return getHDrawingInfo(ll1).getSymmetricHorizontalEnd(tick)
- getHDrawingInfo(ll2).getSymmetricHorizontalStart(tick);
}
}
@Override
public double getHorizontalStart(Container container) {
return getHDrawingInfo(container.getFirstLifeline()).getHorizontalStart() + containerLeftPadding.get(container);
}
@Override
public double getHorizontalEnd(Container container) {
return getHDrawingInfo(container.getLastLifeline()).getHorizontalEnd() - containerRightPadding.get(container);
}
@Override
public double getWidth(Container container) {
return getHorizontalEnd(container) - getHorizontalStart(container);
}
@Override
public double getDiagramHorizontalStart() {
return diagramStart;
}
@Override
public double getDiagramHorizontalEnd() {
return diagramStart + diagramWidth;
}
@Override
public double getDiagramWidth() {
return diagramWidth;
}
private static class PaddingInfo {
private final double[] paddings;
private final double maxPadding;
public PaddingInfo(double[] paddings, double maxPadding) {
super();
this.paddings = paddings;
this.maxPadding = maxPadding;
}
}
}