package gnu.xml.validation.xmlschema;

import gnu.xml.validation.datatypes.Annotation;
import gnu.xml.validation.datatypes.AtomicSimpleType;
import gnu.xml.validation.datatypes.ListSimpleType;
import gnu.xml.validation.datatypes.Type;
import gnu.xml.validation.datatypes.UnionSimpleType;

import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.xml.XMLConstants;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * Parses an XML Schema DOM tree, constructing a compiled internal
 * representation.
 *
 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
 */
class XMLSchemaBuilder
{

  XMLSchema schema;

  void parseSchema(Node node)
  {
    String uri = node.getNamespaceURI();
    String name = node.getLocalName();
    if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
        node.getNodeType() == Node.ELEMENT_NODE)
      {
        if ("schema".equals(name))
          {
            NamedNodeMap attrs = node.getAttributes();
            String targetNamespace = getAttribute(attrs, "targetNamespace");
            String version = getAttribute(attrs, "version");
            String fd = getAttribute(attrs, "finalDefault");
            int finalDefault = parseFullDerivationSet(fd);
            String bd = getAttribute(attrs, "blockDefault");
            int blockDefault = parseBlockSet(bd);
            String afd = getAttribute(attrs, "attributeFormDefault");
            boolean attributeFormQualified = "qualified".equals(afd);
            String efd = getAttribute(attrs, "elementFormDefault");
            boolean elementFormQualified = "qualified".equals(efd);
            schema = new XMLSchema(targetNamespace, version,
                                   finalDefault, blockDefault,
                                   afd, efd);
            for (Node child = node.getFirstChild(); child != null;
                 child = child.getNextSibling())
              {
                parseTopLevelElement(child);
              }
            return;
          }
      }
    // TODO throw schema exception
  }

  void parseTopLevelElement(Node node)
  {
    String uri = node.getNamespaceURI();
    String name = node.getLocalName();
    if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
        node.getNodeType() == Node.ELEMENT_NODE)
      {
        if ("element".equals(name))
          {
            // TODO
          }
        else if ("attribute".equals(name))
          {
            AttributeDeclaration ad =
              (AttributeDeclaration) parseAttribute(node, true);
          }
        else if ("type".equals(name))
          {
            // TODO
          }
        else if ("group".equals(name))
          {
            // TODO
          }
        else if ("attributeGroup".equals(name))
          {
            // TODO
          }
        else if ("notation".equals(name))
          {
            // TODO
          }
        else if ("identityConstraint".equals(name))
          {
            // TODO
          }
      }
    // TODO throw schema exception
  }

  Object parseAttribute(Node node, boolean scope)
  {
    NamedNodeMap attrs = node.getAttributes();
    String def = getAttribute(attrs, "default");
    String fixed = getAttribute(attrs, "fixed");
    int constraintType = AttributeDeclaration.NONE;
    String constraintValue = null;
    if (def != null)
      {
        constraintType = AttributeDeclaration.DEFAULT;
        constraintValue = def;
      }
    else if (fixed != null)
      {
        constraintType = AttributeDeclaration.FIXED;
        constraintValue = fixed;
      }
    // TODO form = (qualified | unqualified)
    String attrName = getAttribute(attrs, "name");
    String ref = getAttribute(attrs, "ref");
    String use = getAttribute(attrs, "use");
    String type = getAttribute(attrs, "type");
    SimpleType datatype = (type == null) ? null :
      parseSimpleType(type);
    Annotation annotation = null;
    for (Node child = node.getFirstChild(); child != null;
         child = child.getNextSibling())
      {
        uri = child.getNamespaceURI();
        name = child.getLocalName();
        if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
            node.getNodeType() == Node.ELEMENT_NODE)
          {
            if ("annotation".equals(name))
              {
                annotation = parseAnnotation(child);
              }
            else if ("simpleType".equals(name))
              {
                datatype = parseSimpleType(child);
              }
            else
              {
                // TODO throw schema exception
              }
          }
      }
    if (scope)
      {
        return new AttributeDeclaration(scope,
                                        constraintType,
                                        constraintValue,
                                        name,
                                        datatype,
                                        annotation);
      }
    else 
      {
        boolean required = "required".equals(use);
        AttributeDeclaration decl = (ref == null) ?
              new AttributeDeclaration(scope,
                                       AttributeDeclaration.NONE,
                                       null,
                                       name,
                                       datatype,
                                       annotation) :
              schema.getAttributeDeclaration(ref);
        return new AttributeUse(required,
                                constraintType,
                                constraintValue,
                                decl);
      }
  }

  int parseFullDerivationSet(String value)
  {
    int finalDefault = XMLSchema.FINAL_NONE;
    if ("#all".equals(value))
      {
        finalDefault = XMLSchema.FINAL_ALL;
      }
    else
      {
        StringTokenizer st = new StringTokenizer(value, " ");
        while (st.hasMoreTokens())
          {
            String token = st.nextToken();
            if ("extension".equals(token))
              {
                finalDefault |= XMLSchema.FINAL_EXTENSION;
              }
            else if ("restriction".equals(token))
              {
                finalDefault |= XMLSchema.FINAL_RESTRICTION;
              }
            else if ("list".equals(token))
              {
                finalDefault |= XMLSchema.FINAL_LIST;
              }
            else if ("union".equals(token))
              {
                finalDefault |= XMLSchema.FINAL_UNION;
              }
          }
      }
    return finalDefault;
  }

  int parseBlockSet(String value)
  {
    int blockDefault = XMLSchema.BLOCK_NONE;
    if ("#all".equals(value))
      {
        blockDefault = XMLSchema.BLOCK_ALL;
      }
    else
      {
        StringTokenizer st = new StringTokenizer(value, " ");
        while (st.hasMoreTokens())
          {
            String token = st.nextToken();
            if ("extension".equals(token))
              {
                blockDefault |= XMLSchema.BLOCK_EXTENSION;
              }
            else if ("restriction".equals(token))
              {
                blockDefault |= XMLSchema.BLOCK_RESTRICTION;
              }
            else if ("substitution".equals(token))
              {
                blockDefault |= XMLSchema.BLOCK_SUBSTITUTION;
              }
          }
      }
    return blockDefault;
  }
  
}

