package de.pdark.decentxml.dtd;

import de.pdark.decentxml.EntityResolver;
import de.pdark.decentxml.XMLParseException;

public class DocTypeEntityResolver extends EntityResolver {
  private final DocType docType;

  public DocTypeEntityResolver(DocType docType) {
    super();
    this.docType = docType;
  }

  public DocTypeEntityResolver(DocType docType, EntityResolver parent) {
    super(parent);
    this.docType = docType;
  }

  public DocType getDocType() {
    return docType;
  }

  @Override
  public boolean isDefined(String name) {
    name = stripName(name);

    DocTypeEntity e = docType.getEntity(name);
    if (e == null) return super.isDefined(name);

    return true;
  }

  @Override
  public String resolve(String name) {
    name = stripName(name);

    DocTypeEntity e = docType.getEntity(name);
    if (e == null) return super.resolve(name);

    String s = e.getResolvedText();
    if (s == null) {
      s = resolveLiteralValueOfEntity(name, e.getText());
      e.setResolvedText(s);
    }

    return s;
  }

  /** Resolve character and parameter-entity references */
  public String resolveLiteralValueOfEntity(String name, String text) {
    int pos = 0;
    int N = text.length();

    StringBuilder buffer = new StringBuilder(1024);

    while (pos < N) {
      int parEntityPos = text.indexOf('%', pos);
      int charEntityPos = text.indexOf("&#", pos);

      if (parEntityPos == -1 && charEntityPos == -1) break;

      if (parEntityPos == -1) parEntityPos = N;
      if (charEntityPos == -1) charEntityPos = N;

      if (parEntityPos < charEntityPos) {
        if (pos < parEntityPos) buffer.append(text, pos, parEntityPos);

        int endPos = text.indexOf(';', parEntityPos);
        if (endPos == -1)
          throw new XMLParseException(
              "Missing ';' after '%' of parameter entity name: " + text.substring(parEntityPos));
        endPos++;

        String parEntityName = text.substring(parEntityPos + 1, endPos - 1);
        DocTypeEntity parEntity = docType.getParameterEntity(parEntityName);
        String s = parEntity.getResolvedText();
        if (s == null) {
          s = resolveLiteralValueOfEntity(parEntityName, s);
          parEntity.setResolvedText(s);
        }
        buffer.append(s);

        pos = endPos;
      } else {
        if (pos < charEntityPos) buffer.append(text, pos, charEntityPos);

        int endPos = text.indexOf(';', charEntityPos);
        if (endPos == -1)
          throw new XMLParseException(
              "Missing ';' after '&#' of character entity: " + text.substring(charEntityPos));
        endPos++;

        String s = text.substring(charEntityPos, endPos);
        buffer.append(expand(s));

        pos = endPos;
      }
    }

    if (pos < N) buffer.append(text, pos, N);

    return buffer.toString();
  }
}
