package net.sf.jasperreports.engine.fill;

import java.awt.Color;
import java.awt.font.TextAttribute;
import java.util.HashMap;
import java.util.Map;
import net.sf.jasperreports.engine.JRAlignment;
import net.sf.jasperreports.engine.JRBox;
import net.sf.jasperreports.engine.JRBoxContainer;
import net.sf.jasperreports.engine.JRCommonElement;
import net.sf.jasperreports.engine.JRCommonText;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRFont;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPen;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JRPropertiesHolder;
import net.sf.jasperreports.engine.JRReportFont;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRTextElement;
import net.sf.jasperreports.engine.util.JRFontUtil;
import net.sf.jasperreports.engine.util.JRPenUtil;
import net.sf.jasperreports.engine.util.JRProperties;
import net.sf.jasperreports.engine.util.JRSingletonCache;
import net.sf.jasperreports.engine.util.JRStringUtil;
import net.sf.jasperreports.engine.util.JRStyleResolver;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.engine.util.JRTextMeasurerUtil;
import net.sf.jasperreports.engine.util.LineBoxWrapper;
import net.sf.jasperreports.engine.util.MarkupProcessor;
import net.sf.jasperreports.engine.util.MarkupProcessorFactory;

public abstract class JRFillTextElement extends JRFillElement implements JRTextElement {
  private static final JRSingletonCache markupProcessorFactoryCache = new JRSingletonCache(MarkupProcessorFactory.class);
  
  private static final Map markupProcessors = new HashMap();
  
  private boolean isLeftToRight = true;
  
  private JRTextMeasurer textMeasurer = null;
  
  private float lineSpacingFactor = 0.0F;
  
  private float leadingOffset = 0.0F;
  
  private float textHeight = 0.0F;
  
  private int textStart = 0;
  
  private int textEnd = 0;
  
  private String textTruncateSuffix;
  
  private String rawText = null;
  
  private JRStyledText styledText = null;
  
  private Map styledTextAttributesMap = new HashMap();
  
  protected final JRReportFont reportFont;
  
  protected JRFillTextElement(JRBaseFiller filler, JRTextElement textElement, JRFillObjectFactory factory) {
    super(filler, (JRElement)textElement, factory);
    this.reportFont = factory.getReportFont(textElement.getReportFont());
  }
  
  protected JRFillTextElement(JRFillTextElement textElement, JRFillCloneFactory factory) {
    super(textElement, factory);
    this.reportFont = textElement.reportFont;
  }
  
  private void createTextMeasurer() {
    this.textMeasurer = JRTextMeasurerUtil.createTextMeasurer((JRCommonText)this);
  }
  
  protected void ensureTextMeasurer() {
    if (this.textMeasurer == null)
      createTextMeasurer(); 
  }
  
  public byte getMode() {
    return JRStyleResolver.getMode((JRCommonElement)this, (byte)2);
  }
  
  public byte getTextAlignment() {
    return JRStyleResolver.getHorizontalAlignment((JRAlignment)this);
  }
  
  public void setTextAlignment(byte horizontalAlignment) {}
  
  public byte getHorizontalAlignment() {
    return JRStyleResolver.getHorizontalAlignment((JRAlignment)this);
  }
  
  public Byte getOwnHorizontalAlignment() {
    return ((JRTextElement)this.parent).getOwnHorizontalAlignment();
  }
  
  public void setHorizontalAlignment(byte horizontalAlignment) {}
  
  public void setHorizontalAlignment(Byte horizontalAlignment) {}
  
  public byte getVerticalAlignment() {
    return JRStyleResolver.getVerticalAlignment((JRAlignment)this);
  }
  
  public Byte getOwnVerticalAlignment() {
    return ((JRTextElement)this.parent).getOwnVerticalAlignment();
  }
  
  public void setVerticalAlignment(byte verticalAlignment) {}
  
  public void setVerticalAlignment(Byte verticalAlignment) {}
  
  public byte getRotation() {
    return JRStyleResolver.getRotation((JRCommonText)this);
  }
  
  public Byte getOwnRotation() {
    return ((JRTextElement)this.parent).getOwnRotation();
  }
  
  public void setRotation(byte rotation) {}
  
  public void setRotation(Byte rotation) {}
  
  public byte getLineSpacing() {
    return JRStyleResolver.getLineSpacing((JRCommonText)this);
  }
  
  public Byte getOwnLineSpacing() {
    return ((JRTextElement)this.parent).getOwnLineSpacing();
  }
  
  public void setLineSpacing(byte lineSpacing) {}
  
  public void setLineSpacing(Byte lineSpacing) {}
  
  public boolean isStyledText() {
    return "styled".equals(getMarkup());
  }
  
  public Boolean isOwnStyledText() {
    String mkp = getOwnMarkup();
    return "styled".equals(mkp) ? Boolean.TRUE : ((mkp == null) ? null : Boolean.FALSE);
  }
  
  public void setStyledText(boolean isStyledText) {}
  
  public void setStyledText(Boolean isStyledText) {}
  
  public String getMarkup() {
    return JRStyleResolver.getMarkup((JRCommonText)this);
  }
  
  public String getOwnMarkup() {
    return ((JRTextElement)this.parent).getOwnMarkup();
  }
  
  public void setMarkup(String markup) {}
  
  public JRBox getBox() {
    return (JRBox)new LineBoxWrapper(getLineBox());
  }
  
  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 JRFont getFont() {
    return (JRFont)this;
  }
  
  protected Map getStyledTextAttributes() {
    JRStyle style = getStyle();
    Map styledTextAttributes = (Map)this.styledTextAttributesMap.get(style);
    if (styledTextAttributes == null) {
      styledTextAttributes = new HashMap();
      JRFontUtil.setAttributes(styledTextAttributes, (JRFont)this);
      styledTextAttributes.put(TextAttribute.FOREGROUND, getForecolor());
      if (getMode() == 1)
        styledTextAttributes.put(TextAttribute.BACKGROUND, getBackcolor()); 
      this.styledTextAttributesMap.put(style, styledTextAttributes);
    } 
    return styledTextAttributes;
  }
  
  protected float getLineSpacingFactor() {
    return this.lineSpacingFactor;
  }
  
  protected void setLineSpacingFactor(float lineSpacingFactor) {
    this.lineSpacingFactor = lineSpacingFactor;
  }
  
  protected float getLeadingOffset() {
    return this.leadingOffset;
  }
  
  protected void setLeadingOffset(float leadingOffset) {
    this.leadingOffset = leadingOffset;
  }
  
  protected byte getRunDirection() {
    return this.isLeftToRight ? 0 : 1;
  }
  
  protected float getTextHeight() {
    return this.textHeight;
  }
  
  protected void setTextHeight(float textHeight) {
    this.textHeight = textHeight;
  }
  
  protected int getTextStart() {
    return this.textStart;
  }
  
  protected void setTextStart(int textStart) {
    this.textStart = textStart;
  }
  
  protected int getTextEnd() {
    return this.textEnd;
  }
  
  protected void setTextEnd(int textEnd) {
    this.textEnd = textEnd;
  }
  
  protected String getRawText() {
    return this.rawText;
  }
  
  protected void setRawText(String rawText) {
    this.rawText = rawText;
    this.styledText = null;
  }
  
  protected void reset() {
    super.reset();
    this.isLeftToRight = true;
    this.lineSpacingFactor = 0.0F;
    this.leadingOffset = 0.0F;
    this.textHeight = 0.0F;
  }
  
  protected void rewind() {
    this.textStart = 0;
    this.textEnd = 0;
  }
  
  protected JRStyledText getStyledText() {
    if (this.styledText == null) {
      String text = getRawText();
      if (text != null)
        this.styledText = this.filler.getStyledTextParser().getStyledText(getStyledTextAttributes(), text, !"none".equals(getMarkup())); 
    } 
    return this.styledText;
  }
  
  public String getText() {
    JRStyledText tmpStyledText = getStyledText();
    if (tmpStyledText == null)
      return null; 
    return tmpStyledText.getText();
  }
  
  protected void chopTextElement(int availableStretchHeight) {
    ensureTextMeasurer();
    JRStyledText tmpStyledText = getStyledText();
    if (tmpStyledText == null)
      return; 
    if (getTextEnd() == tmpStyledText.getText().length())
      return; 
    JRMeasuredText measuredText = this.textMeasurer.measure(tmpStyledText, getTextEnd(), availableStretchHeight, canOverflow());
    this.isLeftToRight = measuredText.isLeftToRight();
    setTextHeight(measuredText.getTextHeight());
    if (getRotation() == 0) {
      setStretchHeight((int)getTextHeight() + getLineBox().getTopPadding().intValue() + getLineBox().getBottomPadding().intValue());
    } else {
      setStretchHeight(getHeight());
    } 
    setTextStart(getTextEnd());
    setTextEnd(measuredText.getTextOffset());
    setTextTruncateSuffix(measuredText.getTextSuffix());
    setLineSpacingFactor(measuredText.getLineSpacingFactor());
    setLeadingOffset(measuredText.getLeadingOffset());
  }
  
  public JRReportFont getReportFont() {
    return this.reportFont;
  }
  
  public void setReportFont(JRReportFont reportFont) {}
  
  public String getFontName() {
    return JRStyleResolver.getFontName((JRFont)this);
  }
  
  public String getOwnFontName() {
    return ((JRFont)this.parent).getOwnFontName();
  }
  
  public void setFontName(String fontName) {}
  
  public boolean isBold() {
    return JRStyleResolver.isBold((JRFont)this);
  }
  
  public Boolean isOwnBold() {
    return ((JRFont)this.parent).isOwnBold();
  }
  
  public void setBold(boolean isBold) {}
  
  public void setBold(Boolean isBold) {}
  
  public boolean isItalic() {
    return JRStyleResolver.isItalic((JRFont)this);
  }
  
  public Boolean isOwnItalic() {
    return ((JRFont)this.parent).isOwnItalic();
  }
  
  public void setItalic(boolean isItalic) {}
  
  public void setItalic(Boolean isItalic) {}
  
  public boolean isUnderline() {
    return JRStyleResolver.isUnderline((JRFont)this);
  }
  
  public Boolean isOwnUnderline() {
    return ((JRFont)this.parent).isOwnUnderline();
  }
  
  public void setUnderline(boolean isUnderline) {}
  
  public void setUnderline(Boolean isUnderline) {}
  
  public boolean isStrikeThrough() {
    return JRStyleResolver.isStrikeThrough((JRFont)this);
  }
  
  public Boolean isOwnStrikeThrough() {
    return ((JRFont)this.parent).isOwnStrikeThrough();
  }
  
  public void setStrikeThrough(boolean isStrikeThrough) {}
  
  public void setStrikeThrough(Boolean isStrikeThrough) {}
  
  public int getFontSize() {
    return JRStyleResolver.getFontSize((JRFont)this);
  }
  
  public Integer getOwnFontSize() {
    return ((JRFont)this.parent).getOwnFontSize();
  }
  
  public void setFontSize(int size) {}
  
  public void setFontSize(Integer size) {}
  
  public int getSize() {
    return getFontSize();
  }
  
  public Integer getOwnSize() {
    return getOwnFontSize();
  }
  
  public void setSize(int size) {}
  
  public void setSize(Integer size) {}
  
  public String getPdfFontName() {
    return JRStyleResolver.getPdfFontName((JRFont)this);
  }
  
  public String getOwnPdfFontName() {
    return ((JRFont)this.parent).getOwnPdfFontName();
  }
  
  public void setPdfFontName(String pdfFontName) {}
  
  public String getPdfEncoding() {
    return JRStyleResolver.getPdfEncoding((JRFont)this);
  }
  
  public String getOwnPdfEncoding() {
    return ((JRFont)this.parent).getOwnPdfEncoding();
  }
  
  public void setPdfEncoding(String pdfEncoding) {}
  
  public boolean isPdfEmbedded() {
    return JRStyleResolver.isPdfEmbedded((JRFont)this);
  }
  
  public Boolean isOwnPdfEmbedded() {
    return ((JRFont)this.parent).isOwnPdfEmbedded();
  }
  
  public void setPdfEmbedded(boolean isPdfEmbedded) {
    setPdfEmbedded(isPdfEmbedded ? Boolean.TRUE : Boolean.FALSE);
  }
  
  public void setPdfEmbedded(Boolean isPdfEmbedded) {}
  
  public Color getDefaultLineColor() {
    return getForecolor();
  }
  
  public void setHeight(int height) {
    super.setHeight(height);
    createTextMeasurer();
  }
  
  public void setWidth(int width) {
    super.setWidth(width);
    createTextMeasurer();
  }
  
  protected String processMarkupText(String text) {
    text = JRStringUtil.replaceCRwithLF(text);
    if (text != null) {
      String markup = getMarkup();
      if (!"none".equals(markup) && !"styled".equals(markup))
        text = getMarkupProcessor(markup).convert(text); 
    } 
    return text;
  }
  
  protected static MarkupProcessor getMarkupProcessor(String markup) {
    MarkupProcessor markupProcessor = (MarkupProcessor)markupProcessors.get(markup);
    if (markupProcessor == null) {
      String factoryClass = JRProperties.getProperty("net.sf.jasperreports.markup.processor.factory." + markup);
      if (factoryClass == null)
        throw new JRRuntimeException("No markup processor factory specifyed for '" + markup + "' markup."); 
      MarkupProcessorFactory factory = null;
      try {
        factory = (MarkupProcessorFactory)markupProcessorFactoryCache.getCachedInstance(factoryClass);
      } catch (JRException e) {
        throw new JRRuntimeException(e);
      } 
      markupProcessor = factory.createMarkupProcessor();
      markupProcessors.put(markup, markupProcessor);
    } 
    return markupProcessor;
  }
  
  protected void setPrintText(JRPrintText printText) {
    int startIndex = getTextStart();
    int endIndex = getTextEnd();
    JRStyledText fullStyledText = getStyledText();
    String fullText = fullStyledText.getText();
    boolean keepAllText = (!canOverflow() && JRProperties.getBooleanProperty((JRPropertiesHolder)this, "net.sf.jasperreports.print.keep.full.text", false));
    if (keepAllText) {
      if (startIndex != 0)
        throw new JRRuntimeException("Text start index != 0 on keep all text."); 
      if (!"none".equals(getMarkup())) {
        String styledText = this.filler.getStyledTextParser().write(fullStyledText);
        printText.setText(styledText);
      } else {
        printText.setText(fullText);
      } 
      if (endIndex < fullText.length())
        printText.setTextTruncateIndex(new Integer(endIndex)); 
    } else {
      String printedText;
      if (!"none".equals(getMarkup())) {
        printedText = this.filler.getStyledTextParser().write(fullStyledText, startIndex, endIndex);
      } else {
        printedText = fullText.substring(startIndex, endIndex);
      } 
      printText.setText(printedText);
    } 
    printText.setTextTruncateSuffix(getTextTruncateSuffix());
  }
  
  protected String getTextTruncateSuffix() {
    return this.textTruncateSuffix;
  }
  
  protected void setTextTruncateSuffix(String textTruncateSuffix) {
    this.textTruncateSuffix = textTruncateSuffix;
  }
  
  protected abstract boolean canOverflow();
}
