package net.sf.jasperreports.engine.base;

import java.io.Serializable;
import java.util.StringTokenizer;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRExpressionChunk;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.util.JRClassLoader;

public class JRBaseExpression implements JRExpression, Serializable {
  private static final long serialVersionUID = 10200L;
  
  protected String valueClassName = null;
  
  protected String valueClassRealName = null;
  
  protected int id = 0;
  
  protected transient Class valueClass = null;
  
  private JRExpressionChunk[] chunks = null;
  
  private static int lastId = 0;
  
  protected JRBaseExpression() {}
  
  protected JRBaseExpression(JRExpression expression, JRBaseObjectFactory factory, Integer expressionId) {
    factory.put(expression, this);
    this.valueClassName = expression.getValueClassName();
    if (expressionId == null) {
      this.id = expression.getId();
    } else {
      this.id = expressionId.intValue();
    } 
    JRExpressionChunk[] jrChunks = expression.getChunks();
    if (jrChunks != null && jrChunks.length > 0) {
      this.chunks = new JRExpressionChunk[jrChunks.length];
      for (int i = 0; i < this.chunks.length; i++)
        this.chunks[i] = factory.getExpressionChunk(jrChunks[i]); 
    } 
  }
  
  protected JRBaseExpression(JRExpression expression, JRBaseObjectFactory factory) {
    this(expression, factory, null);
  }
  
  private static synchronized int getNextId() {
    return lastId++;
  }
  
  public void regenerateId() {
    this.id = getNextId();
  }
  
  public Class getValueClass() {
    if (this.valueClass == null) {
      String className = getValueClassRealName();
      if (className != null)
        try {
          this.valueClass = JRClassLoader.loadClassForName(className);
        } catch (ClassNotFoundException e) {
          throw new JRRuntimeException(e);
        }  
    } 
    return this.valueClass;
  }
  
  public String getValueClassName() {
    return this.valueClassName;
  }
  
  private String getValueClassRealName() {
    if (this.valueClassRealName == null)
      this.valueClassRealName = JRClassLoader.getClassRealName(this.valueClassName); 
    return this.valueClassRealName;
  }
  
  public int getId() {
    return this.id;
  }
  
  public JRExpressionChunk[] getChunks() {
    return this.chunks;
  }
  
  public String getText() {
    String text = "";
    this.chunks = getChunks();
    if (this.chunks != null && this.chunks.length > 0) {
      StringBuffer sbuffer = new StringBuffer();
      for (int i = 0; i < this.chunks.length; i++) {
        String textChunk;
        String escapedText;
        switch (this.chunks[i].getType()) {
          case 2:
            sbuffer.append("$P{");
            sbuffer.append(this.chunks[i].getText());
            sbuffer.append("}");
            break;
          case 3:
            sbuffer.append("$F{");
            sbuffer.append(this.chunks[i].getText());
            sbuffer.append("}");
            break;
          case 4:
            sbuffer.append("$V{");
            sbuffer.append(this.chunks[i].getText());
            sbuffer.append("}");
            break;
          case 5:
            sbuffer.append("$R{");
            sbuffer.append(this.chunks[i].getText());
            sbuffer.append("}");
            break;
          default:
            textChunk = this.chunks[i].getText();
            escapedText = escapeTextChunk(textChunk);
            sbuffer.append(escapedText);
            break;
        } 
      } 
      text = sbuffer.toString();
    } 
    return text;
  }
  
  protected String escapeTextChunk(String text) {
    if (text == null || text.indexOf('$') < 0)
      return text; 
    StringBuffer sb = new StringBuffer(text.length() + 4);
    StringTokenizer tkzer = new StringTokenizer(text, "$", true);
    boolean wasDelim = false;
    while (tkzer.hasMoreElements()) {
      String token = tkzer.nextToken();
      if (wasDelim && (token.startsWith("P{") || token.startsWith("F{") || token.startsWith("V{") || token.startsWith("R{")) && token.indexOf('}') > 0)
        sb.append('$'); 
      sb.append(token);
      wasDelim = token.equals("$");
    } 
    return sb.toString();
  }
  
  public Object clone() {
    JRBaseExpression clone = null;
    try {
      clone = (JRBaseExpression)super.clone();
    } catch (CloneNotSupportedException e) {
      throw new JRRuntimeException(e);
    } 
    if (this.chunks != null) {
      clone.chunks = new JRExpressionChunk[this.chunks.length];
      for (int i = 0; i < this.chunks.length; i++)
        clone.chunks[i] = (JRExpressionChunk)this.chunks[i].clone(); 
    } 
    return clone;
  }
}
