package co.smartreceipts.android.workers.reports.pdf.renderer.grid;
import android.support.annotation.NonNull;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import co.smartreceipts.android.utils.log.Logger;
import co.smartreceipts.android.workers.reports.pdf.pdfbox.PdfBoxWriter;
import co.smartreceipts.android.workers.reports.pdf.renderer.Renderer;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.HeightConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.WidthConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.XPositionConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.YPositionConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.formatting.Padding;
public class GridRenderer extends Renderer {
protected final List<GridRowRenderer> rowRenderers = new ArrayList<>();
protected final List<GridRowRenderer> newLineRows = new ArrayList<>();
public GridRenderer(float width, float height) {
this(new WidthConstraint(width), new HeightConstraint(height));
}
public GridRenderer(@NonNull WidthConstraint widthConstraint, @NonNull HeightConstraint heightConstraint) {
this.getRenderingConstraints().addConstraint(widthConstraint);
this.getRenderingConstraints().addConstraint(heightConstraint);
}
public void addRow(@NonNull GridRowRenderer rowRenderer) {
rowRenderers.add(rowRenderer);
}
public void addRows(@NonNull List<GridRowRenderer> rows) {
rowRenderers.addAll(rows);
}
@NonNull
@Override
public Renderer copy() {
final List<GridRowRenderer> renderers = new ArrayList<>();
final List<GridRowRenderer> newLines = new ArrayList<>();
for (final GridRowRenderer renderer : this.rowRenderers) {
final GridRowRenderer rowCopy = (GridRowRenderer) renderer.copy();
renderers.add(rowCopy);
if (newLines.contains(renderer)) {
newLines.add(rowCopy);
}
}
final GridRenderer copy = new GridRenderer(width, this.height);
copy.rowRenderers.addAll(renderers);
copy.newLineRows.addAll(newLines);
copy.height = this.height;
copy.width = this.width;
copy.getRenderingConstraints().setConstraints(this.getRenderingConstraints());
copy.getRenderingFormatting().setFormatting(this.getRenderingFormatting());
return copy;
}
@Override
public void measure() throws IOException {
final float widthConstraint = Preconditions.checkNotNull(getRenderingConstraints().getConstraint(WidthConstraint.class));
final float heightConstraint = Preconditions.checkNotNull(getRenderingConstraints().getConstraint(HeightConstraint.class));
final Float padding = getRenderingFormatting().getFormatting(Padding.class);
if (padding != null) {
for (final GridRowRenderer rowRenderer : rowRenderers) {
rowRenderer.getRenderingFormatting().addFormatting(new Padding(padding));
}
}
// First - measure out "required" space for rows that have constraints or are set to WRAP
float heightOfRowsWithoutMatchParent = 0;
int matchParentRowCount = 0;
for (final GridRowRenderer rowRenderer : rowRenderers) {
Preconditions.checkArgument(rowRenderer.getWidth() == MATCH_PARENT, "All grid rows must currently match the parent size");
rowRenderer.getRenderingConstraints().addConstraint(new WidthConstraint(widthConstraint));
if (rowRenderer.getHeight() != MATCH_PARENT) {
// If we're using a constraint or WRAP_CONTENT
rowRenderer.measure();
heightOfRowsWithoutMatchParent += rowRenderer.getHeight();
} else {
matchParentRowCount++;
}
}
// Next - split the remaining space evenly between the remaining rows
final float matchParentSpacePerItem = (heightConstraint - heightOfRowsWithoutMatchParent) / matchParentRowCount;
for (final GridRowRenderer rowRenderer : rowRenderers) {
if (rowRenderer.getHeight() == MATCH_PARENT) {
rowRenderer.getRenderingConstraints().addConstraint(new HeightConstraint(matchParentSpacePerItem));
rowRenderer.measure();
}
}
// Finally - apply the x/y coords based on the heights
final float x = Preconditions.checkNotNull(getRenderingConstraints().getConstraint(XPositionConstraint.class, 0f));
float y = Preconditions.checkNotNull(getRenderingConstraints().getConstraint(YPositionConstraint.class, 0f));
List<GridRowRenderer> gridRowRendererLinkedList = new LinkedList<>(rowRenderers);
for (ListIterator<GridRowRenderer> listIterator = gridRowRendererLinkedList.listIterator(); listIterator.hasNext(); ) {
final GridRowRenderer rowRenderer = listIterator.next();
if (y + rowRenderer.getHeight() >= heightConstraint && listIterator.hasNext()) {
Logger.info(this, "Identified a page breaking table. Marking it as such");
y = 0;
if (rowRenderer.getAssociatedHeader() != null) {
final GridRowRenderer newPageHeader = (GridRowRenderer) rowRenderer.getAssociatedHeader().copy();
newPageHeader.getRenderingConstraints().addConstraint(new XPositionConstraint(x));
newPageHeader.getRenderingConstraints().addConstraint(new YPositionConstraint(y));
newPageHeader.measure();
rowRenderers.add(rowRenderers.indexOf(rowRenderer), newPageHeader);
y += rowRenderer.getHeight();
newLineRows.add(newPageHeader);
} else {
newLineRows.add(rowRenderer);
}
}
rowRenderer.getRenderingConstraints().addConstraint(new XPositionConstraint(x));
rowRenderer.getRenderingConstraints().addConstraint(new YPositionConstraint(y));
y += rowRenderer.getHeight();
// Run a final measure pass based on these new coordinates
rowRenderer.measure();
}
}
@Override
public void render(@NonNull PdfBoxWriter writer) throws IOException {
for (final GridRowRenderer rowRenderer : rowRenderers) {
if (newLineRows.contains(rowRenderer)) {
writer.newPage();
}
rowRenderer.render(writer);
}
}
}