package com.thaiopensource.validate.xerces;

import com.thaiopensource.util.PropertyId;
import com.thaiopensource.util.PropertyMap;
import com.thaiopensource.validate.AbstractSchemaReader;
import com.thaiopensource.validate.IncorrectSchemaException;
import com.thaiopensource.validate.Option;
import com.thaiopensource.validate.Schema;
import com.thaiopensource.validate.ValidateProperty;
import com.thaiopensource.validate.prop.wrap.WrapProperty;
import com.thaiopensource.xml.util.Name;
import org.apache.xerces.parsers.CachingParserPool;
import org.apache.xerces.parsers.XMLGrammarPreparser;
import org.apache.xerces.util.EntityResolverWrapper;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.SynchronizedSymbolTable;
import org.apache.xerces.util.XMLGrammarPoolImpl;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.grammars.XMLGrammarDescription;
import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.transform.sax.SAXSource;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

class SchemaReaderImpl extends AbstractSchemaReader {
  private static final PropertyId<?>[] supportedPropertyIds = {
    ValidateProperty.ERROR_HANDLER,
    ValidateProperty.ENTITY_RESOLVER,
  };
  public Schema createSchema(SAXSource source, PropertyMap properties)
          throws IOException, SAXException, IncorrectSchemaException {
    SymbolTable symbolTable = new SymbolTable();
    XMLGrammarPreparser preparser = new XMLGrammarPreparser(symbolTable);
    XMLGrammarPool grammarPool = new XMLGrammarPoolImpl();
    preparser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, null);
    preparser.setGrammarPool(grammarPool);
    ErrorHandler eh = properties.get(ValidateProperty.ERROR_HANDLER);
    SAXXMLErrorHandler xeh = new SAXXMLErrorHandler(eh);
    preparser.setErrorHandler(xeh);
    EntityResolver er = properties.get(ValidateProperty.ENTITY_RESOLVER);
    if (er != null)
      preparser.setEntityResolver(new EntityResolverWrapper(er));
    try {
      preparser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA, toXMLInputSource(source.getInputSource()));
      Name attributeOwner = properties.get(WrapProperty.ATTRIBUTE_OWNER);
      if (attributeOwner != null) {
        Reader r = new StringReader(createWrapper(attributeOwner));
   	preparser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA,
                                  new XMLInputSource(null, null, null, r, null));
      }
    }
    catch (XNIException e) {
      throw ValidatorImpl.toSAXException(e);
    }
    if (xeh.getHadError())
      throw new IncorrectSchemaException();
    return new SchemaImpl(new SynchronizedSymbolTable(symbolTable),
                          new CachingParserPool.SynchronizedGrammarPool(grammarPool),
                          properties,
                          supportedPropertyIds);
  }

  public Option getOption(String uri) {
    return null;
  }

  static private String createWrapper(Name attributeOwner) {
    return "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" +
           "    targetNamespace=\"" + attributeOwner.getNamespaceUri() + "\">" +
           "  <xs:element name=\"" + attributeOwner.getLocalName() + "\">" +
           "    <xs:complexType><xs:anyAttribute processContents=\"strict\"/></xs:complexType>" +
           "  </xs:element>" +
           "</xs:schema>";
  }
  
  private static XMLInputSource toXMLInputSource(InputSource in) {
    XMLInputSource xin = new XMLInputSource(in.getPublicId(), in.getSystemId(), null);
    xin.setByteStream(in.getByteStream());
    xin.setCharacterStream(in.getCharacterStream());
    xin.setEncoding(in.getEncoding());
    return xin;
  }
}
