package com.thaiopensource.validate.picl;

import com.thaiopensource.util.PropertyMap;
import com.thaiopensource.util.SinglePropertyMap;
import com.thaiopensource.util.Localizer;
import com.thaiopensource.validate.IncorrectSchemaException;
import com.thaiopensource.validate.Schema;
import com.thaiopensource.validate.ValidateProperty;
import com.thaiopensource.validate.Validator;
import com.thaiopensource.validate.auto.SchemaFuture;
import com.thaiopensource.xml.sax.CountingErrorHandler;
import com.thaiopensource.xml.sax.DelegatingContentHandler;
import com.thaiopensource.xml.util.WellKnownNamespaces;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.Attributes;

import java.util.Vector;
import java.util.Stack;

class SchemaParser extends DelegatingContentHandler implements SchemaFuture, NamespaceContext {
  private final Vector constraints = new Vector();
  private final PropertyMap properties;
  private final CountingErrorHandler ceh;
  private Locator locator;
  private final Stack prefixes = new Stack();
  private final Localizer localizer = new Localizer(SchemaParser.class);
  private final PatternParser patternParser;

  SchemaParser(PropertyMap properties, Schema piclSchema) {
    this.properties = properties;
    ceh = new CountingErrorHandler(properties.get(ValidateProperty.ERROR_HANDLER));
    Validator validator = piclSchema.createValidator(SinglePropertyMap.newInstance(ValidateProperty.ERROR_HANDLER, ceh));
    setDelegate(validator.getContentHandler());
    patternParser = new PatternParser(ceh, localizer);
  }

  public void setDocumentLocator(Locator locator) {
    super.setDocumentLocator(locator);
    this.locator = locator;
  }

  public void startDocument()
          throws SAXException {
    super.startDocument();
    prefixes.push("xml");
    prefixes.push(WellKnownNamespaces.XML);
  }

  public void startPrefixMapping(String prefix, String uri)
          throws SAXException {
    if (prefix == null)
      prefix = "";
    prefixes.push(prefix);
    if (uri != null && uri.length() == 0)
      uri = null;
    prefixes.push(uri);
    super.startPrefixMapping(prefix, uri);
  }

  public void endPrefixMapping(String prefix)
          throws SAXException {
    prefixes.pop();
    prefixes.pop();
    super.endPrefixMapping(prefix);
  }

  public void startElement(String namespaceURI, String localName,
                           String qName, Attributes atts)
          throws SAXException {
    super.startElement(namespaceURI, localName, qName, atts);
    if (ceh.getHadErrorOrFatalError())
      return;
    if (!localName.equals("constraint"))
      return;
    String key = atts.getValue("", "key");
    try {
      Pattern keyPattern = patternParser.parse(key, locator, this);
      String ref = atts.getValue("", "ref");
      if (ref != null) {
        Pattern refPattern = patternParser.parse(ref, locator, this);
        constraints.addElement(new KeyRefConstraint(keyPattern, refPattern));
      }
      else
        constraints.addElement(new KeyConstraint(keyPattern));
    }
    catch (InvalidPatternException e) {
    }
  }

  public Schema getSchema() throws IncorrectSchemaException {
    if (ceh.getHadErrorOrFatalError())
      throw new IncorrectSchemaException();
    Constraint constraint;
    if (constraints.size() == 1)
      constraint = (Constraint)constraints.elementAt(0);
    else {
      Constraint[] v = new Constraint[constraints.size()];
      for (int i = 0; i < v.length; i++)
        v[i] = (Constraint)constraints.elementAt(i);
      constraint = new MultiConstraint(v);
    }
    return new SchemaImpl(properties, constraint);
  }

  public RuntimeException unwrapException(RuntimeException e) {
    return e;
  }

  public String getNamespaceUri(String prefix) {
    for (int i = prefixes.size(); i > 0; i -= 2) {
      if (prefixes.elementAt(i - 2).equals(prefix))
        return (String)prefixes.elementAt(i - 1);
    }
    return null;
  }

  public String defaultPrefix() {
    return "";
  }
}
