/*
 * Copyright 2008 Werner Guttmann
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.exolab.castor.builder.printing;

import java.lang.reflect.Array;
import java.util.ArrayList;

import org.exolab.javasource.JAnnotation;
import org.exolab.javasource.JClass;
import org.exolab.javasource.JEnum;

/**
 * Helper class that provides convenience methods used by Velocity templates during {@link JClass}
 * printing.
 */
public class TemplateHelper {

  /**
   * Removes all line breaks from a given string.
   * 
   * @param string The string containing line breaks.
   * @return A string without line breaks.
   */
  public String removeLineBreaks(String string) {
    return string.replaceAll("\n", "");
  }

  /**
   * Takes a comment string and splits it into lines that have the maximum length of 70 chars,
   * 
   * @param comment The comment as string.
   * @return The comment string splitted into a list.
   */
  public ArrayList<String> getLines(String comment) {
    comment = removeLineBreaks(comment);
    ArrayList<String> list = new ArrayList<String>();
    do {
      comment = computeLine(comment, list);
    } while (comment.length() > 0);
    return list;
  }

  /**
   * Helper methods that computes the next line for {@link #getLines(String)}.
   * 
   * @param comment The current comment string.
   * @param list The current list.
   * @return The comment without the next line.
   */
  private String computeLine(String comment, ArrayList<String> list) {
    if (comment == null) {
      return "";
    }

    if (comment.length() <= 70) {
      list.add(comment);
      return "";
    }

    int pos = comment.indexOf(' ', 70);
    if (pos == -1) {
      list.add(comment);
      return "";
    }

    String line = comment.substring(0, pos);
    list.add(line);
    return comment.substring(pos + 1);

  }

  /**
   * Returns true if the {@link JClass} instance is instance of JEnum.
   * 
   * @param jClass The {@link JClass} instance to check.
   * @return true if instance of JEnum.
   */
  public boolean isEnum(final JClass jClass) {
    return (jClass instanceof JEnum);
  }

  /**
   * Converts the given {@link JAnnotation} to a string representation.
   * 
   * @param annotation The annotation to translate.
   * @param shift The intent.
   * @return A string representation of the annotation.
   */
  public String printAnnotation(final JAnnotation annotation, String shift) {
    StringBuilder stringBuffer = new StringBuilder(32).append(shift).append("@")
        .append(annotation.getAnnotationType().getLocalName()).append("(");
    // Single element annotation?
    String[] elementNames = annotation.getElementNames();
    if (elementNames.length == 1 && elementNames[0].equals(JAnnotation.VALUE)) {
      // Just output value
      stringBuffer
          .append(printAnnotationValue(annotation.getElementValueObject(JAnnotation.VALUE), shift));
    } else if (elementNames.length > 0) {
      // Max element name length?
      int maxLength = 0;
      for (String elementName : elementNames) {
        int elementNameLength = elementName.length();
        if (elementNameLength > maxLength) {
          maxLength = elementNameLength;
        }
      }
      // Output element name and values
      stringBuffer.append('\n').append(shift + "    ");
      for (int i = 0; i < elementNames.length; i++) {
        int elementNameLength = elementNames[i].length();
        // Output element name with padding
        stringBuffer.append(elementNames[i]);
        for (int p = 0; p < maxLength - elementNameLength; p++) {
          stringBuffer.append(' ');
        }
        // Assignment operator
        stringBuffer.append(" = ")
            // Value
            .append(printAnnotationValue(annotation.getElementValueObject(elementNames[i]), shift));
        if (i < elementNames.length - 1) {
          stringBuffer.append(",\n").append(shift + "    ");
        }
      }
    }
    stringBuffer.append(')');
    return stringBuffer.toString();
  }

  /**
   * Helper method that translates an annotation value.
   * 
   * @param elementValue The value to translate.
   * @param shift The intent,
   * @return The string representation of the value.
   */
  private String printAnnotationValue(final Object elementValue, final String shift) {
    // String?
    if (elementValue instanceof String) {
      return (String) elementValue;
    } else if (elementValue instanceof JAnnotation) {
      JAnnotation annotation = (JAnnotation) elementValue;
      return printAnnotation(annotation, shift);
    } else if (elementValue.getClass().isArray()) {
      // Short hand for single item list
      int listLength = Array.getLength(elementValue);
      if (listLength == 1) {
        return printAnnotationValue(Array.get(elementValue, 0), shift);
      }
      // Output list items
      StringBuilder stringBuffer = new StringBuilder().append("\n{\n").append(shift);
      for (int i = 0; i < listLength; i++) {
        stringBuffer.append(shift).append(printAnnotationValue(Array.get(elementValue, i), shift));
        if (i < listLength - 1) {
          stringBuffer.append(',');
        }
        stringBuffer.append('\n');
      }
      stringBuffer.append('}');
      return stringBuffer.toString();
    }
    throw new IllegalArgumentException("'" + elementValue + "' was not expected.");
  }

}
