package net.sf.jasperreports.engine.util;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import net.sf.jasperreports.engine.JRQueryChunk;
import net.sf.jasperreports.engine.JRRuntimeException;

public class JRQueryParser {
  private static final JRQueryParser singleton = new JRQueryParser();
  
  public static JRQueryParser instance() {
    return singleton;
  }
  
  public void parse(String text, JRQueryChunkHandler chunkHandler) {
    if (text != null) {
      StringBuffer textChunk = new StringBuffer();
      StringTokenizer tkzer = new StringTokenizer(text, "$", true);
      boolean wasDelim = false;
      while (tkzer.hasMoreTokens()) {
        String token = tkzer.nextToken();
        if (token.equals("$")) {
          if (wasDelim)
            textChunk.append("$"); 
          wasDelim = true;
          continue;
        } 
        if (token.startsWith("P{") && wasDelim) {
          int end = token.indexOf('}');
          if (end > 0) {
            if (textChunk.length() > 0)
              chunkHandler.handleTextChunk(textChunk.toString()); 
            String parameterChunk = token.substring(2, end);
            chunkHandler.handleParameterChunk(parameterChunk);
            textChunk = new StringBuffer(token.substring(end + 1));
          } else {
            if (wasDelim)
              textChunk.append("$"); 
            textChunk.append(token);
          } 
        } else if (token.startsWith("P!{") && wasDelim) {
          int end = token.indexOf('}');
          if (end > 0) {
            if (textChunk.length() > 0)
              chunkHandler.handleTextChunk(textChunk.toString()); 
            String parameterClauseChunk = token.substring(3, end);
            chunkHandler.handleParameterClauseChunk(parameterClauseChunk);
            textChunk = new StringBuffer(token.substring(end + 1));
          } else {
            if (wasDelim)
              textChunk.append("$"); 
            textChunk.append(token);
          } 
        } else if (token.startsWith("X{") && wasDelim) {
          int end = token.indexOf('}');
          if (end > 0) {
            if (textChunk.length() > 0)
              chunkHandler.handleTextChunk(textChunk.toString()); 
            String clauseChunk = token.substring(2, end);
            String[] tokens = parseClause(clauseChunk);
            chunkHandler.handleClauseChunk(tokens);
            textChunk = new StringBuffer(token.substring(end + 1));
          } else {
            if (wasDelim)
              textChunk.append("$"); 
            textChunk.append(token);
          } 
        } else {
          if (wasDelim)
            textChunk.append("$"); 
          textChunk.append(token);
        } 
        wasDelim = false;
      } 
      if (wasDelim)
        textChunk.append("$"); 
      if (textChunk.length() > 0)
        chunkHandler.handleTextChunk(textChunk.toString()); 
    } 
  }
  
  protected String[] parseClause(String clauseChunk) {
    List tokens = new ArrayList();
    boolean wasClauseToken = false;
    String separator = determineClauseTokenSeparator(clauseChunk);
    StringTokenizer tokenizer = new StringTokenizer(clauseChunk, separator, true);
    while (tokenizer.hasMoreTokens()) {
      String token = tokenizer.nextToken();
      if (token.equals(separator)) {
        if (!wasClauseToken)
          tokens.add(""); 
        wasClauseToken = false;
        continue;
      } 
      tokens.add(token);
      wasClauseToken = true;
    } 
    if (!wasClauseToken)
      tokens.add(""); 
    return tokens.<String>toArray(new String[tokens.size()]);
  }
  
  protected String determineClauseTokenSeparator(String clauseChunk) {
    String allSeparators = getTokenSeparators();
    if (allSeparators == null || allSeparators.length() == 0)
      throw new JRRuntimeException("No token separators configured"); 
    int firstSepIdx = 0;
    int clauseLenght = clauseChunk.length();
    for (int idx = 0; idx < clauseLenght; idx++) {
      int sepIdx = allSeparators.indexOf(clauseChunk.charAt(idx));
      if (sepIdx >= 0) {
        firstSepIdx = sepIdx;
        break;
      } 
    } 
    return String.valueOf(allSeparators.charAt(firstSepIdx));
  }
  
  protected String getTokenSeparators() {
    return JRProperties.getProperty("net.sf.jasperreports.query.chunk.token.separators");
  }
  
  public String asText(JRQueryChunk[] chunks) {
    String text = "";
    if (chunks != null && chunks.length > 0) {
      StringBuffer sbuffer = new StringBuffer();
      for (int i = 0; i < chunks.length; i++) {
        JRQueryChunk queryChunk = chunks[i];
        switch (queryChunk.getType()) {
          case 2:
            sbuffer.append("$P{");
            sbuffer.append(queryChunk.getText());
            sbuffer.append("}");
            break;
          case 3:
            sbuffer.append("$P!{");
            sbuffer.append(queryChunk.getText());
            sbuffer.append("}");
            break;
          case 4:
            sbuffer.append("$X{");
            sbuffer.append(queryChunk.getText());
            sbuffer.append("}");
            break;
          default:
            sbuffer.append(queryChunk.getText());
            break;
        } 
      } 
      text = sbuffer.toString();
    } 
    return text;
  }
  
  public String asClauseText(String[] tokens) {
    StringBuffer sb = new StringBuffer();
    if (tokens != null && tokens.length > 0)
      for (int i = 0; i < tokens.length; i++) {
        if (i > 0)
          sb.append(','); 
        String token = tokens[i];
        if (token != null)
          sb.append(token); 
      }  
    return sb.toString();
  }
}
