package net.sf.jasperreports.engine.fill;

import java.awt.Color;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.jasperreports.engine.JRBoxContainer;
import net.sf.jasperreports.engine.JRCommonElement;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRElementGroup;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpressionCollector;
import net.sf.jasperreports.engine.JRFrame;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPen;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRVisitor;
import net.sf.jasperreports.engine.base.JRBaseElementGroup;
import net.sf.jasperreports.engine.util.JRBoxUtil;
import net.sf.jasperreports.engine.util.JRPenUtil;
import net.sf.jasperreports.engine.util.JRStyleResolver;

public class JRFillFrame extends JRFillElement implements JRFrame {
  protected final JRFrame parentFrame;
  
  private JRFillFrameElements frameContainer;
  
  private Map bottomTemplateFrames;
  
  private Map topTemplateFrames;
  
  private Map topBottomTemplateFrames;
  
  private boolean first;
  
  private boolean fillBottomBorder;
  
  private boolean filling;
  
  public JRFillFrame(JRBaseFiller filler, JRFrame frame, JRFillObjectFactory factory) {
    super(filler, (JRElement)frame, factory);
    this.parentFrame = frame;
    this.frameContainer = new JRFillFrameElements(factory);
    this.bottomTemplateFrames = new HashMap();
    this.topTemplateFrames = new HashMap();
    this.topBottomTemplateFrames = new HashMap();
    setShrinkable(true);
  }
  
  protected JRFillFrame(JRFillFrame frame, JRFillCloneFactory factory) {
    super(frame, factory);
    this.parentFrame = frame.parentFrame;
    this.frameContainer = new JRFillFrameElements(frame.frameContainer, factory);
    this.bottomTemplateFrames = frame.bottomTemplateFrames;
    this.topTemplateFrames = frame.topTemplateFrames;
    this.topBottomTemplateFrames = frame.topBottomTemplateFrames;
  }
  
  public byte getMode() {
    return JRStyleResolver.getMode((JRCommonElement)this, (byte)2);
  }
  
  public Color getDefaultLineColor() {
    return getForecolor();
  }
  
  protected void evaluate(byte evaluation) throws JRException {
    reset();
    evaluatePrintWhenExpression(evaluation);
    evaluateProperties(evaluation);
    if (isPrintWhenExpressionNull() || isPrintWhenTrue()) {
      this.frameContainer.evaluate(evaluation);
      boolean repeating = true;
      JRFillElement[] elements = (JRFillElement[])getElements();
      for (int i = 0; repeating && i < elements.length; i++)
        repeating &= elements[i].isValueRepeating(); 
      setValueRepeating(repeating);
    } 
    this.filling = false;
  }
  
  protected void rewind() throws JRException {
    this.frameContainer.rewind();
    this.filling = false;
  }
  
  protected boolean prepare(int availableStretchHeight, boolean isOverflow) throws JRException {
    super.prepare(availableStretchHeight, isOverflow);
    if (!isToPrint())
      return false; 
    this.first = (!isOverflow || !this.filling);
    int topPadding = this.first ? getLineBox().getTopPadding().intValue() : 0;
    int bottomPadding = getLineBox().getBottomPadding().intValue();
    if (availableStretchHeight < getRelativeY() - getY() - getBandBottomY() - topPadding) {
      setToPrint(false);
      return true;
    } 
    if (!this.filling && !isPrintRepeatedValues() && isValueRepeating() && (!isPrintInFirstWholeBand() || !getBand().isFirstWholeOnPageColumn()) && (getPrintWhenGroupChanges() == null || !getBand().isNewGroup(getPrintWhenGroupChanges())) && (!isOverflow || !isPrintWhenDetailOverflows())) {
      setToPrint(false);
      return false;
    } 
    if (!this.filling && isOverflow && isAlreadyPrinted())
      if (isPrintWhenDetailOverflows()) {
        rewind();
        setReprinted(true);
      } else {
        setToPrint(false);
        return false;
      }  
    int stretchHeight = availableStretchHeight - getRelativeY() + getY() + getBandBottomY();
    this.frameContainer.initFill();
    this.frameContainer.resetElements();
    int frameElemsAvailableHeight = stretchHeight + bottomPadding + getLineBox().getTopPadding().intValue() - topPadding;
    this.frameContainer.prepareElements(frameElemsAvailableHeight, true);
    boolean willOverflow = this.frameContainer.willOverflow();
    if (willOverflow) {
      this.fillBottomBorder = false;
      setStretchHeight(getHeight() + stretchHeight);
    } else {
      int neededStretch = this.frameContainer.getStretchHeight() - this.frameContainer.getFirstY() + topPadding + bottomPadding;
      if (neededStretch <= getHeight() + stretchHeight) {
        this.fillBottomBorder = true;
        setStretchHeight(neededStretch);
      } else {
        this.fillBottomBorder = false;
        setStretchHeight(getHeight() + stretchHeight);
      } 
    } 
    this.filling = willOverflow;
    return willOverflow;
  }
  
  protected void setStretchHeight(int stretchHeight) {
    super.setStretchHeight(stretchHeight);
    int topPadding = this.first ? getLineBox().getTopPadding().intValue() : 0;
    int bottomPadding = this.fillBottomBorder ? getLineBox().getBottomPadding().intValue() : 0;
    this.frameContainer.setStretchHeight(stretchHeight + this.frameContainer.getFirstY() - topPadding - bottomPadding);
  }
  
  protected void stretchHeightFinal() {
    this.frameContainer.stretchElements();
    this.frameContainer.moveBandBottomElements();
    this.frameContainer.removeBlankElements();
    int topPadding = this.first ? getLineBox().getTopPadding().intValue() : 0;
    int bottomPadding = this.fillBottomBorder ? getLineBox().getBottomPadding().intValue() : 0;
    super.setStretchHeight(this.frameContainer.getStretchHeight() - this.frameContainer.getFirstY() + topPadding + bottomPadding);
  }
  
  protected JRPrintElement fill() throws JRException {
    JRTemplatePrintFrame printFrame = new JRTemplatePrintFrame(getTemplate());
    printFrame.setX(getX());
    printFrame.setY(getRelativeY());
    printFrame.setWidth(getWidth());
    this.frameContainer.fillElements(printFrame);
    printFrame.setHeight(getStretchHeight());
    transferProperties(printFrame);
    return printFrame;
  }
  
  protected JRTemplateFrame getTemplate() {
    Map templatesMap;
    JRStyle style = getStyle();
    if (this.first) {
      if (this.fillBottomBorder) {
        templatesMap = this.templates;
      } else {
        templatesMap = this.bottomTemplateFrames;
      } 
    } else if (this.fillBottomBorder) {
      templatesMap = this.topTemplateFrames;
    } else {
      templatesMap = this.topBottomTemplateFrames;
    } 
    JRTemplateFrame boxTemplate = (JRTemplateFrame)templatesMap.get(style);
    if (boxTemplate == null) {
      boxTemplate = new JRTemplateFrame((this.band == null) ? null : this.band.getOrigin(), this.filler.getJasperPrint().getDefaultStyleProvider(), this);
      transferProperties(boxTemplate);
      if (this.first) {
        if (!this.fillBottomBorder) {
          boxTemplate.copyBox(getLineBox());
          JRBoxUtil.reset(boxTemplate.getLineBox(), false, false, false, true);
        } 
      } else if (this.fillBottomBorder) {
        boxTemplate.copyBox(getLineBox());
        JRBoxUtil.reset(boxTemplate.getLineBox(), false, false, true, false);
      } else {
        boxTemplate.copyBox(getLineBox());
        JRBoxUtil.reset(boxTemplate.getLineBox(), false, false, true, true);
      } 
      templatesMap.put(style, boxTemplate);
    } 
    return boxTemplate;
  }
  
  protected void resolveElement(JRPrintElement element, byte evaluation) {}
  
  public JRElement[] getElements() {
    return this.frameContainer.getElements();
  }
  
  public List getChildren() {
    return this.frameContainer.getChildren();
  }
  
  public void collectExpressions(JRExpressionCollector collector) {
    collector.collect(this);
  }
  
  public JRLineBox getLineBox() {
    return ((JRBoxContainer)this.parent).getLineBox();
  }
  
  public byte getBorder() {
    return JRPenUtil.getPenFromLinePen((JRPen)getLineBox().getPen());
  }
  
  public Byte getOwnBorder() {
    return JRPenUtil.getOwnPenFromLinePen((JRPen)getLineBox().getPen());
  }
  
  public void setBorder(byte border) {
    JRPenUtil.setLinePenFromPen(border, (JRPen)getLineBox().getPen());
  }
  
  public void setBorder(Byte border) {
    JRPenUtil.setLinePenFromPen(border, (JRPen)getLineBox().getPen());
  }
  
  public Color getBorderColor() {
    return getLineBox().getPen().getLineColor();
  }
  
  public Color getOwnBorderColor() {
    return getLineBox().getPen().getOwnLineColor();
  }
  
  public void setBorderColor(Color borderColor) {
    getLineBox().getPen().setLineColor(borderColor);
  }
  
  public int getPadding() {
    return getLineBox().getPadding().intValue();
  }
  
  public Integer getOwnPadding() {
    return getLineBox().getOwnPadding();
  }
  
  public void setPadding(int padding) {
    getLineBox().setPadding(padding);
  }
  
  public void setPadding(Integer padding) {
    getLineBox().setPadding(padding);
  }
  
  public byte getTopBorder() {
    return JRPenUtil.getPenFromLinePen((JRPen)getLineBox().getTopPen());
  }
  
  public Byte getOwnTopBorder() {
    return JRPenUtil.getOwnPenFromLinePen((JRPen)getLineBox().getTopPen());
  }
  
  public void setTopBorder(byte topBorder) {
    JRPenUtil.setLinePenFromPen(topBorder, (JRPen)getLineBox().getTopPen());
  }
  
  public void setTopBorder(Byte topBorder) {
    JRPenUtil.setLinePenFromPen(topBorder, (JRPen)getLineBox().getTopPen());
  }
  
  public Color getTopBorderColor() {
    return getLineBox().getTopPen().getLineColor();
  }
  
  public Color getOwnTopBorderColor() {
    return getLineBox().getTopPen().getOwnLineColor();
  }
  
  public void setTopBorderColor(Color topBorderColor) {
    getLineBox().getTopPen().setLineColor(topBorderColor);
  }
  
  public int getTopPadding() {
    return getLineBox().getTopPadding().intValue();
  }
  
  public Integer getOwnTopPadding() {
    return getLineBox().getOwnTopPadding();
  }
  
  public void setTopPadding(int topPadding) {
    getLineBox().setTopPadding(topPadding);
  }
  
  public void setTopPadding(Integer topPadding) {
    getLineBox().setTopPadding(topPadding);
  }
  
  public byte getLeftBorder() {
    return JRPenUtil.getPenFromLinePen((JRPen)getLineBox().getLeftPen());
  }
  
  public Byte getOwnLeftBorder() {
    return JRPenUtil.getOwnPenFromLinePen((JRPen)getLineBox().getLeftPen());
  }
  
  public void setLeftBorder(byte leftBorder) {
    JRPenUtil.setLinePenFromPen(leftBorder, (JRPen)getLineBox().getLeftPen());
  }
  
  public void setLeftBorder(Byte leftBorder) {
    JRPenUtil.setLinePenFromPen(leftBorder, (JRPen)getLineBox().getLeftPen());
  }
  
  public Color getLeftBorderColor() {
    return getLineBox().getLeftPen().getLineColor();
  }
  
  public Color getOwnLeftBorderColor() {
    return getLineBox().getLeftPen().getOwnLineColor();
  }
  
  public void setLeftBorderColor(Color leftBorderColor) {
    getLineBox().getLeftPen().setLineColor(leftBorderColor);
  }
  
  public int getLeftPadding() {
    return getLineBox().getLeftPadding().intValue();
  }
  
  public Integer getOwnLeftPadding() {
    return getLineBox().getOwnLeftPadding();
  }
  
  public void setLeftPadding(int leftPadding) {
    getLineBox().setLeftPadding(leftPadding);
  }
  
  public void setLeftPadding(Integer leftPadding) {
    getLineBox().setLeftPadding(leftPadding);
  }
  
  public byte getBottomBorder() {
    return JRPenUtil.getPenFromLinePen((JRPen)getLineBox().getBottomPen());
  }
  
  public Byte getOwnBottomBorder() {
    return JRPenUtil.getOwnPenFromLinePen((JRPen)getLineBox().getBottomPen());
  }
  
  public void setBottomBorder(byte bottomBorder) {
    JRPenUtil.setLinePenFromPen(bottomBorder, (JRPen)getLineBox().getBottomPen());
  }
  
  public void setBottomBorder(Byte bottomBorder) {
    JRPenUtil.setLinePenFromPen(bottomBorder, (JRPen)getLineBox().getBottomPen());
  }
  
  public Color getBottomBorderColor() {
    return getLineBox().getBottomPen().getLineColor();
  }
  
  public Color getOwnBottomBorderColor() {
    return getLineBox().getBottomPen().getOwnLineColor();
  }
  
  public void setBottomBorderColor(Color bottomBorderColor) {
    getLineBox().getBottomPen().setLineColor(bottomBorderColor);
  }
  
  public int getBottomPadding() {
    return getLineBox().getBottomPadding().intValue();
  }
  
  public Integer getOwnBottomPadding() {
    return getLineBox().getOwnBottomPadding();
  }
  
  public void setBottomPadding(int bottomPadding) {
    getLineBox().setBottomPadding(bottomPadding);
  }
  
  public void setBottomPadding(Integer bottomPadding) {
    getLineBox().setBottomPadding(bottomPadding);
  }
  
  public byte getRightBorder() {
    return JRPenUtil.getPenFromLinePen((JRPen)getLineBox().getRightPen());
  }
  
  public Byte getOwnRightBorder() {
    return JRPenUtil.getOwnPenFromLinePen((JRPen)getLineBox().getRightPen());
  }
  
  public void setRightBorder(byte rightBorder) {
    JRPenUtil.setLinePenFromPen(rightBorder, (JRPen)getLineBox().getRightPen());
  }
  
  public void setRightBorder(Byte rightBorder) {
    JRPenUtil.setLinePenFromPen(rightBorder, (JRPen)getLineBox().getRightPen());
  }
  
  public Color getRightBorderColor() {
    return getLineBox().getRightPen().getLineColor();
  }
  
  public Color getOwnRightBorderColor() {
    return getLineBox().getRightPen().getOwnLineColor();
  }
  
  public void setRightBorderColor(Color rightBorderColor) {
    getLineBox().getRightPen().setLineColor(rightBorderColor);
  }
  
  public int getRightPadding() {
    return getLineBox().getRightPadding().intValue();
  }
  
  public Integer getOwnRightPadding() {
    return getLineBox().getOwnRightPadding();
  }
  
  public void setRightPadding(int rightPadding) {
    getLineBox().setRightPadding(rightPadding);
  }
  
  public void setRightPadding(Integer rightPadding) {
    getLineBox().setRightPadding(rightPadding);
  }
  
  public void visit(JRVisitor visitor) {
    visitor.visitFrame(this);
  }
  
  public JRElement getElementByKey(String key) {
    return JRBaseElementGroup.getElementByKey(getElements(), key);
  }
  
  public JRFillCloneable createClone(JRFillCloneFactory factory) {
    return new JRFillFrame(this, factory);
  }
  
  protected class JRFillFrameElements extends JRFillElementContainer {
    private final JRFillFrame this$0;
    
    JRFillFrameElements(JRFillObjectFactory factory) {
      super(JRFillFrame.this.filler, (JRElementGroup)JRFillFrame.this.parentFrame, factory);
      initElements();
    }
    
    JRFillFrameElements(JRFillFrameElements frameElements, JRFillCloneFactory factory) {
      super(frameElements, factory);
      initElements();
    }
    
    protected int getContainerHeight() {
      return JRFillFrame.this.getHeight() - JRFillFrame.this.getLineBox().getTopPadding().intValue() - JRFillFrame.this.getLineBox().getBottomPadding().intValue();
    }
  }
}
